Log4j日志输出详解

Log4j初始化详解: http://donald-draper.iteye.com/blog/2332385
Log4j日志输出详解 : http://donald-draper.iteye.com/blog/2332395
slf4j + Log4j的使用: http://donald-draper.iteye.com/blog/2332407
上一篇我们说了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。

猜你喜欢

转载自donald-draper.iteye.com/blog/2332395