Log4j日志输出详解

泪湿孤枕 提交于 2020-03-02 04:39:30

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

Java代码

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


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

Java代码

  1. public final class NOPLogger extends Logger  
  2. public class Logger extends Category  


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

Java代码 下载

  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代码

  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代码

  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。

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!