[prev in list] [next in list] [prev in thread] [next in thread]
List: log4j-dev
Subject: DatedFileAppender, failure on java.io.File.renameTo()
From: Geir W Rogde <geirwr () gmail ! com>
Date: 2008-11-28 11:53:31
Message-ID: 492FDBBB.5020509 () gmail ! com
[Download RAW message or body]
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
log4j-libraries.
Or really the java.io.File.renameTo() which returns a boolean success or
failure.
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.
see
https://issues.apache.org/bugzilla/show_bug.cgi?id=29726
https://issues.apache.org/bugzilla/show_bug.cgi?id=39023
https://issues.apache.org/bugzilla/show_bug.cgi?id=41735
https://issues.apache.org/bugzilla/show_bug.cgi?id=43374
and
http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4167147
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/"
debug="false">
<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" />
</layout>
</appender>
<root>
<appender-ref ref="file" />
</root>
</log4j:configuration>
/*
*/
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
failure.<br>
* 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
file.<br>
* 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>
* <appender name="file"
class="no.gwr.log4j.DatedFileAppender">
* <param name="File"
value="application-log.log" />
* <layout class="org.apache.log4j.PatternLayout">
* <param name="ConversionPattern"
value="[%d{dd.MM.yyyy HH:mm:ss}, %-5p, %c{1}] - %m%n"/>
* </layout>
* </appender>
* </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
formatted
* 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+ "].");
return;
}
long n = System.currentTimeMillis();
if (n >= rotationTime) {
rollOver(n);
}
if (this.qw == null) { // should never happen
errorHandler.error("No output stream or file set for the
appender named ["+ name + "].");
return;
}
subAppend(event);
}
/**
* 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();
rotationTime=resolveNextRollOverTime(calendar);
super.activateOptions(); // close current file and open new file
}
/**
* @return a filename containing the timestamp part
*/
private String getFileName() {
StringBuffer sb = new StringBuffer();
sb.append(file);
SimpleDateFormat fmt = new SimpleDateFormat(datePattern);
sb.append(fmt.format(calendar.getTime()));
return sb.toString();
}
@Override
public void setFile(String f) {
this.file = f;
super.setFile(getFileName());
}
/**
* 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
DailyRollingFileAppender?
* 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
rolling
//int hour = calendar.get(Calendar.HOUR_OF_DAY)+1;gives hourly
rolling
//int minute = calendar.get(Calendar.MINUTE)+1;gives minutely
rolling
calendar.clear();
calendar.set(year,month,day);
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
[prev in list] [next in list] [prev in thread] [next in thread]
Configure |
About |
News |
Add a list |
Sponsored by KoreLogic