阅读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
<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 filter MDCInsertingServletFilter 实现的。


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 表达式中,所以我们可以直接使用以下属性判断:
NameTypeDescription
eventLoggingEvent原始的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, 所以需要使用throwableProxy
throwableProxyIThrowableProxylogging 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

下面看个配置:
<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>
  • 3
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
提供的源码资源涵盖了Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 适合毕业设计、课程设计作业。这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。 所有源码均经过严格测试,可以直接运行,可以放心下载使用。有任何使用问题欢迎随时与博主沟通,第一时间进行解答!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值