上节介绍日志级别的顺序,知道了日志打印请求的级别必须大于或等于当前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的级别,相当于对整个代码的统一设置。
