上节介绍日志级别的顺序,知道了日志打印请求的级别必须大于或等于当前logger的日志级别才能打印出,这次来学习下日志级别的层级继承关系。
我们先看示例代码:
package com.zhuzhuodong.share.log4j.lesson2; import com.zhuzhuodong.share.log4j.util.SystemUtil; import org.apache.log4j.BasicConfigurator; import org.apache.log4j.Level; import org.apache.log4j.Logger; public class Lesson2 { public static void main(String[] args) { BasicConfigurator.configure(); Logger rootLogger = Logger.getRootLogger(); rootLogger.setLevel(Level.INFO); SystemUtil.systemErrPrintlnLogLevel(rootLogger); Logger a = Logger.getLogger("a"); //查看a默认级别及日志打印 SystemUtil.systemErrPrintlnLogLevel(a); a.debug("a.debug"); a.info("a.info"); //设置a日志级别为debug a.setLevel(Level.DEBUG); SystemUtil.systemErrPrintlnLogLevel(a); a.debug("a.debug"); a.info("a.info"); //取消a日志级别设置 a.setLevel(null); SystemUtil.systemErrPrintlnLogLevel(a); a.debug("a.debug"); a.info("a.info"); //定义子级logger ab Logger ab = Logger.getLogger("a.b"); SystemUtil.systemErrPrintlnLogLevel(ab); ab.debug("ab.debug"); ab.info("ab.info"); ab.warn("ab.warn"); //设置a为warn a.setLevel(Level.WARN); SystemUtil.systemErrPrintlnLogLevel(a); SystemUtil.systemErrPrintlnLogLevel(ab); ab.debug("ab.debug"); ab.info("ab.info"); ab.warn("ab.warn"); //设置a为null,提高root级别 a.setLevel(null); rootLogger.setLevel(Level.ERROR); SystemUtil.systemErrPrintlnLogLevel(rootLogger); SystemUtil.systemErrPrintlnLogLevel(a); SystemUtil.systemErrPrintlnLogLevel(ab); ab.debug("ab.debug"); ab.info("ab.info"); ab.warn("ab.warn"); ab.fatal("ab.fatal"); //设置ab级别为debug ab.setLevel(Level.DEBUG); SystemUtil.systemErrPrintlnLogLevel(rootLogger); SystemUtil.systemErrPrintlnLogLevel(a); SystemUtil.systemErrPrintlnLogLevel(ab); ab.debug("ab.debug"); ab.info("ab.info"); ab.warn("ab.warn"); ab.fatal("ab.fatal"); } }
然后贴出控制台输出内容:
# SERR: logger:root level:INFO effectiveLevel:INFO # SERR: logger:a level:null effectiveLevel:INFO 0 [main] INFO a - a.info # SERR: logger:a level:DEBUG effectiveLevel:DEBUG 103 [main] DEBUG a - a.debug 103 [main] INFO a - a.info # SERR: logger:a level:null effectiveLevel:INFO 208 [main] INFO a - a.info # SERR: logger:a.b level:null effectiveLevel:INFO 317 [main] INFO a.b - ab.info 317 [main] WARN a.b - ab.warn # SERR: logger:a level:WARN effectiveLevel:WARN # SERR: logger:a.b level:null effectiveLevel:WARN 531 [main] WARN a.b - ab.warn # SERR: logger:root level:ERROR effectiveLevel:ERROR # SERR: logger:a level:null effectiveLevel:ERROR # SERR: logger:a.b level:null effectiveLevel:ERROR 846 [main] FATAL a.b - ab.fatal # SERR: logger:root level:ERROR effectiveLevel:ERROR # SERR: logger:a level:null effectiveLevel:ERROR # SERR: logger:a.b level:DEBUG effectiveLevel:DEBUG 1169 [main] DEBUG a.b - ab.debug 1170 [main] INFO a.b - ab.info 1170 [main] WARN a.b - ab.warn 1170 [main] FATAL a.b - ab.fatal
我们主要验证以下几点:
1.root logger默认级别debug,其他logger为分配级别时为空;
2.在logger都没有分配级别时,继承root logger的级别;
3.在logger分配级别时,无论父级别或祖先级别是否有分配,都以logger分配级别;
4.logger未分配级别时,向上找父logger直至root logger,以离logger最近的父logger级别为准
以上验证的几点从例子的输出也可以看出,刚开始未设置a级别,则以root为准;设置a的级别后,以a为准;a级别设置为null后,以root为准;ab级别最初设置因为a没有设置级别,以root为准;设置a的级别为warn,则ab继承了a的级别;设置a级别为null并设置root级别为error,ab继承root的级别;设置ab级别为debug后,以ab本身级别为准。
接下来我们研究下这个层级的继承是如何实现的,源码:
public Level getEffectiveLevel() { for(Category c = this; c != null; c=c.parent) { if(c.level != null) return c.level; } return null; // If reached will cause an NullPointerException. }
源码逻辑也比较简单,Logger是继承了Category类的,Category类我们关注的两个属性是level、parent,level是级别,parent是父logger,通过循环一直向上查询,直到查询到level不为空的logger,这个level即为logger最终的level。
还有一点我们注意到注释中写如果返回null,会抛出异常,这也是为什么root logger的level无论使用log4j.xml还是log4j.properties文件配置,都必须设置root logger的级别,相当于对整个代码的统一设置。