你是不是经常看到日志框架(log4j、log4j2、logback等)配置文件中有类似配置,但是始终搞不清楚啥意思?
<root level="INFO">
<appender-ref ref="CONSOLE" />
<appender-ref ref="FILE" />
</root>
<logger name="xxxx" level="DEBUG">
<appender-ref ref="FILE" />
</logger>
<logger name="yyyy" level="DEBUG" />
1,首先搞清楚这里的logger指的是什么?
就是咱们在java代码中创建的logger对象,如下:
Logger log = LoggerFactory.getLogger(App.class);
logger的name有两种指定方式,源码如下:
推荐使用传入当前类型的方式(使用全限定名作为logger的name),因为这种方式能使所有的logger形成一棵树,当然自己传入字符串作为logger的name也可以形成一棵树,只不过相对麻烦,具体看下面说明。
2. logger之间是怎么形成一棵树的
通过logger的name。
假设在com.aa包中有A1.class和A2.class, 在com.bb包中有B1.class和B2.class,在com.aa.user包中AUser.class。
A1.class中创建logger:
Logger logger = LoggerFactory.getLogger(A1.class);
A2.class中创建logger:
Logger logger = LoggerFactory.getLogger(A2.class);
B1.class中创建logger:
Logger logger = LoggerFactory.getLogger(B1.class);
B2.class中创建logger:
Logger logger = LoggerFactory.getLogger(B2.class);
AUser.class中创建logger:
Logger logger = LoggerFactory.getLogger(AUser.class);
那么这几个logger就通过name形成了一定关系,简图如下(蓝色的是目录,绿色的logger):
形成树之后有什么好处?
因为子节点会继承父节点的属性值(如日志级别、appender、日志格式等),所以可以方便批量的控制和管理logger,而root logger相当于树根,可用来做默认配置,比如rootlogger的日志级别设置为INFO,那么下面所有logger的日志级别都是INFO,除非针对某个logger设定它自己的级别,举几个例子如下:
1.设定com.aa包及其子包下的所有logger的日志级别为INFO,使用配置如下:
<logger name="com.aa" level="INFO" />
2.设定com.aa.user包及其子包下的所有logger的日志级别为ERROR,使用配置如下:
<logger name="com.aa.user" level="ERROR" />
3.设定com.bb包及其子包下的所有logger的日志级别为DEBUG,使用配置如下:
<logger name="com.bb" level="DEBUG" />
4.设定所有日志只输出到控制台,但com.bb包及其子包下的所有logger不光要输出到控制台,还要输出到log文件中:
<!--所有的logger都只输出到控制台-->
<root level="INFO">
<appender-ref ref="CONSOLE" />
</root>
<!--com.bb包及其子包下的所有logger,不仅要输出到控制台,还要输出到log文件-->
<logger name="com.bb" level="DEBUG">
<appender-ref ref="CONSOLE" />
<appender-ref ref="myfile" />
</logger>
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder charset="UTF-8">
<!-- 输出日志记录格式 -->
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<appender name="myfile" class="ch.qos.logback.core.rolling.RollingFileAppender">
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- 输出文件路径+文件名 -->
<fileNamePattern>${CATALINA_BASE}/collie.%d{yyyyMMdd}.log</fileNamePattern>
<!-- 保存30天的日志 -->
<maxHistory>30</maxHistory>
</rollingPolicy>
<encoder charset="UTF-8">
<!-- 输出日志记录格式 -->
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
3.总结
① 配置文件中logger标签的name并不是瞎写的,而是要在代码中确实存在的,否则没有任何效果
② 用类型作为logger的name,本质就是使用类的全限定名作为name,它本身就具有树形结构,所以创建logger时推荐使用类型作为入参,自己传入的字符串很难形成树形结构且不好维护
③ logger之间形成树,且子会继承父的属性,如果不想继承,可增加additivity="false"属性,如下:
<logger name="org.example" level="INFO" additivity="false" />
④ 所有日志框架的基本套路: 有 logger、appender、日志格式 三大组件
logger就是在代码中通过LoggerFactory.getLogger创建的对象,我们用它来输出日志,
每个logger都可以通过setXXX方法来设置其日志输出级别、日志输出目的地appender(一般日志格式的配置是在appender内的),
但是如果每个logger都设置一遍这些参数就太繁琐了,所以日志框架的设计者就让这些logger形成一颗树,咱们在配置文件中只需对树上的某些节点进行配置日志级别、appender等就可以了