Log4j初始化详解: http://donald-draper.iteye.com/blog/2332385
上一篇我们说了log4j的初始化,Logger的实例为NOPLogger,所有Appender,委托给
rootLogger管理,今天我们来看一下,日志的打印输出。
日志输出源头为下一句
log.info("========test daily level info=========");
我们来看一下,这一句都做了些什么?
public final class NOPLogger extends Logger
public class Logger extends Category
而NOPLogger,Logger,没有info方法,来看Category
//Category
public class Category
implements AppenderAttachable
{
AppenderAttachableImpl aai;
static
{
FQCN = (org.apache.log4j.Category.class).getName();
}
//输出日志
public void info(Object message)
{
//查看info日志,是否开启
if(repository.isDisabled(20000))
return;
//如果INFO,大于等于有效的日志级别,则输出日志
if(Level.INFO.isGreaterOrEqual(getEffectiveLevel()))
forcedLog(FQCN, Level.INFO, message, null);
}
//根据日志级别,输出日志
protected void forcedLog(String fqcn, Priority level, Object message, Throwable t)
{
//将LoggingEvent,委托给AppenderS处理,
callAppenders(new LoggingEvent(fqcn, this, level, message, t));
}
public void callAppenders(LoggingEvent event)
{
int writes;
Category c;
writes = 0;
c = this;
_L3:
label0:
{
if(c == null)
break; /* Loop/switch isn't completed */
synchronized(c)
{
if(c.aai != null)
//Appenders处理日志输出事件
writes += c.aai.appendLoopOnAppenders(event);
if(c.additive)
break label0;
}
break; /* Loop/switch isn't completed */
}
category;
JVM INSTR monitorexit ;
goto _L1
exception;
throw exception;
_L1:
c = c.parent;
if(true) goto _L3; else goto _L2
_L2:
if(writes == 0)
repository.emitNoAppenderWarning(this);
return;
}
}
//AppenderAttachableImpl
public class AppenderAttachableImpl
implements AppenderAttachable
{
protected Vector appenderList;
//遍历rootLooger的Appenders,每一个Appenders分别处理log输出事件
public int appendLoopOnAppenders(LoggingEvent event)
{
int size = 0;
if(appenderList != null)
{
size = appenderList.size();
for(int i = 0; i < size; i++)
{
Appender appender = (Appender)appenderList.elementAt(i);
appender.doAppend(event);
}
}
return size;
}
}
这里我们来看一下DailyRollingFileAppender
//DailyRollingFileAppender
public class DailyRollingFileAppender extends FileAppender
{
static final int TOP_OF_TROUBLE = -1;
static final int TOP_OF_MINUTE = 0;
static final int TOP_OF_HOUR = 1;
static final int HALF_DAY = 2;
static final int TOP_OF_DAY = 3;
static final int TOP_OF_WEEK = 4;
static final int TOP_OF_MONTH = 5;
private String datePattern;
private String scheduledFilename;
private long nextCheck;
Date now;
SimpleDateFormat sdf;
RollingCalendar rc;
int checkPeriod;
static final TimeZone gmtTimeZone = TimeZone.getTimeZone("GMT");
public DailyRollingFileAppender(Layout layout, String filename, String datePattern)
throws IOException
{
super(layout, filename, true);
this.datePattern = "'.'yyyy-MM-dd";
nextCheck = System.currentTimeMillis() - 1L;
now = new Date();
rc = new RollingCalendar();
checkPeriod = -1;
this.datePattern = datePattern;
//构造是调用激活配置方法
activateOptions();
}
}
//AppenderSkeleton
public abstract class AppenderSkeleton
implements Appender, OptionHandler
{
//处理日志事件
public synchronized void doAppend(LoggingEvent event)
{
append(event);
}
//待子类拓展
protected abstract void append(LoggingEvent loggingevent);
}
//AppenderSkeleton
public class WriterAppender extends AppenderSkeleton
{
//处理日志事件
public void append(LoggingEvent event)
{
if(!checkEntryConditions())
{
return;
} else
{
subAppend(event);
return;
}
}
protected void subAppend(LoggingEvent event)
{
qw.write(layout.format(event));
if(layout.ignoresThrowable())
{
String s[] = event.getThrowableStrRep();
if(s != null)
{
int len = s.length;
for(int i = 0; i < len; i++)
{
//输出日志,关键是QuietWriter
qw.write(s[i]);
qw.write(Layout.LINE_SEP);
}
}
}
if(shouldFlush(event))
qw.flush();
}
protected boolean immediateFlush;
protected String encoding;
protected QuietWriter qw;
}
下面看一下QuietWriter是什么?如何来的?
看DailyRollingFileAppender的构造方法中,调用了一个方法激活配置activateOptions
public DailyRollingFileAppender(Layout layout, String filename, String datePattern)
throws IOException
{
super(layout, filename, true);
this.datePattern = "'.'yyyy-MM-dd";
nextCheck = System.currentTimeMillis() - 1L;
now = new Date();
rc = new RollingCalendar();
checkPeriod = -1;
this.datePattern = datePattern;
//激活配置
activateOptions();
}
public void activateOptions()
{
super.activateOptions();
if(datePattern != null && fileName != null)
{
now.setTime(System.currentTimeMillis());
sdf = new SimpleDateFormat(datePattern);
int type = computeCheckPeriod();
printPeriodicity(type);
rc.setType(type);
File file = new File(fileName);
scheduledFilename = fileName + sdf.format(new Date(file.lastModified()));
} else
{
LogLog.error("Either File or DatePattern options are not set for appender [" + name + "].");
}
}
查看FileAppender
public class FileAppender extends WriterAppender
{
public void activateOptions()
{
if(fileName != null)
{
try
{
setFile(fileName, fileAppend, bufferedIO, bufferSize);
}
}
public synchronized void setFile(String fileName, boolean append, boolean bufferedIO, int bufferSize)
throws IOException
{
LogLog.debug("setFile called: " + fileName + ", " + append);
if(bufferedIO)
setImmediateFlush(false);
reset();
FileOutputStream ostream = null;
try
{
ostream = new FileOutputStream(fileName, append);
}
//根据文件流,创建Writer
Writer fw = createWriter(ostream);
if(bufferedIO)
fw = new BufferedWriter(fw, bufferSize);
//设置QuietWriter的输出流
setQWForFiles(fw);
this.fileName = fileName;
fileAppend = append;
this.bufferedIO = bufferedIO;
this.bufferSize = bufferSize;
writeHeader();
LogLog.debug("setFile ended");
}
//设置QuietWriter的输出流
protected OutputStreamWriter createWriter(OutputStream os)
{
OutputStreamWriter retval = null;
String enc = getEncoding();
if(enc != null)
try
{
retval = new OutputStreamWriter(os, enc);
}
if(retval == null)
retval = new OutputStreamWriter(os);
return retval;
}
//设置QuietWriter的输出流
protected void setQWForFiles(Writer writer)
{
qw = new QuietWriter(writer, errorHandler);
}
}
再看一下ConsoleAppender
//ConsoleAppender。
ublic class ConsoleAppender extends WriterAppender
{
private static class SystemOutStream extends OutputStream
{
public void close()
{
}
public void flush()
{
System.out.flush();
}
public void write(byte b[])
throws IOException
{
System.out.write(b);
}
public void write(byte b[], int off, int len)
throws IOException
{
System.out.write(b, off, len);
}
public void write(int b)
throws IOException
{
System.out.write(b);
}
public SystemOutStream()
{
}
}
public ConsoleAppender(Layout layout, String target)
{
this.target = "System.out";
follow = false;
setLayout(layout);
setTarget(target);
activateOptions();
}
public void activateOptions()
{
if(follow)
{
if(target.equals("System.err"))
setWriter(createWriter(new SystemErrStream()));
else
//设置输出流
setWriter(createWriter(new SystemOutStream()));
} else
if(target.equals("System.err"))
setWriter(createWriter(System.err));
else
setWriter(createWriter(System.out));
super.activateOptions();
}
}
总结:
从上面的分析我们可以看出,log日志的输出,是遍历rootLogger的Appender来处理日志输出事件,Appender,首先确定日志级别是否大于RootLogger的日志级别,大于,则处理日志,而日志的输出委托给QuietWriter,而QuietWriter来源于DailyRollingFileAppender,
ConsoleAppender。