LogBack学习记录(七)Filters的学习

来源:互联网 发布:windows资源管理器cpu 编辑:程序博客网 时间:2024/06/13 03:16

logback-class提供了两种类型的filters: regular filters 和 turbo filters.

Regular filters

Regular logback-classic filters 继承自 Filter的抽象类,这个抽象类只有一个方法decide(),该方法采用一个ILoggingEvent 实例作为其参数。

filter基于顺序list和三元判断来进行组织。每个filter中的decide(ILoggingEvent event)会按照顺序依次调用,该方法会返回FilterReply枚举值:DENY,NEUTRAL,ACCEPT三个值中的一个。如果返回deny,这个日志事件就会被立即抛弃不再由其他filter进行判断。如果返回值是neutral,就会有下一个filter进行判断。如果已经没有后续filter,那么会对这个日志事件进行处理。如果判断是accept,那么就会立即对该日志事件进行处理,不再进行后续判断。在logback-classic中,可以将filter添加到appender中,通过添加一定数量的filter,可以实现任意的过滤规则来对日志事件进行过滤。比如日志信息中是否包含指定内容,MDC的内容,日志事件的时间属性等等。

Implementing your own Filter 创建自己的filter

创建自己的filter非常简单,只需要继承filter抽象类,并实现decide接口就可以了。下面的例子中,如果日志信息中包含“sample”字符就返回accept,其他的日志事件,则返回neutral。

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;    }  }}

对应的配置文件如下;将samplefilter绑定到appender中。

<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>
在logback配置系统中,配置filter及其组件的属性非常简单。在filter的实现类中添加了相应的setter方法,然后再XML文件中的<filter>元素内容进行配置即可。通常,filter的逻辑会包含两个相互正交的部分,具体来说就是一对符合/不符合测试,和这组测试的返回值。比如,测试信息是否为“footbar”,一个过滤器可能可能到等于时返回accept,不等于时返回neutral,但是另外一个过滤器就当不等于时deny,当等于时返回neutral。logback提供了一个AbstractMatcherFilter class来给每个符合/不符合的判断提供返回值,这是通过OnMatch 和 OnMismatch属性来设置的。大部分的regular filter都会继承自AbstractMatcherFilter。

LevelFilter

LevelFilter基于日志事件等级的匹配程度。如果事件的等级于配置等级相同,filter就会通过onmatch 或者onmismatch的配置接受或者拒绝该事件。具体使用参见下面的例子:

<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是基于特定的阈值进行过滤的。当事件的等级等于或者高于阈值时,decide方法将会返回neutral,而当事件的等级低于阈值时,就会返回denied。配置文件如下:
<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 是一个通贵封装一个EventEvaluator来进行事件评估的过滤器。(原文如下,实在难以翻译 EvaluatorFilter is a generic filter encapsulating an EventEvaluator),就像他的名字那样,它会判断事件是否满足指定的条件,它会根据配置文件中的onmatch或者onmismatch属性返回不同的值。eventevaluator是一个抽象类,可以通过继承这个类实现自己的事件过滤器。

GEventEvaluator

这个采用Groovy表达式进行判断,Groovy暂时不研究。

JaninoEventEvaluator

