-
概述
在平时的开发过程中,我们一般都会选择将将日志级别设定为DEBUG甚至更低,这样当出现问题时可以知晓更多,更详细的信息,方便问题的排查;但万事有利必有弊,详细也意味着繁琐,大量嘈杂的日志信息可能会将你真正关注的日志内容淹没掉,加大查找难度。因此最近同事提出了一个动态筛选日志信息的优化需求让我十分感兴趣。 -
思考
类似的需求,最容易想到的简单方法就是在进行日志输出时,谨慎选择甚至自定义日志级别,但是考虑到框架里的日志级别不受控制,随意自定义日志级别徒劳增加开发人员的记忆负担等等缺陷,这个想法在出现的瞬间基本就被判了死刑。
通过查阅log4j2官网,最终发现其提供的Filter机制应该是能够满足本次需求的。 -
过滤结果
log4j2走过滤器的逻辑后,会返回对应的过滤Result结果,以控制是否记录日志、怎样记录日志。过滤器的结果有:
ACCEPT:不需要再走后面的过滤器了,需要记录当前日志。
NEUTRAL:需不需要记录当前日志,由后续过滤器决定。若所有过滤器返回的结果都是NEUTRAL,那么需要记录日志。
DENY:不需要再走后面的过滤器了,不需要记录当前日志。
- 配置
log4j2中的Filter可以配置在四个位置,由全局到局部依次是 Context-wide,Logger ,Appender ,Appender Reference。下面给出一个具体的配置示例代码:
<Configuration status="TRACE" monitorInterval="5" packages="com.kanq.extend.cat.log4j2">
<Filters>
<!-- 全局级别Filter -->
<LevelRangeFilter minLevel="DEBUG" maxLevel="ERROR" onMatch="DENY"></LevelRangeFilter>
</Filters>
<Appenders>
<RollingFile name="RollingFile" fileName="logs/app.log"
filePattern="logs/app-%d{MM-dd-yyyy}.log.gz">
<!-- Appender级别的Filter -->
<BurstFilter level="INFO" rate="16" maxBurst="100"/>
<PatternLayout>
<pattern>%d %p %c{1.} [%t] %m%n</pattern>
</PatternLayout>
<TimeBasedTriggeringPolicy />
</RollingFile>
</Appenders>
<Loggers>
<!-- Logger级别的Filter -->
<Root level="error">
<MapFilter onMatch="ACCEPT" onMismatch="NEUTRAL" operator="or">
<KeyValuePair key="eventId" value="Login"/>
<KeyValuePair key="eventId" value="Logout"/>
</MapFilter>
</Root>
<Logger name="TestJavaScriptFilter" level="trace" additivity="false">
<!-- AppenderRef级别的Filter -->
<AppenderRef ref="List">
<ScriptFilter onMatch="ACCEPT" onMisMatch="DENY">
<ScriptRef ref="filter.js" />
</ScriptFilter>
</AppenderRef>
</Logger>
</Loggers>
</Configuration>
-
log4j2提供的过滤器功能简述(基于log4j2.13.3)
-
执行流程
这里贴一下在单元测试下的log4j2调用执行堆栈图(对应的配置文件是上面配置中的全局级别Filter)。
从以上堆栈图中,我们大致可以得到如下信息:
每次的日志记录行为都会涉及到Filter的回调,这也是我们所配置的Filter能够生效的原因。
我们还可以看到设计模式之组合模式的经典应用,以对外提供统一的调用API。 -
测试代码
参考文章 log4j2使用filter过滤日志 -
注意
配置文件中节点的顺序, 全局Filter节点 必须放在节点之后。 否则会出现意料不到的问题。例如本人碰到的就是 替换失败。