阅读Logback文档笔记--Logback的filter配置

来源:互联网 发布:数据分析师 挂靠 编辑:程序博客网 时间:2024/06/05 16:49
在前面我们说过 basic selection rule,这一节我们来讲另一个附加的过滤方法。
Logback filters 可以通过串链方式组成一个复杂过滤规则,类似 linux 系统的 iptables 防火墙。

在Logback-classic中提供两个类型的 filters , 一种是 regular filters , 另一种是 turbo filter。
regular filters 是与appender 绑定的, 而turbo filter是与与logger context(logger 上下文)绑定的,区别就是,turbo filter过滤所有logging request ,而regular filter只过滤某个appender的logging request。


Regular filters
在 logback-classic中,filters 继承 Filter 抽象类,Filter 抽象类有一个 decide()抽象方法,接收一个 ILoggingEvent 对象参数,而在 logback-access 中 则是 AccessEvent 对象。该方法返回一个enum类型 FilterReply。
值可以是
  • DENY:如果方法返回DENY(拒绝),则跳出过滤链,而该 logging event 也会被抛弃。
  • NRUTRAL:如果返回NRUTRAL(中立),则继续过滤链中的下一个过滤器。
  • ACCEPT:如果返回ACCEPT(通过),则跳出过滤链
public abstract FilterReply decide(E event);

下面我们实现一个简单的 Filter
package chapters.filters;

import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.core.filter.Filter;
import ch.qos.logback.core.spi.FilterReply;

public class SampleFilter extends Filter<ILoggingEvent> {

  @Override
  public FilterReply decide(ILoggingEvent event) {   
    if (event.getMessage().contains("sample")) {
      return FilterReply.ACCEPT;
    } else {
      return FilterReply.NEUTRAL;
    }
  }
}

然后配置使用自己的Filter
<configuration>
  <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">

    <filter class="chapters.filters.SampleFilter" />

    <encoder>
      <pattern>
        %-4relative [%thread] %-5level %logger - %msg%n
      </pattern>
    </encoder>
  </appender>
       
  <root>
    <appender-ref ref="STDOUT" />
  </root>
</configuration>


上面的例子中,我们的filter类继承了Filter抽象类,但实际上,我们都会继承 AbstractMatcherFilter ,AbstractMatcherFilter继承自Filter,并提供了两个属性,OnMatch 和 OnMismatch。

下面我们看一下 Logback-classic中封装的几个 regular Filter

LevelFilter
LevelFilter 的过滤是基于 logging event 的 level ,如果等于配置的level,则过滤器通过,否则拒绝。下面是它的配置
<configuration>
  <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
    <filter class="ch.qos.logback.classic.filter.LevelFilter">
      <level>INFO</level>
      <onMatch>ACCEPT</onMatch>
      <onMismatch>DENY</onMismatch>
    </filter>

    <encoder>
      <pattern>
        %-4relative [%thread] %-5level %logger{30} - %msg%n
      </pattern>
    </encoder>
  </appender>
  <root level="DEBUG">
    <appender-ref ref="CONSOLE" />
  </root>
</configuration>


ThresholdFilter
ThresholdFilter 其实跟我们之前说的basic selection rule 很像,也是基于日志等级门槛过滤的。当logging event的 level 大于等于配置等级,才能通过过滤器。
<configuration>
  <appender name="CONSOLE"
    class="ch.qos.logback.core.ConsoleAppender">
    <!-- deny all events with a level below INFO, that is TRACE and DEBUG -->
    <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
      <level>INFO</level>
    </filter>

    <encoder>
      <pattern>
        %-4relative [%thread] %-5level %logger{30} - %msg%n
      </pattern>
    </encoder>
  </appender>
  <root level="DEBUG">
    <appender-ref ref="CONSOLE" />
  </root>
</configuration>



EvaluatorFilter
EvaluatorFilter 是一个通用的 filter,它包含了一个 EventEvaluator对象。EventEvaluator对象负责过滤条件的判断,过滤的结果由 onMatch 和 onMismatch这两个属性决定。
注意: EventEvaluator 是一个抽象类。你也可以实现自己的 Evaluator 

下面我们看几个常见的EventEvaluator

GEventEvaluator
GEventEvaluator接收 Groovy 语言的条件表达式作为判断条件。Groovy evaluation expression 是目前最灵活的表达式。GEventEvaluator需要Groovy运行环境。表达式会在运行时,在解释配置文件的时候被编译。
使用maven 添加groovy包到classpath
<dependency>
  <groupId>org.codehaus.groovy</groupId>
  <artifactId>groovy-all</artifactId>
  <version>2.4.0</version>
