早就想写这个事情了,起因是自己想写一个东西,其中使用logback日志框架记录日志
打算 将所有包的ERROR及以上级别日志打到一个文件中,各个包下的日志打到对应包的文件中。
起初写的xml配置类似于这样:
<!-- 其中一个appender,其他appender与其相同 ,只有name、file和fileNamePattern不同-->
<appender name="ALL-ERROR" class="">
<file></file>
<rollingPolicy class="">
<fileNamePattern>
${log_dir}/all-error.%d{yyyy-MM-dd}.%
</fileNamePattern>
<maxFileSize>100MB</maxFileSize>
<maxHistory>60</maxHistory>
<totalSizeCap>20GB</totalSizeCap>
</rollingPolicy>
<encoder>
<pattern>
%d{yyyy-MM-dd HH:mm:} [%thread] %-5level %logger - %msg%n
</pattern>
</encoder>
</appender>
<logger name=".package1" level="INFO" additivty="true">
<appender-ref ref="appender-1"/>
</logger>
<logger name=".package2" level="INFO" additivty="true">
<appender-ref ref="appender-2"/>
</logger>
<logger name="" level="ERROR" additivty="true">
<appender-ref ref="ALL-ERROR"/>
</logger>
然而运行后却发现,这样配置后的,并没有达到预期目标。
反而,所有INFO及以上的信息,不仅在appender appender-1
和appender-2
对应的日志文件中有,在appender为ALL-ERROR对应的日志文件中也都有,这是为何?
追踪了一下断点,发现如下代码片段:
/**
* Invoke all the appenders of this logger.
*
* @param event
* The event to log
*/
public void callAppenders(ILoggingEvent event) {
int writes = 0;
for (Logger l = this; l != null; l = ) {
writes += (event);
if (!) {
break;
}
}
// No appenders in hierarchy
if (writes == 0) {
(this);
}
}
这段代码来自logback的 文件,是最终决定日志内容输出在哪里的代码。
从这段代码我们可以发现:
1. logback会找到第一个符合日志级别要求的logger,然后将日志内容输入到这个logger下配置的appender中。举例来说:如果有一个.package1
内的类的INFO级别日志,那么首先会找到logger .package1
,然后找到logger下配置的appender appender-1
;最后根据appender-1
的配置,将日志内容输出到appender-1配置的文件中。
2. 之后,logback根据additivty
检查logger是否允许继承,如果配置为true(默认为true),则查找上一级logger(实际是按照以包名为name查找上一层包的logger),找到logger后,不再判断logger配置是否符合日志级别要求,直接找到对应的appender,将日志内容输出。
这就带来了一个问题,位于低层次包的logger,在接收到日志后,不仅会把它输出到自身的appender中,还会将其传递给位于高层次包logger的appender中,无论高层次包logger配置的日志级别是什么。正因为如此,所以我打算将所有包的ERROR级别 日志输出到一个文件的目的没有实现,反而所有INFO及以上级别的日志都输出了。
按照这个思路,如果logger .package1
和.package2
日记级别为ERROR,而logger 日志级别为INFO的话,是否所有INFO及以上级别的日志都可以记入logger
对应的appender下,而ERROR及以上级别的日志会记入logger
.package1
和.package2
呢?测试证明,是这样。
知道了为什么上面的配置达不到目的,接下来要考虑的是,借助什么方式实现这个需求呢?
logback提供了实现需求的方式:借助Filter来做:
既然logger无法判断日志级别,那我们可以在对应的appender里判断日志级别。
logback的过滤器使用起来可以达到对每一条日志的DENY、ACCEPT和NEUTRAL。
根据文章开始提出的需求,我们需要的是一个绑定appender的,过滤日志等级的filter,那么正好是我们需要的。通过加入如下配置,appender
ALL-ERROR
将只能接受ERROR及以上的日志:
<filter class="">
<level>INFO</level>
</filter>
完整的xml配置如下,仅改变了filter的部分,就实现了需求:
<!-- 其中一个appender,其他appender与其相同 ,只有name、file和fileNamePattern不同,并且没有filter的标签-->
<appender name="ALL-ERROR" class="">
<file></file>
<filter class="">
<level>ERROR</level>
</filter>
<rollingPolicy class="">
<fileNamePattern>
${log_dir}/all-error.%d{yyyy-MM-dd}.%
</fileNamePattern>
<maxFileSize>100MB</maxFileSize>
<maxHistory>60</maxHistory>
<totalSizeCap>20GB</totalSizeCap>
</rollingPolicy>
<encoder>
<pattern>
%d{yyyy-MM-dd HH:mm:} [%thread] %-5level %logger - %msg%n
</pattern>
</encoder>
</appender>
<logger name=".package1" level="INFO" additivty="true">
<appender-ref ref="appender-1"/>
</logger>
<logger name=".package2" level="INFO" additivty="true">
<appender-ref ref="appender-2"/>
</logger>
<logger name="" level="ERROR" additivty="true">
<appender-ref ref="ALL-ERROR"/>
</logger>
更多关于logback的Filter的讲解,请见
1. logback filter文档
2. logback filter博文