log4j的初始化,Logger的实例为NOPLogger,所有Appender,委托给 
rootLogger管理,今天我们来看一下,日志的打印输出。 
日志输出源头为下一句 

Java代码  Log4j日志输出详解_mybatis

  1. log.info("========test daily level info=========");  


我们来看一下,这一句都做了些什么? 

Java代码  Log4j日志输出详解_mybatis

  1. public final class NOPLogger extends Logger  

  2. public class Logger extends Category  


而NOPLogger,Logger,没有info方法,来看Category 
//Category 下载

Java代码  Log4j日志输出详解_mybatis

  1. public class Category  

  2.     implements AppenderAttachable  

  3. {  

  4.   AppenderAttachableImpl aai;  

  5.   static   

  6.     {  

  7.         FQCN = (org.apache.log4j.Category.class).getName();  

  8.     }  

  9.     //输出日志  

  10.     public void info(Object message)  

  11.     {  

  12.         //查看info日志,是否开启  

  13.         if(repository.isDisabled(20000))  

  14.             return;  

  15.      //如果INFO,大于等于有效的日志级别,则输出日志  

  16.         if(Level.INFO.isGreaterOrEqual(getEffectiveLevel()))  

  17.             forcedLog(FQCN, Level.INFO, message, null);  

  18.     }  

  19.     //根据日志级别,输出日志  

  20.     protected void forcedLog(String fqcn, Priority level, Object message, Throwable t)  

  21.     {  

  22.         //将LoggingEvent,委托给AppenderS处理,  

  23.         callAppenders(new LoggingEvent(fqcn, this, level, message, t));  

  24.     }  

  25.     public void callAppenders(LoggingEvent event)  

  26.     {  

  27.         int writes;  

  28.         Category c;  

  29.         writes = 0;  

  30.         c = this;  

  31. _L3:  

  32. label0:  

  33.         {  

  34.             if(c == null)  

  35.                 break/* Loop/switch isn't completed */  

  36.             synchronized(c)  

  37.             {  

  38.                 if(c.aai != null)  

  39.             //Appenders处理日志输出事件  

  40.                     writes += c.aai.appendLoopOnAppenders(event);  

  41.                 if(c.additive)  

  42.                     break label0;  

  43.             }  

  44.             break/* Loop/switch isn't completed */  

  45.         }  

  46.         category;  

  47.         JVM INSTR monitorexit ;  

  48.           goto _L1  

  49.         exception;  

  50.         throw exception;  

  51. _L1:  

  52.         c = c.parent;  

  53.         if(truegoto _L3; else goto _L2  

  54. _L2:  

  55.         if(writes == 0)  

  56.             repository.emitNoAppenderWarning(this);  

  57.         return;  

  58.     }  

  59. }  


//AppenderAttachableImpl 

Java代码  下载

  1. public class AppenderAttachableImpl  

  2.     implements AppenderAttachable  

  3. {  

  4. protected Vector appenderList;  

  5.  //遍历rootLooger的Appenders,每一个Appenders分别处理log输出事件  

  6.  public int appendLoopOnAppenders(LoggingEvent event)  

  7.     {  

  8.         int size = 0;  

  9.         if(appenderList != null)  

  10.         {  

  11.             size = appenderList.size();  

  12.             for(int i = 0; i < size; i++)  

  13.             {  

  14.                 Appender appender = (Appender)appenderList.elementAt(i);  

  15.                 appender.doAppend(event);  

  16.             }  

  17.   

  18.         }  

  19.         return size;  

  20.     }  

  21. }  


这里我们来看一下DailyRollingFileAppender 
//DailyRollingFileAppender 

Java代码  下载

  1. public class DailyRollingFileAppender extends FileAppender  

  2. {  

  3.     static final int TOP_OF_TROUBLE = -1;  

  4.     static final int TOP_OF_MINUTE = 0;  

  5.     static final int TOP_OF_HOUR = 1;  

  6.     static final int HALF_DAY = 2;  

  7.     static final int TOP_OF_DAY = 3;  

  8.     static final int TOP_OF_WEEK = 4;  

  9.     static final int TOP_OF_MONTH = 5;  

  10.     private String datePattern;  

  11.     private String scheduledFilename;  

  12.     private long nextCheck;  

  13.     Date now;  

  14.     SimpleDateFormat sdf;  

  15.     RollingCalendar rc;  

  16.     int checkPeriod;  

  17.     static final TimeZone gmtTimeZone = TimeZone.getTimeZone("GMT");  

  18.     public DailyRollingFileAppender(Layout layout, String filename, String datePattern)  

  19.         throws IOException  

  20.     {  

  21.         super(layout, filename, true);  

  22.         this.datePattern = "'.'yyyy-MM-dd";  

  23.         nextCheck = System.currentTimeMillis() - 1L;  

  24.         now = new Date();  

  25.         rc = new RollingCalendar();  

  26.         checkPeriod = -1;  

  27.         this.datePattern = datePattern;  

  28.     //构造是调用激活配置方法  

  29.         activateOptions();  

  30.     }  

  31. }  


//AppenderSkeleton 

Java代码  

  1. public abstract class AppenderSkeleton  

  2.     implements Appender, OptionHandler  

  3. {  

  4.    //处理日志事件  

  5. public synchronized void doAppend(LoggingEvent event)  

  6.     {  

  7.         append(event);  

  8.     }  

  9.     //待子类拓展  

  10.    protected abstract void append(LoggingEvent loggingevent);  

  11. }  


//AppenderSkeleton 

Java代码  下载

  1. public class WriterAppender extends AppenderSkeleton  

  2. {  

  3.    //处理日志事件  

  4.    public void append(LoggingEvent event)  

  5.     {  

  6.         if(!checkEntryConditions())  

  7.         {  

  8.             return;  

  9.         } else  

  10.         {  

  11.             subAppend(event);  

  12.             return;  

  13.         }  

  14.     }  

  15.     protected void subAppend(LoggingEvent event)  

  16.     {  

  17.         qw.write(layout.format(event));  

  18.         if(layout.ignoresThrowable())  

  19.         {  

  20.             String s[] = event.getThrowableStrRep();  

  21.             if(s != null)  

  22.             {  

  23.                 int len = s.length;  

  24.                 for(int i = 0; i < len; i++)  

  25.                 {  

  26.             //输出日志,关键是QuietWriter  

  27.                     qw.write(s[i]);  

  28.                     qw.write(Layout.LINE_SEP);  

  29.                 }  

  30.   

  31.             }  

  32.         }  

  33.         if(shouldFlush(event))  

  34.             qw.flush();  

  35.     }  

  36.     protected boolean immediateFlush;  

  37.     protected String encoding;  

  38.     protected QuietWriter qw;  

  39. }  


下面看一下QuietWriter是什么?如何来的? 
看DailyRollingFileAppender的构造方法中,调用了一个方法激活配置activateOptions 下载

Java代码  Log4j日志输出详解_mybatis

  1. public DailyRollingFileAppender(Layout layout, String filename, String datePattern)  

  2.         throws IOException  

  3.     {  

  4.         super(layout, filename, true);  

  5.         this.datePattern = "'.'yyyy-MM-dd";  

  6.         nextCheck = System.currentTimeMillis() - 1L;  

  7.         now = new Date();  

  8.         rc = new RollingCalendar();  

  9.         checkPeriod = -1;  

  10.         this.datePattern = datePattern;  

  11.     //激活配置  

  12.         activateOptions();  

  13.     }  

  14.      public void activateOptions()  

  15.     {  

  16.         super.activateOptions();  

  17.         if(datePattern != null && fileName != null)  

  18.         {  

  19.             now.setTime(System.currentTimeMillis());  

  20.             sdf = new SimpleDateFormat(datePattern);  

  21.             int type = computeCheckPeriod();  

  22.             printPeriodicity(type);  

  23.             rc.setType(type);  

  24.             File file = new File(fileName);  

  25.             scheduledFilename = fileName + sdf.format(new Date(file.lastModified()));  

  26.         } else  

  27.         {  

  28.             LogLog.error("Either File or DatePattern options are not set for appender [" + name + "].");  

  29.         }  

  30.     }  


查看FileAppender 

Java代码  Log4j日志输出详解_mybatis

  1. public class FileAppender extends WriterAppender  

  2. {  

  3. public void activateOptions()  

  4.     {  

  5.         if(fileName != null)  

  6.         {  

  7.             try  

  8.             {  

  9.                 setFile(fileName, fileAppend, bufferedIO, bufferSize);  

  10.             }  

  11.     }  

  12.     public synchronized void setFile(String fileName, boolean append, boolean bufferedIO, int bufferSize)  

  13.         throws IOException  

  14.     {  

  15.         LogLog.debug("setFile called: " + fileName + ", " + append);  

  16.         if(bufferedIO)  

  17.             setImmediateFlush(false);  

  18.         reset();  

  19.         FileOutputStream ostream = null;  

  20.         try  

  21.         {  

  22.             ostream = new FileOutputStream(fileName, append);  

  23.         }  

  24.     //根据文件流,创建Writer  

  25.         Writer fw = createWriter(ostream);  

  26.         if(bufferedIO)  

  27.             fw = new BufferedWriter(fw, bufferSize);  

  28.     //设置QuietWriter的输出流  

  29.         setQWForFiles(fw);  

  30.         this.fileName = fileName;  

  31.         fileAppend = append;  

  32.         this.bufferedIO = bufferedIO;  

  33.         this.bufferSize = bufferSize;  

  34.         writeHeader();  

  35.         LogLog.debug("setFile ended");  

  36.     }  

  37.     //设置QuietWriter的输出流  

  38.     protected OutputStreamWriter createWriter(OutputStream os)  

  39.     {  

  40.         OutputStreamWriter retval = null;  

  41.         String enc = getEncoding();  

  42.         if(enc != null)  

  43.             try  

  44.             {  

  45.                 retval = new OutputStreamWriter(os, enc);  

  46.             }  

  47.         if(retval == null)  

  48.             retval = new OutputStreamWriter(os);  

  49.         return retval;  

  50.     }  

  51.     //设置QuietWriter的输出流  

  52.      protected void setQWForFiles(Writer writer)  

  53.     {  

  54.         qw = new QuietWriter(writer, errorHandler);  

  55.     }  

  56. }  


再看一下ConsoleAppender 

Java代码  下载

  1. //ConsoleAppender。  

  2. ublic class ConsoleAppender extends WriterAppender  

  3. {  

  4.     private static class SystemOutStream extends OutputStream  

  5.     {  

  6.   

  7.         public void close()  

  8.         {  

  9.         }  

  10.   

  11.         public void flush()  

  12.         {  

  13.             System.out.flush();  

  14.         }  

  15.   

  16.         public void write(byte b[])  

  17.             throws IOException  

  18.         {  

  19.             System.out.write(b);  

  20.         }  

  21.   

  22.         public void write(byte b[], int off, int len)  

  23.             throws IOException  

  24.         {  

  25.             System.out.write(b, off, len);  

  26.         }  

  27.   

  28.         public void write(int b)  

  29.             throws IOException  

  30.         {  

  31.             System.out.write(b);  

  32.         }  

  33.   

  34.         public SystemOutStream()  

  35.         {  

  36.         }  

  37.     }  

  38.      public ConsoleAppender(Layout layout, String target)  

  39.     {  

  40.         this.target = "System.out";  

  41.         follow = false;  

  42.         setLayout(layout);  

  43.         setTarget(target);  

  44.         activateOptions();  

  45.     }  

  46.     public void activateOptions()  

  47.     {  

  48.         if(follow)  

  49.         {  

  50.             if(target.equals("System.err"))  

  51.                 setWriter(createWriter(new SystemErrStream()));  

  52.             else  

  53.             //设置输出流  

  54.                 setWriter(createWriter(new SystemOutStream()));  

  55.         } else  

  56.         if(target.equals("System.err"))  

  57.             setWriter(createWriter(System.err));  

  58.         else  

  59.             setWriter(createWriter(System.out));  

  60.         super.activateOptions();  

  61.     }  

  62. }  


总结: 
从上面的分析我们可以看出,log日志的输出,是遍历rootLogger的Appender来处理日志输出事件,Appender,首先确定日志级别是否大于RootLogger的日志级别,大于,则处理日志,而日志的输出委托给QuietWriter,而QuietWriter来源于DailyRollingFileAppender, 
ConsoleAppender。