</dependency>

在 evaluation expression 中,你可以通过 event 或者缩写 e ,访问 ILoggingEvent对象。例如你可以通过以下表达式判断 当前logging event的等级是否等于DEBUG : event.level == DEBUG 或者 e.level == DEBUG ,如果你想通过大于等于,或小于等于这些操作,就需要将 level转换成int值,再判断。例如下面的例子:
<configuration>
   
  <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
    <filter class="ch.qos.logback.core.filter.EvaluatorFilter">     
      <evaluator class="ch.qos.logback.classic.boolex.GEventEvaluator"> 
        <expression>
           e.level.toInt() >= WARN.toInt() &amp;&amp;  <!-- Stands for && in XML -->
           !(e.mdc?.get("req.userAgent") =~ /Googlebot|msnbot|Yahoo/ )
        </expression>
      </evaluator>
      <OnMismatch>DENY</OnMismatch>
      <OnMatch>NEUTRAL</OnMatch>
    </filter>

    <encoder>
      <pattern>
        %-4relative [%thread] %-5level %logger - %msg%n
      </pattern>
    </encoder>
  </appender>

  <root level="DEBUG">
    <appender-ref ref="STDOUT" />
  </root>
</configuration>

上面的例子过滤掉 logging event 的level小于 warn 的,并且 mdc中 key为req.userAgent的值不是 Googlebot , msnbot, Yahoo 中的其中一个。
需要注意的是,因为mdc中可能不存在对应的键值对,所以可能为空,上面的例子就使用了安全引用符 "?.” 。
我们也注意到了,mdc中包含了HttpServletRequest对象,他是怎么实现的呢?就是通过一个servlet filterMDCInsertingServletFilter实现的。


JaninoEventEvaluator
JaninoEventEvaluator 与 GEventEvaluator 恰恰相反,接收的是一个 java 的判断表达式作为判断条件。JaninoEventEvaluator 依赖于 Janino library
<!-- https://mvnrepository.com/artifact/org.codehaus.janino/janino -->
<dependency>
    <groupId>org.codehaus.janino</groupId>
    <artifactId>janino</artifactId>
    <version>3.0.0</version>
</dependency>
这两个Evaluator相比,GEventEvaluator使用会更加灵活,但JaninoEventEvaluator判断速度会快得多。

Logback-classic会自动帮我们导出logging event的属性到evaluation 表达式中,所以我们可以直接使用以下属性判断:
NameTypeDescriptioneventLoggingEvent原始的logging event 对象。你可以通过该对象获取以下属性。例如event.getMessage()相当于message messageStringlogging request 的原始message。例如,当你编码 I.info(“hello {}”, name); 这时, message的值就是 “hello {}”formattedMessageString格式化后的message。例如:当你编码 I.info(“hello {}”, name); name=“Alice”,则message的值就是 “hello Alice”loggerStringlogger的名称loggerContextLoggerContextVOlogging event 属于的 LoggerContext 对象levelintlogging event 的等级,注意:与GEventEvaluator不同,这里可以直接使用 level > INFO 的方式判断 日志等级,而在GEventEvaluator中需要先转换成int值timeStamplonglogging event 产生的时间markerMarkerlogging request 的 Marker标签。需要注意:marker可以为空,所以你需要自己判断Marker是否为空,避免空指针异常。mdcMapMap类型,包含了该logging event 的MDC值,可以通过:mdc.get(“key”)的方式获取,在logback-classic 0.9.30版本后,mdc永远不为空,但是需要注意,获取的是object对象,按你的编码强制转换类型,例如:((String) mdc.get("k")).contains("val")throwablejava.lang.Throwablelogging event的异常信息,如果没有异常关联,则这个值为null。注意,throwable 不支持序列化,所以在远程日志服务器中,该值为Null, 所以需要使用throwableProxythrowableProxyIThrowableProxylogging event exception 的代理。如果没有异常,则throwableProxy为null,但它支持序列化。


下面看个例子:
<configuration>

  <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
    <filter class="ch.qos.logback.core.filter.EvaluatorFilter">     
      <evaluator> <!-- defaults to type ch.qos.logback.classic.boolex.JaninoEventEvaluator -->
        <expression>return message.contains("billing");</expression>
      </evaluator>
      <OnMismatch>NEUTRAL</OnMismatch>
      <OnMatch>DENY</OnMatch>
    </filter>

    <encoder>
      <pattern>
        %-4relative [%thread] %-5level %logger - %msg%n
      </pattern>
    </encoder>
  </appender>

  <root level="INFO">
    <appender-ref ref="STDOUT" />
  </root>
