Log4j2研究之Filter

x33g5p2x  于2021-12-25 转载在 其他  
字(3.4k)|赞(0)|评价(0)|浏览(474)

作为框架必备的基本组件之一的Filter,在log4j2中同样存在并处于相当重要的地位。本文我们将对其执行逻辑,继承体系等等进行一番简单的探索,意图找出一个动态筛选日志信息的优化需求的解决方案。

1. 概述

在平时的开发过程中,我们一般都会选择将将日志级别设定为DEBUG甚至更低,这样当出现问题时可以知晓更多,更详细的信息,方便问题的排查;但万事有利必有弊,详细也意味着繁琐,大量嘈杂的日志信息可能会将你真正关注的日志内容淹没掉,加大查找难度。因此最近同事提出了一个动态筛选日志信息的优化需求让我十分感兴趣。

2. 思考

类似的需求,最容易想到的简单方法就是在进行日志输出时,谨慎选择甚至自定义日志级别,但是考虑到框架里的日志级别不受控制,随意自定义日志级别徒劳增加开发人员的记忆负担等等缺陷,这个想法在出现的瞬间基本就被判了死刑。

通过查阅log4j2官网,最终发现其提供的Filter机制应该是能够满足本次需求的。这里贴一下官网对Filter的介绍:

1. Filters allow Log Events to be evaluated to determine if or how they should be published. 
2. A Filter will be called on one of its filter methods and will return a Result, which is an Enum that has one of 3 values - ACCEPT, DENY or NEUTRAL.

3. 配置

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>

4. 执行流程

这里贴一下在单元测试下的log4j2调用执行堆栈图(对应的配置文件是上面配置中的全局级别Filter)。

从以上堆栈图中,我们大致可以得到如下信息:

  1. 每次的日志记录行为都会涉及到Filter的回调,这也是我们所配置的Filter能够生效的原因。
  2. 我们还可以看到设计模式之组合模式的经典应用,以对外提供统一的调用API。

借用下面引用链接中的一张图来描述,大概就是如下这样:

5. 继承体系

最后让我们来看看Filter的继承体系。

通过IDE提供的类层次结构图可以看到,可以说是非常标准的继承实现方式了, 契约接口 —— 骨架抽象类 —— 具体实现类 —— 组合模式。

下面就让我们挑选出一些比较典型的Filter进行简单地分析:

  1. CompositeFilter : 组合模式,用以向外界提供统一的访问接口。
  2. ThresholdFilter : 这个Filter负责按照所配置的日志级别过滤Log Event,等于或超出所配置级别的log Event将返回onMatch结果。(threshold : 阈值, 临界值; 门槛,入口)
  3. LevelRangeFilter : 这个Filter也是负责按照日志级别过滤Log,只是相比较于ThresholdFilter,它比较的是指定区间范围。**这里有一个需要密切注意就是其两个参数minLevelmaxLevel的配置;其和我们的思维惯式有着些许的差异,可能会造成困扰。**细节之处可以查看StandardLevel中各个日志级别的底层数值,以及Level.isInRange的比较逻辑。概括而言就是 底层支撑的数值越小,日志危险级别越高。也就是我们如果想要只保留WARN到ERROR级别的日志,那么应该如下配置:
<!-- 危险级别高的等级配置 作为minLevel的值 -->
    <LevelRangeFilter minLevel="ERROR" maxLevel="WARN"
        onMatch="ACCEPT">
    </LevelRangeFilter>
    ```
  1. DynamicThresholdFilter :这个Filter允许依据ThreadContext中是否存在特定的某些值,以及日志级别来过滤LOG。
  2. 略。

6. 注意

  1. 配置文件中节点的顺序, 全局Filter节点<Filters> 必须放在<properties>节点之后。 否则会出现意料不到的问题。例如本人碰到的就是 <PatternLayout pattern="${pattern_debug_info_warn}" /> 替换失败。
  1. Office Site - Filter
  2. Filters - configuration - Office Site
  3. 卫星系统——酒店后端全链路日志收集工具介绍 – 其中涉及Filter的使用
  4. Log4j2自定义过滤器

相关文章