logback-classic 提供了另一个EventEvaluator 的实现类,叫做JaninoEventEvaluator,该类采用java语句进行条件判定。我们通过类似于判断表达式的方式进行条件是否满足的判断,判断的表达式具有很大弹性。JaninoEventEvaluator的使用需要Janino 类库的支持,相关部分参见前面部分。相对于JaninoEventEvaluator而言,GEventEvaluator采用Groovy语言,会更加方便,但是JaninoEventEvaluator的运行效率跟高。判断表达式是在配置文件进行解析时动态生成的。作为用户,你不用关心他的实现方式,你只要确保你的表达式能够返回一个boolean值就可以了。判断表达式是对当前的日志事件进行判定。logback-classic将会自动根据表达式从日志事件抽取相关属性。判断表达式中用到的一些关键字是大小写敏感的,需要进行注意,具体关键词如下:
NameTypeDescriptioneventLoggingEventThe raw logging event associated with the logging request. All of the following variables are also available from the event. For example, event.getMessage() returns the same String value as the message variable described next.
最原始的日志事件,后面的所有属性都来自于日志事件,如 event.getMessage(),将返回与message相同的值。messageStringThe raw message of the logging request. For some logger l, when you write l.info("Hello {}", name); where name is assigned the value "Alice", then "Hello {}" is the message.
日志的原始信息。比如info("Hello {}", name),其中Hello {} 就是message。formattedMessageStringThe formatted message in the logging request. For some logger l, when you write l.info("Hello {}", name); where name is assigned the value "Alice", then "Hello Alice" is the formatted message.
格式化后的日志信息,比如("Hello {}", name),当将那么赋值为alice时,formatted message的值就是"Hello Alice。loggerStringThe name of the logger. 日志的名称loggerContextLoggerContextVOA restricted (value object) view of the logger context to which the logging event belongs to.
日志的上下文对象levelintThe int value corresponding to the level. To help create easily expressions involving levels, the default valueDEBUGINFOWARN and ERROR are also available. Thus, using level > INFO is a correct expression.
日志的级别,可以用 level>INFO 的方式进行表示timeStamplongThe timestamp corresponding to the logging event's creation.
日志的创建时间markerMarkerThe Marker object associated with the logging request. Note that marker can be null and it is your responsibility to check for this condition in order to avoid NullPointerException.
日志的标记,感觉很少用。mdcMapA map containing all the MDC values at the time of the creation of the logging event. A value can be accessed by using the following expression: mdc.get("myKey"). As of logback-classic version 0.9.30, the 'mdc' variable will never be null.

The java.util.Map type is non-parameterized because Janino does not support generics. It follows that the type returned by mdc.get() is Object and not String. To invoke String methods on the returned value, it must be cast asString. For example, ((String) mdc.get("k")).contains("val").

光看这个很难明白,以后用到再研究吧

throwablejava.lang.ThrowableIf no exception is associated with the event, then the value of the "throwable" variable will be null. Unfortunately, "throwable" does not survive serialization. Thus, on remote systems, its value will always be null. For location independent expressions, use the throwableProxy variable described next.
远程系统中不要使用throwableProxyIThrowableProxyA proxy for the exception associated with the logging event. If no exception is associated with the event, then the value of the "throwableProxy" variable will be null. In contrast to "throwable", when an exception is associated with an event, the value of "throwableProxy" will be non-null even on remote systems, that is even after serialization.
远程系统中可以使用。

举例如下:

<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>

配置文件的加粗部分讲EvaluatorFilter 配置到consoleappender中。在EvaluatorFilter 的 evaluator 属性采用了默认的配置,也就是JaninoEventEvaluator 。当然,很少会出现这种将Joran 组建作为默认值得情况。判断表达语言message.contains("billing")将根据内容中是否包含billing及OnMismatch 和match返回不同的值。下面先显示没有加上过滤器的日志输出情况:

0    [main] INFO  chapters.filters.FilterEvents - logging statement 00    [main] INFO  chapters.filters.FilterEvents - logging statement 10    [main] INFO  chapters.filters.FilterEvents - logging statement 20    [main] DEBUG chapters.filters.FilterEvents - logging statement 30    [main] INFO  chapters.filters.FilterEvents - logging statement 40    [main] INFO  chapters.filters.FilterEvents - logging statement 50    [main] ERROR chapters.filters.FilterEvents - billing statement 60    [main] INFO  chapters.filters.FilterEvents - logging statement 70    [main] INFO  chapters.filters.FilterEvents - logging statement 80    [main] INFO  chapters.filters.FilterEvents - logging statement 9

加上了过滤器的运行结果:

0 [main] INFO chapters.filters.FilterEvents - logging statement 0

0 [main] INFO chapters.filters.FilterEvents - logging statement 1

0 [main] INFO chapters.filters.FilterEvents - logging statement 2

0 [main] DEBUG chapters.filters.FilterEvents - logging statement 3

0 [main] INFO chapters.filters.FilterEvents - logging statement 4

0 [main] INFO chapters.filters.FilterEvents - logging statement 5

0 [main] INFO chapters.filters.FilterEvents - logging statement 7

0 [main] INFO chapters.filters.FilterEvents - logging statement 8

0 [main] INFO chapters.filters.FilterEvents - logging statement 9

还可以用java语句块来作为判断表达式,例子如下:

<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>

Matchers