</configuration>

上面的例子中,我们使用了默认JaninoEventEvaluator,由于OnMatch 被设置成DENY,所以该过滤器会丢弃所有message包含”billing”的logging event。

当然,因为JaninoEventEvaluator的expression 接收的是一个java 代码块,只要求该代码块返回 boolean值就行。所以我们可以来个复杂的:
<evaluator>
  <expression>
    if(logger.startsWith("org.apache.http"))
      return true;

    if(mdc == null || mdc.get("entity") == null)
      return false;

    String payee = (String) mdc.get("entity");

    if(logger.equals("org.apache.http.wire") &amp;&amp; <!-- & encoded as &amp; -->
        payee.contains("someSpecialValue") &amp;&amp;
        !message.contains("someSecret")) {
      return true;
    }

    return false;
  </expression>
</evaluator>


如果你还需要用到正则表达式的话,你还能使用Matcher
Matchers
我们不推荐通过调用String类的matches()方法来匹配规则,因为这样每次都会重新创建一个新Pattern对象,浪费资源。我们推荐matcher服用的方式。例如下面配置:
<configuration debug="true">

  <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
    <filter class="ch.qos.logback.core.filter.EvaluatorFilter">
      <evaluator>       
        <matcher>
          <Name>odd</Name>
          <!-- filter out odd numbered statements -->
          <regex>statement [13579]</regex>
        </matcher>
       
        <expression>odd.matches(formattedMessage)</expression>

      </evaluator>
      <OnMismatch>NEUTRAL</OnMismatch>
      <OnMatch>DENY</OnMatch>
    </filter>
    <encoder>
      <pattern>%-4relative [%thread] %-5level %logger - %msg%n</pattern>
    </encoder>
  </appender>

  <root level="DEBUG">
    <appender-ref ref="STDOUT" />
  </root>
</configuration>



现在我们来说。logback-classic中的第二类过滤器。

TurboFilters
TurboFilter 是一个抽象类,是所有TurboFilter的祖先。与regular filters 一样,他也是串链的逻辑。
其实,Trubo Filter 与 Regular Filter 非常相似,只是有两点主要的不同。
  1. TurboFilter对象是与 logging context绑定的。因此,它会处理所有的logging request,而不是单独的appender。过滤范围更广。
  2. 更重要的是,他们在LoggingEvent对象创建之前就已经调用了。因此TurboFilter 对象并不需要 logging event来过滤logging request。因此会有更好的性能表现。

实现自己的TurboFilter
package chapters.filters;

import org.slf4j.Marker;
import org.slf4j.MarkerFactory;

import ch.qos.logback.classic.Level;
import ch.qos.logback.classic.Logger;
import ch.qos.logback.classic.turbo.TurboFilter;
import ch.qos.logback.core.spi.FilterReply;

public class SampleTurboFilter extends TurboFilter {

  String marker;
  Marker markerToAccept;

  @Override
  public FilterReply decide(Marker marker, Logger logger, Level level,
      String format, Object[] params, Throwable t) {

    if (!isStarted()) {
      return FilterReply.NEUTRAL;
    }

    if ((markerToAccept.equals(marker))) {
      return FilterReply.ACCEPT;
    } else {
      return FilterReply.NEUTRAL;
    }
  }

  public String getMarker() {
    return marker;
  }

  public void setMarker(String markerStr) {
    this.marker = markerStr;
  }

  @Override
  public void start() {
    if (marker != null && marker.trim().length() > 0) {
      markerToAccept = MarkerFactory.getMarker(marker);
      super.start(); 
    }
  }
}

配置使用自己的Turbo Filter
<configuration>
  <turboFilter class="chapters.filters.SampleTurboFilter">
    <Marker>sample</Marker>
  </turboFilter>


  <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
    <encoder>
      <pattern>
        %-4relative [%thread] %-5level %logger - %msg%n
      </pattern>
    </encoder>
  </appender>

  <root>
    <appender-ref ref="STDOUT" />
  </root>
</configuration>



Logback-classic 封装了几个常用的TurboFilter
  • MDCFilter : 通过MDC过滤
  • DynamicThresholdFilter :通过MDC 或 level 过滤
  • MarkerFilter :通过marker标签过滤

