現在工作中遇到高頻寫日誌,如果寫一條日誌,log4j flush一次,那麼勢必對服務器的性能會產生一定影響,那麼現在想要實現1秒鐘 1分鐘flush 一次該怎麼辦?
log4j 只要繼承至 fileappender 則都可以immediateflush 屬性。
經實測當沒有指定該屬性時 默認爲true
那麼我們怎麼做到呢?
web項目繼承servlet設置一個定時任務來手動flush 具體參考如下代碼
Set<FileAppender> flushedFileAppenders = new HashSet<FileAppender>();
Enumeration currentLoggers = LogManager.getLoggerRepository().getCurrentLoggers();
while(currentLoggers.hasMoreElements())
{
Object nextLogger = currentLoggers.nextElement();
if(nextLogger instanceof Logger)
{
Logger currentLogger = (Logger) nextLogger;
Enumeration allAppenders = currentLogger.getAllAppenders();
while(allAppenders.hasMoreElements())
{
Object nextElement = allAppenders.nextElement();
if(nextElement instanceof DailyRollingFileAppender)
{
DailyRollingFileAppender fileAppender = (DailyRollingFileAppender) nextElement;
System.out.println(fileAppender.getName());
if(!flushedFileAppenders.contains(fileAppender) && !fileAppender.getImmediateFlush())
{
flushedFileAppenders.add(fileAppender);
fileAppender.setImmediateFlush(true);
currentLogger.info("FLUSH");
fileAppender.setImmediateFlush(false);
}
else{
}
}
}
}
}
這裏源碼很好讀懂 就是從所有的容器中讀取出所有的logger 設置成true 之後再設置成flase。
但是這裏有一個關鍵點。
再設置爲true 需要再接着info 一下,只設置爲true 是不能觸發flush.只有再接着打條日誌。觸發下flush
這就是Log4j1 處理定時flush的一種方式了。
現又發現另外一種方式 那就是自己寫個appender 如下
package test;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import org.apache.log4j.DailyRollingFileAppender;
import org.apache.log4j.Layout;
/**
* 既能設置buffer大小,也能定時刷新(無論是否達到設定的buffer大小)的appender;適用於既想使用buffer的IO提高性能,又想定時強制輸出以不影響某些依賴日誌輸出的後續流程的場景
*
*/
public class TimedBufferedDailyRollingFileAppender extends DailyRollingFileAppender {
private static final int CHECK_INTERVAL = 5;
private static final Object appendersLock = new Object();
private static final List<TimedBufferedDailyRollingFileAppender> appenders = new ArrayList<TimedBufferedDailyRollingFileAppender>();
static {
new Thread(new Runnable() {
public void run() {
while (true) {
try {
synchronized (appendersLock) {
for (TimedBufferedDailyRollingFileAppender appender : appenders)
appender.flush();
}
Thread.sleep(CHECK_INTERVAL * 1000);
} catch (Throwable t) {
// ignore...
}
}
}
}, "TimedBufferedDailyRollingFileAppender-timed-flush").start();
}
private static final int DEFAULT_BUFFER_SIZE = 1024 * 1024; // 默認1MB的buffer
protected int flushInterval = 60; // 默認的定時刷新間隔(秒)
private Date flushTime = new Date(); // 下一次刷新的時間點
public TimedBufferedDailyRollingFileAppender(){
super();
this.setBufferedIO(true);
this.setBufferSize(DEFAULT_BUFFER_SIZE);// 默認1MB的buffer
this.setImmediateFlush(false);
synchronized (appendersLock) {
appenders.add(this);
}
}
public TimedBufferedDailyRollingFileAppender(Layout layout, String filename, String datePattern) throws IOException{
super(layout, filename, datePattern);
this.setBufferedIO(true);
this.setBufferSize(DEFAULT_BUFFER_SIZE);// 默認1MB的buffer
this.setImmediateFlush(false);
synchronized (appendersLock) {
appenders.add(this);
}
}
private void flush() {
if (!(new Date()).after(flushTime)) return;
if (!checkEntryConditions()) return;
qw.flush();
this.flushTime = new Date(System.currentTimeMillis() + this.flushInterval * 1000);
}
public void setFlushInterval(int flushInterval) {
if (flushInterval < CHECK_INTERVAL) flushInterval = CHECK_INTERVAL;// 至少CHECK_INTERVAL秒
this.flushInterval = flushInterval;
}
// 本appender必須是bufferedIO, 否則沒意義
@Override
public boolean getBufferedIO() {
return true;
}
@Override
public void setBufferedIO(boolean bufferedIO) {
super.setBufferedIO(true);
}
@Override
public void setImmediateFlush(boolean value) {
super.setImmediateFlush(false);
}
@Override
public boolean getImmediateFlush() {
return false;
}
}
配置文件中使用如下
<appender name="xxx" class="test.TimedBufferedDailyRollingFileAppender">
<param name="file" value="/tmp/xxx.log"/>
<param name="datePattern" value="'.'yyyy-MM-dd-HH"/>
<param name="append" value="true"/>
<param name="encoding" value="UTF-8"/>
<param name="flushInterval" value="10"/>
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern" value="%d{yyyy-MM-dd HH:mm:ss}|%p|%X{hostIp}||%m%n"/>
</layout>
</appender>
這樣log4j1 的flush就完美解決了。