在对String进行判断时在每次调用matches()方法时,都会进行模式的对比,这将会在每次都重新构造一个pattern对象,从而造成资源消耗。可以通过事先定义若干个Matcher对象以便重复使用。通过name进行引用。举例如下:
<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>

得到如下运行结果:

260 [main] INFO chapters.filters.FilterEvents - logging statement 0

264 [main] INFO chapters.filters.FilterEvents - logging statement 2

264 [main] INFO chapters.filters.FilterEvents - logging statement 4

266 [main] ERROR chapters.filters.FilterEvents - billing statement 6

266 [main] INFO chapters.filters.FilterEvents - logging statement 8


TurboFilters

所有的TurboFilter对象都继承自TurboFilter 抽象类。它也是采用三元逻辑来返回对日志事件的判定结果的。虽然很多地方都和前面提到的过滤器相同,但是有两点不同:TurboFilter 是与日志上下文相关联的。所以,并不是当appender调用才会使用TurboFilter ,而是在每个日志事件发生时,都会调用TurboFilter 。他所处理的范围将大于appender上绑定的其他过滤器。

另外更加重要的一点,在日志事件对象创建之前,TurboFilter 就会被调用。TurboFilter 对象不依赖于日志事件,因此,TurboFilter 对于日志事件的过滤有着更好的效率,他将在日志事件创建前完成创建。

Implementing your own TurboFilter

创建自己的TurboFilter 组件,只需要继承TurboFilter 抽象类就可以了。之前的例子中,我们只是简单的实现了decide方法,下面这个例子要稍微复杂一些。

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();     }  }}

这个TurboFilter的逻辑如下:当日志事件中包含指定的标记是,就接受。如果没有发现,就交给下一个filter处理。为了更加灵活,marker的值可以通过配置文件进行控制。并且实现了一个start方法用以确认相关属性已经进行配置

下面是该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提供了一些TurnboFilter的实现类以供使用。MDCFilter不介绍。(MDC没整明白干啥的。)

DuplicateMessageFilter

重复过滤器是一个单独的部分,它能够发现重复信息,当超过一定的重复数量,就抛弃重复了的信息。这个过滤器是基于string的相等来判断重复的,所以两条语句即使非常相似,也是不同的。如下所示
logger.debug("Hello "+name0);logger.debug("Hello "+name1);

如果name的值不一样,那么将不视为不一样。

需要注意的是,只有原始信息参与比较, 如下所示,只有Hell{}.部分参与比较,所以下面的语句被视为是相同的。

logger.debug("Hello {}.", name0);logger.debug("Hello {}.", name1);

可以被重复的数量通过AllowedRepetitions 属性进行控制,当设置为1时,第二个重复的信息将被抛弃,一次类推。默认情况下,这个值被设置为5.

为了发现重复,过滤器需要建立内存保留历史信息,这个内存大小通过CacheSize 属性设置,默认情况下,这个值设置为100.举例配置文件如下:

<configuration>  <turboFilter class="ch.qos.logback.classic.turbo.DuplicateMessageFilter"/>  <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>

Thus, the output for FilterEvents application configured with duplicateMessage.xml is:

2008-12-19 15:04:26,156 [main] INFO chapters.filters.FilterEvents - logging statement 0

2008-12-19 15:04:26,156 [main] INFO chapters.filters.FilterEvents - logging statement 1

2008-12-19 15:04:26,156 [main] INFO chapters.filters.FilterEvents - logging statement 2

2008-12-19 15:04:26,156 [main] INFO chapters.filters.FilterEvents - logging statement 4

2008-12-19 15:04:26,156 [main] INFO chapters.filters.FilterEvents - logging statement 5

2008-12-19 15:04:26,171 [main] ERROR chapters.filters.FilterEvents - billing statement 6

"logging statement 0" 是第一次出现 "logging statement {}"的信息, "logging statement 1" 是第一次重复, "logging statement 2" 是第二次重复. 需要注意的是"logging statement 3" of level DEBUG, 是第三次重复,即使按照info的级别过滤,他也将被过滤掉。由此可见turbo filters会在其他的filter之前启用,包括基本的过滤规则。因此,DuplicateMessageFilter 认为 "logging statement 3"是一个重复. "logging statement 4" 是第四次,"logging statement 5" 是第五次. 由于默认设置为5次,Statements 6将被抛弃。

Filter翻译完毕。



原创粉丝点击