下面看个配置:
<configuration>  <turboFilterclass="ch.qos.logback.classic.turbo.MDCFilter">    <MDCKey>username</MDCKey>    <Value>sebastien</Value>    <OnMatch>ACCEPT</OnMatch>  </turboFilter>          <turboFilterclass="ch.qos.logback.classic.turbo.MarkerFilter">    <Marker>billing</Marker>    <OnMatch>DENY</OnMatch>  </turboFilter>  <appendername="console"class="ch.qos.logback.core.ConsoleAppender">    <encoder>      <pattern>%date [%thread] %-5level %logger - %msg%n</pattern>    </encoder>  </appender>  <rootlevel="INFO">    <appender-refref="console"/>  </root>  </configuration>



DuplicateMessageFilter
DuplicateMessageFilter是用来过滤重复日志的。需要注意的是,它的判断方式如下
用例子说明:
logger.debug("Hello "+name0);logger.debug("Hello "+name1);
以上会被认为不重复

logger.debug("Hello {}.", name0);
logger.debug("Hello {}.", name1);
这样则会被认为是重复的。

我们可以通过设置AllowedRepetitions的值来设置重复的阈值。默认值为5。
为了判断是否重复,DuplicateMessageFilter需要维持一个old message的引用在内部缓冲区中。这个缓冲区的大小由 CacheSise 属性决定,默认值是100。
看个例子
<configuration>

  <turboFilter class="ch.qos.logback.classic.turbo.DuplicateMessageFilter">
    <AllowedRepetitions>10</AllowedRepetitions>
    <CacheSize>150</CacheSize>
  </turboFilter>

  <appender name="console" class="ch.qos.logback.core.ConsoleAppender">
    <encoder>
      <pattern>%date [%thread] %-5level %logger - %msg%n</pattern>
    </encoder>
  </appender>

  <root level="INFO">
    <appender-ref ref="console" />
  </root> 
</configuration>




In logback-access
与其他组件一样,logback-access也提供了与logback-classis 差不多的功能。不过logback-classic的event的类型 AccessEvent 
logback-access中,只提供了少数的Filter


CountingFilter
通过 CountingFilter , logback-Access  提供数据统计的功能。初始化后,CountingFilter 就会将自己注册成一个MBean,在JMX server 平台上。你可以通过这个MBean访问访问已经统计的数据,例如每分钟,每小时,每天,每星期,每月经过web-server的数据。
看下官网的配置
<configuration>
  <statusListener class="ch.qos.logback.core.status.OnConsoleStatusListener" />

  <filter class="ch.qos.logback.access.filter.CountingFilter">
    <name>countingFilter</name>
  </filter>


  <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
    <encoder>
      <pattern>%h %l %u %t \"%r\" %s %b</pattern>
    </encoder>
  </appender>

  <appender-ref ref="STDOUT" />
</configuration>

你可以通过 jconsole 应用查看




EvaluatorFilter(上面已经说过,与logback-classic是同一个,若不指定EvaluatorFilter,则默认使用JaninoEventEvaluator,参考上面
logback-access 会自动导出当前AccessEvent对象的属性,你可以在expression中直接使用。详细属性,请查看AccessEvent class source code 
下面看个例子:这个例子过滤 access event 的状态马不是404的日志
<configuration>
  <statusListener class="ch.qos.logback.core.status.OnConsoleStatusListener" />

  <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
    <filter class="ch.qos.logback.core.filter.EvaluatorFilter">
      <evaluator>
        <expression>event.getStatusCode() == 404</expression>
      </evaluator>
      <onMismatch>DENY</onMismatch>
    </filter>

   <encoder><pattern>%h %l %u %t %r %s %b</pattern></encoder>
  </appender>

  <appender-ref ref="STDOUT" />
</configuration>

再来一个:这个例子过滤掉状态码不是404并且,uri包含.css的日志
<configuration>
  <statusListener class="ch.qos.logback.core.status.OnConsoleStatusListener" />
  <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
    <filter class="ch.qos.logback.core.filter.EvaluatorFilter">
      <evaluator name="Eval404">
        <expression>
         (event.getStatusCode() == 404)
           &amp;&amp;
  <!-- ampersand characters need to be escaped -->
         !(event.getRequestURI().contains(".css"))
        </expression>
      </evaluator>
      <onMismatch>DENY</onMismatch>
    </filter>

   <encoder><pattern>%h %l %u %t %r %s %b</pattern></encoder>
  </appender>

  <appender-ref ref="STDOUT" />
</configuration>
0 0
原创粉丝点击