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

在前面我们说过 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

    name = "STDOUT"  class = "ch.qos.logback.core.ConsoleAppender" >

     class="chapters.filters.SampleFilter" />

   
     
        %-4relative [%thread] %-5level %logger - %msg%n
     
   
 
       
 
      ref = "STDOUT"  />
 


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

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

LevelFilter
LevelFilter 的过滤是基于 logging event 的 level ,如果等于配置的level,则过滤器通过,否则拒绝。下面是它的配置

    name = "CONSOLE"  class = "ch.qos.logback.core.ConsoleAppender" >
     class="ch.qos.logback.classic.filter.LevelFilter">
      INFO
      ACCEPT
      DENY
   

   
     
        %-4relative [%thread] %-5level %logger{30} - %msg%n
     
   
 
    level = "DEBUG" >
      ref = "CONSOLE"  />
 


ThresholdFilter
ThresholdFilter 其实跟我们之前说的 basic selection rule 很像,也是基于日志等级门槛过滤的。当logging event的 level 大于等于配置等级,才能通过过滤器。

    name = "CONSOLE"
    class = "ch.qos.logback.core.ConsoleAppender" >
   
     class="ch.qos.logback.classic.filter.ThresholdFilter">
      INFO
   

   
     
        %-4relative [%thread] %-5level %logger{30} - %msg%n
     
   
 
    level = "DEBUG" >
      ref = "CONSOLE"  />
 



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

下面我们看几个常见的EventEvaluator

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

  org.codehaus.groovy
  groovy-all
  2.4.0

在 evaluation expression 中,你可以通过 event 或者缩写 e ,访问 ILoggingEvent对象。例如你可以通过以下表达式判断 当前logging event的等级是否等于DEBUG :  event.level == DEBUG 或者  e.level == DEBUG ,如果你想通过大于等于,或小于等于这些操作,就需要将 level转换成int值,再判断。例如下面的例子:

   
    name = "STDOUT"  class = "ch.qos.logback.core.ConsoleAppender" >
     class="ch.qos.logback.core.filter.EvaluatorFilter">     
       class="ch.qos.logback.classic.boolex.GEventEvaluator"> 
       
           e.level.toInt() >= WARN.toInt() && 
           !(e.mdc?.get("req.userAgent") =~ /Googlebot|msnbot|Yahoo/ )
       
     
      DENY
      NEUTRAL
   

   
     
        %-4relative [%thread] %-5level %logger - %msg%n
     
   
 

    level = "DEBUG" >
      ref = "STDOUT"  />
 

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


JaninoEventEvaluator
JaninoEventEvaluator 与  GEventEvaluator 恰恰相反,接收的是一个 java 的判断表达式作为判断条件。 JaninoEventEvaluator 依赖于 Janino library
    org.codehaus.janino
    janino
    3.0.0
这两个Evaluator相比,GEventEvaluator使用会更加灵活,但 JaninoEventEvaluator判断速度会快得多。

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


下面看个例子:


    name = "STDOUT"  class = "ch.qos.logback.core.ConsoleAppender" >
     class="ch.qos.logback.core.filter.EvaluatorFilter">     
       
        return message.contains("billing");
     
      NEUTRAL
      DENY
   

   
     
        %-4relative [%thread] %-5level %logger - %msg%n
     
   
 

    level = "INFO" >
      ref = "STDOUT"  />
 

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

当然,因为JaninoEventEvaluator的expression 接收的是一个java 代码块,只要求该代码块返回 boolean值就行。所以我们可以来个复杂的:

 
    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") && 
        payee.contains("someSpecialValue") &&
        !message.contains("someSecret")) {
      return true;
    }

    return false;
 


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

    name = "STDOUT"  class = "ch.qos.logback.core.ConsoleAppender" >
      class = "ch.qos.logback.core.filter.EvaluatorFilter" >
             
       
          odd
         
          statement [13579]
       
       
        odd.matches(formattedMessage)

     
      NEUTRAL
      DENY
   
   
      %-4relative [%thread] %-5level %logger - %msg%n
   
 

    level = "DEBUG" >
      ref = "STDOUT"  />
 



现在我们来说。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

   class="chapters.filters.SampleTurboFilter">
    sample
 


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

 
      ref = "STDOUT"  />
 



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

下面看个配置:


  class="ch.qos.logback.classic.turbo.MDCFilter">
    username
    sebastien
    ACCEPT
  
        
  class="ch.qos.logback.classic.turbo.MarkerFilter">
    billing
    DENY
  

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

  level="INFO">
    ref="console"/>
    





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。
看个例子

   class="ch.qos.logback.classic.turbo.DuplicateMessageFilter">
    10
    150
 

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

    level = "INFO" >
      ref = "console"  />
   




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的数据。
看下官网的配置

    class = "ch.qos.logback.core.status.OnConsoleStatusListener"  />

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


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

    ref = "STDOUT"  />

你可以通过 jconsole 应用查看
阅读Logback文档笔记--Logback的filter配置_第1张图片



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

    class = "ch.qos.logback.core.status.OnConsoleStatusListener"  />

    name = "STDOUT"  class = "ch.qos.logback.core.ConsoleAppender" >
     class="ch.qos.logback.core.filter.EvaluatorFilter">
     
        event.getStatusCode() == 404
     
      DENY
   

    %h %l %u %t %r %s %b
 

    ref = "STDOUT"  />

再来一个:这个例子过滤掉状态码不是404并且,uri包含.css的日志

    class = "ch.qos.logback.core.status.OnConsoleStatusListener"  />
    name = "STDOUT"  class = "ch.qos.logback.core.ConsoleAppender" >
      class = "ch.qos.logback.core.filter.EvaluatorFilter" >
        name = "Eval404" >
       
          (event.getStatusCode() == 404)
           &&
 
          !(event.getRequestURI().contains(".css"))
       
     
      DENY
   

    %h %l %u %t %r %s %b
 

    ref = "STDOUT"  />

你可能感兴趣的:(logback)