扩展log4j系列[二]动态、分文件记log

来源:互联网 发布:一个人做淘宝 心酸 编辑:程序博客网 时间:2024/06/05 16:30

一。背景分析      

log4j 即便配置到炉火纯青,也无法回避大家面临的日志问题:

       1.单个文件无法装下足够的log。曾经在IDC繁忙时,一个10M的log文件只可以支撑不到2分钟, 最大滚10个的话,那么20分钟以前出现的bug就找不到日志了。

       2.都往一个地方记,不同模块的人记录内容不均,有的人一个操作打一屏log,有的人异常了都不记日志。。不好管理。

       3. 如此快速的日志滚动,通过tail -f 去观察简直就是一个悲剧。

 

二。具体实现

        这里提到的分文件记log,不是在log4j.properties里面一个个类的去指定输出到哪个文件。。这样每次修改、新增模块都需要改配置,而且不能动态的把一系列操作的日志轨迹串联起来。

 

       1.下面分析了一下log4j几个核心类的关系,注意我们调用得最多的Logger对象(Category这个类已经被子类Logger代替,并且不推荐使用)


 

它从根类继承了这么一个犀利的方法:addAppender , 我们可以在Logger实例化的时候,顺带给它加上自己定制的appender

 

  /**     Add <code>newAppender</code> to the list of appenders of this     Category instance.     <p>If <code>newAppender</code> is already in the list of     appenders, then it won't be added again.  */  synchronized  public  void addAppender(Appender newAppender) {    if(aai == null) {      aai = new AppenderAttachableImpl();    }    aai.addAppender(newAppender);    repository.fireAddAppenderEvent(this, newAppender);  }

 

 

 

       2.那么appender有什么好玩意呢?看起来真的很一般,但是它的子类FileAppender就 NB了,注意,有带fileName参数的构造器!!想把日志写到哪个文件,就看你是否会犀利的构造FileAppender了。

 

public class FileAppender extends WriterAppender {  protected boolean bufferedIO = false;  protected int bufferSize = 8*1024;  FileAppender() {  }  public  FileAppender(Layout layout, String filename, boolean append, boolean bufferedIO,       int bufferSize) throws IOException {    this.layout = layout;    this.setFile(filename, append, bufferedIO, bufferSize);  }   public  FileAppender(Layout layout, String filename, boolean append)                                                             throws IOException {    this.layout = layout;    this.setFile(filename, append, false, bufferSize);  }  public  FileAppender(Layout layout, String filename) throws IOException {    this(layout, filename, true);  }

 

 

3.总结一下:

   看起来是这样:在logger第一次初始化的时候,添加我自己的fileappender,

 

public void logXXX(String busiModule, Object message) {String logName = "runtime."+busiModule; //log的nameorg.apache.log4j.Logger logger = Log4jFactory.getInstance(logName);if (logger.getAppender(logName) == null) {initMyAppender(logName, busiModule+".log",logger);}

 

 

   

* 初始化方法里面,使用了带指定filename参数的Fileappender,赋予appender单独的文件。

 

 * @param appenderName * @param logFileName * @param log */private void initMyAppender(String appenderName, String logFileName,org.apache.log4j.Logger  log) {try {//1.创建log文件目录File fileDir = new File(LogDir);if (!fileDir.exists()) {fileDir.mkdirs();}//2.创建appenderRollingFileAppender fileAppender;PatternLayout patternLayout = new PatternLayout();patternLayout.setConversionPattern("%d{yyyy-MM-dd HH:mm:ss} %m%n");fileAppender = new RollingFileAppender(patternLayout, LogDir+logFileName);fileAppender.setMaxFileSize(maxFileSize);fileAppender.setMaxBackupIndex(maxBackupIndex);fileAppender.setName(appenderName);log.addAppender(fileAppender);} catch (IOException e) {throw new RuntimeException(e);}}

 

 

那么在使用的时候,除了记录的消息对象,还需要额外提供一个String字段的model,相同的model导致相同的logger对象,会记录在一个文件;不同的model参数会使得日志分开记录到各自文件。关于这个参数,通常有两种提供方案:

1.选择包名的某个单词,比如"item","deal"传入,这样就是分业务模块记录。

2.使用ThreadLoacl,将用户访问的session做model,这样就可以在一个文件中完整记录用户访问的所有轨迹。

以上两种方案的model则,还以进一步通过封装到接口中,使得调用者无需关心具体model值。

 

 

FAQ:

1.addApender,每个log那不是有多个appender了么?用文件记录岂不是会写多份,耗IO的。。

~~请大家关心一下log4j的这个属性additivity:(#log4j.additivity.runtime=false)

 

more???