log4j定时轮换文件
// <logger name="ASYNC_LOGGER">Logger logger = Logger.getLogger("ASYNC_LOGGER");
?
2、使用仓库配置多个不同的log4j配置文件
?
LoggerRepository loggerRepository1 = new Hierarchy(new RootCategory(Level.DEBUG));String path = new File("log4j_1.xml").getAbsolutePath();new DOMConfigurator().doConfigure(path,loggerRepository1);Logger log_1 = loggerRepository1.getLogger("TEST_LOGGER_1");LoggerRepository loggerRepository2 = new Hierarchy(new RootCategory(Level.DEBUG));path = new File("log4j_2.xml").getAbsolutePath();new DOMConfigurator().doConfigure(path,loggerRepository2);Logger log_2 = loggerRepository2.getLogger("TEST_LOGGER_2");?
?
?-------------------------------------------
轮换文件
?
? log4j可以按大小轮换文件(FileAppender, fileSize), 按天轮换文件 (DailyRollingFileAppender )
在我的某个应用场景中,使用log4j做数据落地,就是把每条日志数据格式化后,写入本地日志文件。
然后通过一个进程来读取所有轮换后的文件,分析每一条数据,入库(mongodb)。
做了如下的修改
1、除了使用文件大小来做轮换规则外,添加一个rollTime属性,表示需要轮换的时间s。
如果文件一直为0,不轮换,创建文件时记录下时间戳,定时与当前时间比较,超过rollTime,强制轮换。
2、同时修改轮换文件名称规则,默认的是.1,.2...这种形式。修改为.时间戳的形式。
其实还自定义了一个简单的Layout序列化数据。这个不是必须的。
?
使用方法,在log4j.xml中,
?
<appender
??value="1024" />
??<param name="Encoding" value="UTF-8" />
??<param name="File" value="esb.log" />
??<!--
???<param name="BufferSize" value="8192" />
???<param name="BufferedIO" value="true" />
??-->
??<param name="ImmediateFlush" value="true" />
??<param name="RollTime" value="60" />
??<param name="MaxFileSize" value="100MB" />
?? ......
?
import java.io.File;import java.io.IOException;import java.io.InterruptedIOException;import java.io.Writer;import java.util.Timer;import java.util.TimerTask;import org.apache.log4j.FileAppender;import org.apache.log4j.helpers.CountingQuietWriter;import org.apache.log4j.helpers.LogLog;import org.apache.log4j.helpers.OptionConverter;import org.apache.log4j.spi.LoggingEvent;/** * TODO 根据时间和大小轮换文件 * * @author kimmking (mailto:qinjw@primeton.com) */public class TimeRollingFileAppender extends FileAppender {/** * The default maximum file size is 10MB. */protected long maxFileSize = 10 * 1024 * 1024;/** * There is one backup file by default. */protected int maxBackupIndex = 1;private long nextRollover = 0;protected int rollTime = 60; // secondslong fileTimeStamp = 0;Timer timer = null;private TimerTask getTask() {return new TimerTask() {@Overridepublic void run() {// 时间戳long result = System.currentTimeMillis() - fileTimeStamp - getRollTime() * 1000;if (result > 0)if (new File(fileName).length() > 0)rollOver();}};}/** * The default constructor simply calls its * {@link FileAppender#FileAppender parents constructor}. */public TimeRollingFileAppender() {super();timer = new Timer("TimeRollingFileAppender", true);long period = rollTime * 1000;TimerTask task = getTask();timer.schedule(task, period, 1l); // 从一个轮换时间间隔以后,每秒检查一次}/** * Returns the value of the <b>MaxBackupIndex</b> option. */public int getMaxBackupIndex() {return maxBackupIndex;}/** * Get the maximum size that the output file is allowed to reach before * being rolled over to backup files. * * @since 1.1 */public long getMaximumFileSize() {return maxFileSize;}/** * Implements the usual roll over behaviour. * * <p> * If <code>MaxBackupIndex</code> is positive, then files { * <code>File.1</code>, ..., <code>File.MaxBackupIndex -1</code> are * renamed to {<code>File.2</code>, ..., * <code>File.MaxBackupIndex</code> . Moreover, <code>File</code> is * renamed <code>File.1</code> and closed. A new <code>File</code> is * created to receive further log output. * * <p> * If <code>MaxBackupIndex</code> is equal to zero, then the * <code>File</code> is truncated with no backup files created. */public// synchronization not necessary since doAppend is alreasy synchedsynchronized void rollOver() {File target;File file;if (qw != null) {long size = ((CountingQuietWriter) qw).getCount();LogLog.debug("rolling over count=" + size);// if operation fails, do not roll again until// maxFileSize more bytes are writtennextRollover = size + maxFileSize;}// LogLog.debug("maxBackupIndex=" + maxBackupIndex);long timestamp = System.currentTimeMillis();boolean renameSucceeded = true;// If maxBackups <= 0, then there is no file renaming to be done.// if (maxBackupIndex > 0) {// // Delete the oldest file, to keep Windows happy.// file = new File(fileName + '.' + maxBackupIndex);// if (file.exists())// renameSucceeded = file.delete();// // Map {(maxBackupIndex - 1), ..., 2, 1} to {maxBackupIndex, ..., 3,// // 2}// for (int i = maxBackupIndex - 1; i >= 1 && renameSucceeded; i--) {// file = new File(fileName + "." + i);// if (file.exists()) {// target = new File(fileName + '.' + (i + 1));// LogLog.debug("Renaming file " + file + " to " + target);// renameSucceeded = file.renameTo(target);// }// }//// if (renameSucceeded) {// Rename fileName to fileName.1target = new File(fileName + "." + timestamp);this.closeFile(); // keep windows happy.file = new File(fileName);LogLog.debug("Renaming file " + file + " to " + target);renameSucceeded = file.renameTo(target);//// if file rename failed, reopen file with append = true//if (!renameSucceeded) {try {this.setFile(fileName, true, bufferedIO, bufferSize);} catch (IOException e) {if (e instanceof InterruptedIOException) {Thread.currentThread().interrupt();}LogLog.error("setFile(" + fileName + ", true) call failed.", e);}}// }// }//// if all renames were successful, then//if (renameSucceeded) {try {// This will also close the file. This is OK since multiple// close operations are safe.this.setFile(fileName, false, bufferedIO, bufferSize);nextRollover = 0;} catch (IOException e) {if (e instanceof InterruptedIOException) {Thread.currentThread().interrupt();}LogLog.error("setFile(" + fileName + ", false) call failed.", e);}}}public synchronized void setFile(String fileName, boolean append, boolean bufferedIO, int bufferSize) throws IOException {File file = new File(fileName).getParentFile();if (!file.exists())file.mkdirs();super.setFile(fileName, append, this.bufferedIO, this.bufferSize);if (append) {File f = new File(fileName);((CountingQuietWriter) qw).setCount(f.length());}fileTimeStamp = System.currentTimeMillis();}public void setRollTime(int rollTime) {if (this.rollTime == rollTime)return;this.rollTime = rollTime;// if (this.timer != null) {// this.timer.cancel();// }// this.timer = new Timer("TimeRollingFileAppender", true);//// if (rollTime > 0) {// long period = rollTime * 1000;// this.timer.schedule(getTask(), period, period);// }}public final int getRollTime() {return this.rollTime;}/** * Set the maximum number of backup files to keep around. * * <p> * The <b>MaxBackupIndex</b> option determines how many backup files are * kept before the oldest is erased. This option takes a positive integer * value. If set to zero, then there will be no backup files and the log * file will be truncated when it reaches <code>MaxFileSize</code>. */public void setMaxBackupIndex(int maxBackups) {this.maxBackupIndex = maxBackups;}/** * Set the maximum size that the output file is allowed to reach before * being rolled over to backup files. * * <p> * This method is equivalent to {@link #setMaxFileSize} except that it is * required for differentiating the setter taking a <code>long</code> * argument from the setter taking a <code>String</code> argument by the * JavaBeans {@link java.beans.Introspector Introspector}. * * @see #setMaxFileSize(String) */public void setMaximumFileSize(long maxFileSize) {this.maxFileSize = maxFileSize;}/** * Set the maximum size that the output file is allowed to reach before * being rolled over to backup files. * * <p> * In configuration files, the <b>MaxFileSize</b> option takes an long * integer in the range 0 - 2^63. You can specify the value with the * suffixes "KB", "MB" or "GB" so that the integer is interpreted being * expressed respectively in kilobytes, megabytes or gigabytes. For example, * the value "10KB" will be interpreted as 10240. */public void setMaxFileSize(String value) {maxFileSize = OptionConverter.toFileSize(value, maxFileSize + 1);}protected void setQWForFiles(Writer writer) {this.qw = new CountingQuietWriter(writer, errorHandler);}/** * This method differentiates RollingFileAppender from its super class. * * @since 0.9.0 */protected void subAppend(LoggingEvent event) {super.subAppend(event);if (fileName != null && qw != null) {long size = ((CountingQuietWriter) qw).getCount();if (size >= maxFileSize && size >= nextRollover) {rollOver();}}}}?
?
?
<p>?? ......</p><p>?</p>
<pre name="code" + size);
// if operation fails, do not roll again until
// maxFileSize more bytes are written
nextRollover = size + maxFileSize;
}
// LogLog.debug("maxBackupIndex=" + maxBackupIndex);
long timestamp = System.currentTimeMillis();
boolean renameSucceeded = true;
// If maxBackups <= 0, then there is no file renaming to be done.
// if (maxBackupIndex > 0) {
// // Delete the oldest file, to keep Windows happy.
// file = new File(fileName + '.' + maxBackupIndex);
// if (file.exists())
// renameSucceeded = file.delete();
// // Map {(maxBackupIndex - 1), ..., 2, 1} to {maxBackupIndex, ..., 3,
// // 2}
// for (int i = maxBackupIndex - 1; i >= 1 && renameSucceeded; i--) {
// file = new File(fileName + "." + i);
// if (file.exists()) {
// target = new File(fileName + '.' + (i + 1));
// LogLog.debug("Renaming file " + file + " to " + target);
// renameSucceeded = file.renameTo(target);
// }
// }
//
// if (renameSucceeded) {
// Rename fileName to fileName.1
target = new File(fileName + "." + timestamp);
this.closeFile(); // keep windows happy.
file = new File(fileName);
LogLog.debug("Renaming file " + file + " to " + target);
renameSucceeded = file.renameTo(target);
//
// if file rename failed, reopen file with append = true
//
if (!renameSucceeded) {
try {
this.setFile(fileName, true, bufferedIO, bufferSize);
} catch (IOException e) {
if (e instanceof InterruptedIOException) {
Thread.currentThread().interrupt();
}
LogLog.error("setFile(" + fileName + ", true) call failed.", e);
}
}
// }
// }
//
// if all renames were successful, then
//
if (renameSucceeded) {
try {
// This will also close the file. This is OK since multiple
// close operations are safe.
this.setFile(fileName, false, bufferedIO, bufferSize);
nextRollover = 0;
} catch (IOException e) {
if (e instanceof InterruptedIOException) {
Thread.currentThread().interrupt();
}
LogLog.error("setFile(" + fileName + ", false) call failed.", e);
}
}
}
public synchronized void setFile(String fileName, boolean append, boolean bufferedIO, int bufferSize) throws IOException {
File file = new File(fileName).getParentFile();
if (!file.exists())
file.mkdirs();
super.setFile(fileName, append, this.bufferedIO, this.bufferSize);
if (append) {
File f = new File(fileName);
((CountingQuietWriter) qw).setCount(f.length());
}
fileTimeStamp = System.currentTimeMillis();
}
public void setRollTime(int rollTime) {
if (this.rollTime == rollTime)
return;
this.rollTime = rollTime;
// if (this.timer != null) {
// this.timer.cancel();
// }
// this.timer = new Timer("TimeRollingFileAppender", true);
//
// if (rollTime > 0) {
// long period = rollTime * 1000;
// this.timer.schedule(getTask(), period, period);
// }
}
public final int getRollTime() {
return this.rollTime;
}
/**
* Set the maximum number of backup files to keep around.
*
* <p>
* The <b>MaxBackupIndex</b> option determines how many backup files are
* kept before the oldest is erased. This option takes a positive integer
* value. If set to zero, then there will be no backup files and the log
* file will be truncated when it reaches <code>MaxFileSize</code>.
*/
public void setMaxBackupIndex(int maxBackups) {
this.maxBackupIndex = maxBackups;
}
/**
* Set the maximum size that the output file is allowed to reach before
* being rolled over to backup files.
*
* <p>
* This method is equivalent to {@link #setMaxFileSize} except that it is
* required for differentiating the setter taking a <code>long</code>
* argument from the setter taking a <code>String</code> argument by the
* JavaBeans {@link java.beans.Introspector Introspector}.
*
* @see #setMaxFileSize(String)
*/
public void setMaximumFileSize(long maxFileSize) {
this.maxFileSize = maxFileSize;
}
/**
* Set the maximum size that the output file is allowed to reach before
* being rolled over to backup files.
*
* <p>
* In configuration files, the <b>MaxFileSize</b> option takes an long
* integer in the range 0 - 2^63. You can specify the value with the
* suffixes "KB", "MB" or "GB" so that the integer is interpreted being
* expressed respectively in kilobytes, megabytes or gigabytes. For example,
* the value "10KB" will be interpreted as 10240.
*/
public void setMaxFileSize(String value) {
maxFileSize = OptionConverter.toFileSize(value, maxFileSize + 1);
}
protected void setQWForFiles(Writer writer) {
this.qw = new CountingQuietWriter(writer, errorHandler);
}
/**
* This method differentiates RollingFileAppender from its super class.
*
* @since 0.9.0
*/
protected void subAppend(LoggingEvent event) {
super.subAppend(event);
if (fileName != null && qw != null) {
long size = ((CountingQuietWriter) qw).getCount();
if (size >= maxFileSize && size >= nextRollover) {
rollOver();
}
}
}
}</pre>
<p>?</p>
<p>?</p>
<p>?</p>
</div>
<p>?</p>
<p><span style="">kimmking?</span>哥你搞复杂了,呵呵!</p>
<p>?</p>
<p>扩张一下DailyRollingFileAppender的<span style="white-space: pre;">subAppend:</span></p>
<p>?</p>
<p>?</p>
<pre name="code" class="java">protected void subAppend(LoggingEvent event) {
long n = System.currentTimeMillis();
if (n >= nextCheck) {
now.setTime(n);
nextCheck = rc.getNextCheckMillis(now);
try {
rollOver();
}
catch(IOException ioe) {
LogLog.error("rollOver() failed.", ioe);
}
}
super.subAppend(event);
}</pre>
<p>?</p>
<p>每次调用log方法时做了定期检查,不过这个DailyRollingFileAppender只能定位到分钟,不能粒度更小(不过应该可以满足99%的需要)。</p>
<p>?</p>
<p>加一个限定文件大小的check就OK了。</p> 26 楼 kimmking 2011-04-26 你的方式考虑过,写了半分钟数据,然后没写,不会rollover,
但是我的需求是不行的。
rollover后的文件,我有一个进程处理入库。
这个进程是与生成log的esb进程独立的。
我的需求里,入库的数据要求延迟不能太大。
就是说:esb里来了一条数据,我必须让它在2分钟内在governor的控制台上可以看到。
ps:其实可以esb的log直接入库,但是考虑入库的效率和esb的稳定性,这两步分离了。
27 楼 mercyblitz 2011-04-26 kimmking 写道你的方式考虑过,写了半分钟数据,然后没写,不会rollover,
但是我的需求是不行的。
rollover后的文件,我有一个进程处理入库。
这个进程是与生成log的esb进程独立的。
我的需求里,入库的数据要求延迟不能太大。
就是说:esb里来了一条数据,我必须让它在2分钟内在governor的控制台上可以看到。
ps:其实可以esb的log直接入库,但是考虑入库的效率和esb的稳定性,这两步分离了。
是的,你的实现需要比较精确的写日志。我的方法是被动式,还是有问题的,呵呵。
JDK7正式发布就好了,文件大小限制可以通过File Modification Notification事件来回调,Timer只管理定时了,这样代码更加好维护。又做定时和大小还是比较麻烦的,尤其是多线程。 28 楼 kimmking 2011-04-26 mercyblitz 写道kimmking 写道你的方式考虑过,写了半分钟数据,然后没写,不会rollover,
但是我的需求是不行的。
rollover后的文件,我有一个进程处理入库。
这个进程是与生成log的esb进程独立的。
我的需求里,入库的数据要求延迟不能太大。
就是说:esb里来了一条数据,我必须让它在2分钟内在governor的控制台上可以看到。
ps:其实可以esb的log直接入库,但是考虑入库的效率和esb的稳定性,这两步分离了。
是的,你的实现需要比较精确的写日志。我的方法是被动式,还是有问题的,呵呵。
JDK7正式发布就好了,文件大小限制可以通过File Modification Notification事件来回调,Timer只管理定时了,这样代码更加好维护。又做定时和大小还是比较麻烦的,尤其是多线程。
File Modification Notification的话,
可以直接把这部分用 native的方式来做。
ps: dotnet一直都支持 filewatch。