logging-log4j-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From Geir W Rogde <gei...@gmail.com>
Subject DatedFileAppender, failure on java.io.File.renameTo()
Date Fri, 28 Nov 2008 11:53:31 GMT

After going through all test phases successfully we moved to production.

But just to often the log-files were missing.

After investigating this it appears that this is a well-known problem in 
Or really the java.io.File.renameTo() which returns a boolean success or 

Realizing that log-files are completely lost we had a hard time 
convincing management
that log4j was a proper choice for log-handling.
And we ended up writing our own appender, to avoid the whole rename-problem.
A bit disappointing that this is not fixed in stable branch yet. Only in 
the discontinued 1.3 or the experimental 2.0.

The DatedFileAppender will create files with datePattern-suffix. So 
there is no need for
rolling. Handling the failed renameTo and just continue appending was 
not an option as
one log file named application.log.2008-10-23 could contain also the log 
entries for both 2008-10-22, 2008-10-21 etc. Which would be confusing 
for the defect handlers.



If anybody else could make use of it, here it is.
First example log4j.xml, then the DatedFileAppender

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/"    

    <appender name="file" class="org.apache.log4j.DatedFileAppender">
        <param name="File" value="application.log" />
        <layout class="org.apache.log4j.PatternLayout">
            <param name="ConversionPattern" value="[%d{dd.MM.yyyy 
HH:mm:ss}, %-5p, %c{1}] - %m%n" />

        <appender-ref ref="file" />


package org.apache.log4j;
import java.io.File;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import org.apache.log4j.FileAppender;
import org.apache.log4j.spi.LoggingEvent;
 * Loosing logfiles?
 * There is a known issue with DailyRollingFileAppender and 1.2.x.<br>
 * It is fixed in the discontinued 1.3 and in the experimental 2.x, but 
in production system
 * you really don't want to loose your log-files, and you are most 
likely using the stable 1.2.x.
 * </p>
 * The defects causing the need for this additional DatedFileAppender:<br>
 * log4j defects<br>
 * <ul>
 * <li>https://issues.apache.org/bugzilla/show_bug.cgi?id=29726</li>
 * <li>https://issues.apache.org/bugzilla/show_bug.cgi?id=39023</li>
 * <li>https://issues.apache.org/bugzilla/show_bug.cgi?id=41735</li>
 * <li>https://issues.apache.org/bugzilla/show_bug.cgi?id=43374</li>
 * </ul>
 * caused by java sun defect<br>
 * <ul>
 * <li>http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4167147</li>
 * </ul>
 * <p>
 * The main reason for this is the defect in java.io.File.renameTo() 
causing log-files to be lost occasionally.<br>
 * The RollingFileAppender tries to rename the old files during roll over.
 * However, it does not check if the rename succeeded and as a result, 
it may end up deleting information.<br>
 * There is no exception thrown just a boolean return for success or 
 * In particular, on Windows, the file rename can fail if
 * another process has the file open.
 * To avoid the whole renaming-problem the log-file is created with date 
suffix, and not postponed until rolling occurs.
 * </p>
 * DatedFileAppender appends Log4j logging events to a different log 
file every
 * day. Today's date (as yyyy-MM-dd) is appended to the end of the 
 * DatedFileAppender creates a new log file with the first message it 
logs on a
 * given day and continues to use that same file throughout the day. 
<br>Thus avoiding the whole rename-problem.<br><br>
 * If you want hourly/minutely etc,modify the datepattern and 
resolveNextRollOverTime method accordingly.
 * The following sample log4j.xml appender block
 * <pre>
 *  &lt;appender name=&quot;file&quot; 
 *  &lt;param name=&quot;File&quot; 
value=&quot;application-log.log&quot; /&gt;  
 *  &lt;layout class=&quot;org.apache.log4j.PatternLayout&quot;&gt;
 *  &lt;param name=&quot;ConversionPattern&quot; 
value=&quot;[%d{dd.MM.yyyy HH:mm:ss}, %-5p, %c{1}] - %m%n&quot;/&gt;
 *  &lt;/layout&gt;
 *  &lt;/appender&gt;
 * </pre>
 * @author geirwr
public class DatedFileAppender extends FileAppender {
    private Calendar calendar = Calendar.getInstance();
    private String datePattern = "'.'yyyy-MM-dd";//change this if you 
want hourly/minutely, i.e. different rolling than daily
    private long rotationTime = 0L;
    private String file;
     * Called by AppenderSkeleton.doAppend() to write a log message 
     * according to the layout defined for this appender.
    public void append(LoggingEvent event) {
        if (this.layout == null) {
            errorHandler.error("No layout set for the appender named [" 
+ name+ "].");
        long n = System.currentTimeMillis();
        if (n >= rotationTime) {
        if (this.qw == null) { // should never happen
            errorHandler.error("No output stream or file set for the 
appender named ["+ name + "].");
     * rolling over to a new file. 
     * No File.renameTo or File.copy necessary, as the appropriate 
filename is logged to directly
     * not as in default DailyRollingFileAppender, which can cause 
problems in Windows-env
     * @param currentTimeInMillis
    private void rollOver(long currentTimeInMillis) {
        calendar.setTime(new Date(currentTimeInMillis));
        File newFile = new File(getFileName());
        this.fileName = newFile.getAbsolutePath();
        super.activateOptions(); // close current file and open new file
     * @return a filename containing the timestamp part
    private String getFileName() {
        StringBuffer sb = new StringBuffer();
        SimpleDateFormat fmt = new SimpleDateFormat(datePattern);
        return sb.toString();
    public void setFile(String f) {
        this.file = f;
     * resolving next rollover time-
     * improve with hourly, minutely,weekly,monthly if needed.
     * Must also have a datepattern to match it.
     * Could the rollover-frequency be resolved from datePattern, as in 
     * sample 
     * <pre>
        int hour = calendar.get(Calendar.HOUR_OF_DAY);
        int minute = calendar.get(Calendar.MINUTE);
        if (DAILY) {hour=minute=0;}//due to daily rollover
        calendar.clear(); // clear all fields
        calendar.set(year,month,day,hour,minute); // setting next rollover
     * </pre>
     * Sets a calendar to the start of tomorrow, with all time values 
reset to
     * zero.
     * This must be changed if different rolling wanted. 
    public static long resolveNextRollOverTime(Calendar calendar) {
        int year = calendar.get(Calendar.YEAR);
        int month = calendar.get(Calendar.MONTH);
        int day = calendar.get(Calendar.DAY_OF_MONTH)+1;//gives daily 
        //int hour = calendar.get(Calendar.HOUR_OF_DAY)+1;gives hourly 
        //int minute = calendar.get(Calendar.MINUTE)+1;gives minutely 
        return calendar.getTime().getTime();
    public void setDatePattern(String datePattern) {
        this.datePattern = datePattern;
    public String getDatePattern() {
        return datePattern;

To unsubscribe, e-mail: log4j-dev-unsubscribe@logging.apache.org
For additional commands, e-mail: log4j-dev-help@logging.apache.org

View raw message