第 7 章:过滤器

**有很多主意,可丢掉坏主意。除非您有很多想法和某种选择原则,否则您将不会有好的想法.

—LINUS PAULING**

在前面的章节中,介绍了基本选择规则,它是 logback-classic 的核心。在本章中,将介绍其他过滤方法。

Logback 过滤器基于三元逻辑,允许将它们组装或链接在一起以构成任意复杂的过滤策略。它们很大程度上受到 Linux iptables 的启发。

In logback-classic

Logback-classic 提供两种类型的过滤器,常规过滤器和 Turbine 过滤器。

Regular filters

常规的 logback-classic 过滤器扩展了Filter抽象类,该类基本上由一个以ILoggingEvent实例作为参数的decide()方法组成。

过滤器被组织成一个有序列表,并基于三元逻辑。依次调用每个过滤器的decide(ILoggingEvent event)方法。此方法返回FilterReply枚举值之一,即DENYNEUTRALACCEPT之一。如果decide()返回的值为DENY,则立即丢弃日志事件,而无需咨询其余过滤器。如果返回的值为NEUTRAL,则查询列表中的下一个过滤器。如果没有其他过滤器可参考,则将正常处理日志记录事件。如果返回的值为ACCEPT,则将立即跳过其余过滤器的调用来处理日志记录事件。

在经典的 logback 模式中,可以将过滤器添加到Appender个实例。通过将一个或多个过滤器添加到附加程序,可以按任意条件过滤事件,例如日志消息的内容,MDC 的内容,一天中的时间或日志事件的任何其他部分。

实施自己的过滤器

创建自己的过滤器很容易。您要做的就是扩展Filter抽象类并实现decide()方法。

下面显示的 SampleFilter 类提供了一个示例。其decide方法返回 ACCEPT 来记录其消息字段中包含字符串“ sample”的事件。对于其他事件,返回值 NEUTRAL。

例如:基本的自定义过滤器(logback-examples/src/main/java/chapters/filters/SampleFilter.java)

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附加到ConsoleAppender

*示例:SampleFilter 配置(logback-examples/src/main/resources/chapters/filters/SampleFilterConfig.xml)*查看为.groovy

<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 的配置框架 Joran 的帮助,为过滤器指定属性或子组件也很容易。在过滤器类中添加了相应的 setter 方法之后,请在以该属性命名的 xml 元素中指定该属性的值,并将其嵌套在<filter>元素内。

通常,所需的滤波器逻辑由两个正交部分组成,一个匹配/不匹配测试和一个取决于匹配/不匹配的响应。例如,对于给定的测试,例如消息等于“ foobar”,一个过滤器可能在匹配时响应 ACCEPT 而在不匹配时响应 NEUTRAL,而另一个过滤器可能在匹配时响应 NEUTRAL 而在不匹配时响应 DENY。

注意这一正交性,logback 附带了AbstractMatcherFilter类,该类提供了一个有用的框架,借助两个属性* OnMatch OnMismatch *,可以指定对匹配和不匹配的适当响应。 logback 中包含的大多数常规过滤器均源自AbstractMatcherFilter

LevelFilter

LevelFilter根据确切的级别匹配过滤事件。如果事件的级别等于已配置的级别,则过滤器将接受或拒绝事件,具体取决于 onMatch 和 onMismatch 属性的配置。这是一个示例配置文件。

*示例:示例 LevelFilter 配置(logback-examples/src/main/resources/chapters/filters/levelFilterConfig.xml)*查看为.groovy

<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过滤低于指定阈值的事件。对于等于或高于阈值的事件,当ThresholdFilterdecide()方法被调用时,它将响应 NEUTRAL。但是,级别低于阈值的事件将被拒绝。这是一个示例配置文件。

*示例:示例 ThresholdFilter 配置(logback-examples/src/main/resources/chapters/filters/thresholdFilterConfig.xml)*查看为.groovy

<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的通用过滤器。顾名思义,EventEvaluator评估给定事件是否满足给定条件。在匹配和不匹配时,主机EvaluatorFilter将分别返回由 onMatch 或 onMismatch 属性指定的值。

请注意,EventEvaluator是抽象类。您可以通过子类EventEvaluator来实现自己的事件评估逻辑。

GEventEvaluator

GEventEvaluator是具体的EventEvaluator实现,它采用任意 Groovy 语言布尔表达式作为评估标准。我们将此类 Groovy 语言布尔表达式称为“ groovy 评估表达式”。 Groovy 评估表达式为事件过滤提供了前所未有的灵 Active。 GEventEvaluator需要 Groovy 运行时。在将 Groovy 运行时添加到 Classpath 时,请参阅安装文档的corresponding section

评估表达式是在配置文件的解释过程中即时编译的。作为用户,您不必担心实际的管道问题。但是,您有责任确保 groovy 语言表达式有效。

评估表达式作用于当前的日志事件。 Logback 会自动将类型为ILoggingEvent的当前日志记录事件作为变量“ * event *”以及其简写形式“ * e *”插入。变量 TRACE,DEBUG,INFO,WARN 和 ERROR 也会导出到表达式的范围内。因此,“ event.level == DEBUG”和“ e.level == DEBUG”是等效且有效的常规表达式,仅当当前日志记录事件的级别与 DEBUG 相同时才返回true。对于级别上的其他比较运算符,应该使用toInt()运算符将级别字段转换为整数。

这是一个更完整的示例。

观看为.groovy

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

除非错误是由与 Google,MSN 或 Yahoo 关联的 Web 搜寻器生成的,否则上述过滤器将使 WARN 级及更高级别的事件通过控制台。通过检查与事件关联的 MDC 是否包含与/Googlebot|msnbot|Yahoo/正则表达式匹配的“ req.userAgent”的值来实现此目的。请注意,由于 MDCMap 可以为 null,因此我们也使用 Groovy 的安全解引用运算符,即?。操作员。如果用 Java 表示,等效逻辑将更长。

如果您想知道用户代理的标识符是如何在'req.userAgent'键下插入 MDC 的,那么我们应该提到 logback 附带了一个名为MDCInsertingServletFilter的 servlet 过滤器。将在下一章中进行描述。

JaninoEventEvaluator

Logback-classic 附带了另一个名为JaninoEventEvaluator的具体EventEvaluator实现,它采用任意 Java 语言块返回布尔值作为评估标准。我们将此类 Java 语言布尔表达式称为“ * evaluation expressions *”。评估表达式为事件过滤提供了极大的灵 Active。 JaninoEventEvaluator要求Janino library。请参阅安装文档的corresponding section。与JaninoEventEvaluator相比,借助 Groovy 语言,GEventEvaluator更加方便使用,但是JaninoEventEvaluator通常对于等效表达式运行(快得多)。

评估表达式是在配置文件的解释过程中即时编译的。作为用户,您不必担心实际的管道问题。但是,您有责任确保 Java 语言表达式返回一个布尔值,即它的计算结果为 true 或 false。

评估表达式是在当前日志记录事件上评估的。 Logback-classic 自动将日志记录事件的各个字段导出为可从评估表达式访问的变量。下面列出了这些导出变量的区分大小写的名称。

NameTypeDescription
eventLoggingEvent与日志记录请求关联的原始日志记录事件。事件中还提供以下所有变量。例如,event.getMessage()返回与下面描述的* message *变量相同的 String 值。
messageString日志记录请求的原始消息。对于某些 Logger* l *,当您编写 l.info(“ Hello{}”,name);其中为名称分配了值“ Alice”,则消息为“ Hello{}”。
formattedMessageString日志记录请求中的格式化消息。对于某些 Logger* l *,当您编写 l.info(“ Hello{}”,name);其中为名称分配了值“ Alice”,则“ Hello Alice”是格式化的消息。
loggerStringLogger 的名称。
loggerContextLoggerContextVO记录事件所属的 Logger 上下文的受限(值对象)视图。
levelint对应于级别的 int 值。为了帮助轻松创建涉及级别的表达式,还提供了默认值* DEBUG INFO WARN ERROR 。因此,使用 level> INFO *是正确的表达式。
timeStamplong与记录事件的创建相对应的时间戳。
markerMarker与日志记录请求关联的Marker对象。请注意,标记可以为 null,您有责任检查此条件以避免NullPointerException
mdcMap在创建日志记录事件时包含所有 MDC 值的 Map。可以使用以下表达式访问值:* mdc.get(“ myKey”)*。从经典 logback 版本 0.9.30 开始,“ mdc”变量将永远不会为 null。


java.util.Map类型是非参数化的,因为 Janino 不支持泛型。因此,由mdc.get()返回的类型是Object而不是String。要对返回的值调用String方法,必须将其强制转换为String。例如((String) mdc.get("k")).contains("val")
| throwable | java.lang.Throwable |如果没有异常与事件关联,则“ throwable”变量的值将为 null。不幸的是,“ throwable”无法在序列化中幸免。因此,在远程系统上,其值将始终为 null。对于位置无关的表达式,请使用下面描述的throwableProxy变量。 |
| throwableProxy | IThrowableProxy |与日志记录事件关联的异常的代理。如果没有异常与事件关联,则“ throwableProxy”变量的值将为 null。与“ throwable”相反,当异常与事件相关联时,即使在远程系统上(即在序列化之后),“ throwableProxy”的值也不会为空。 |

这是一个具体的例子。

*示例:基本事件评估程序的用法(logback-examples/src/main/resources/chapters/filters/basicEventEvaluator.xml)*查看为.groovy

<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。然后将类型JaninoEventEvaluator的评估器注入EvaluatorFilter。如果用户指定的<evaluator>元素中没有 class 属性,Joran 将为评估者推断默认类型JaninoEventEvaluator。这是few occurrences之一,其中 Joran 隐式推断组件的类型。

  • expression 元素对应于刚刚讨论的评估表达式。表达式return message.contains("billing");返回布尔值。注意, message *变量由JaninoEventEvaluator自动导出。

假定 OnMismatch 属性设置为 NEUTRAL 且 OnMatch 属性设置为 DENY,则此评估程序过滤器将删除其消息包含字符串“ billing”的所有日志记录事件。

FilterEvents应用程序发出 10 个日志记录请求,编号为 0 到 9.让我们首先运行FilterEvents类,不使用任何过滤器:

java chapters.filters.FilterEvents src/main/java/chapters/filters/basicConfiguration.xml

将显示所有请求,如下所示:

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] ERROR chapters.filters.FilterEvents - billing statement 6
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

假设我们要摆脱“计费声明”。上面列出的* basicEventEvaluator.xml *配置文件对包含字符串“ billing”的消息进行过滤,这恰好是所需的结果。

使用* basicEventEvaluator.xml *运行:

Java Chapters.filters.FilterEvents src/main/java/chapters/filters/basicEventEvaluator.xml

we obtain:

0 [main] INFO 的 chapters.filters.FilterEvents-日志记录语句 0 0 [main] INFO 的 chapters.filters.FilterEvents-日志记录语句 1 0 [main] INFO 的 chapters.filters.FilterEvents-日志记录语句 2 0 [main]调试的 Chapters.filters .FilterEvents-日志记录语句 3 0 [main] INFO 章.filters.FilterEvents-日志记录语句 4 0 [main] INFO 章.filters.FilterEvents-日志记录语句 5 0 [main] INFO Chapters.filters.FilterEvents-日志记录语句 7 0 [ [主] INFO 的 chapters.filters.FilterEvents-日志记录语句 8 0 [主] INFO 的 chapters.filters.FilterEvents-日志记录语句 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对象。为了消除这种开销,您可以 sched 义一个或多个Matcher对象。定义匹配器后,可以在评估器表达式中按名称重复引用它。

一个例子可以阐明这一点:

*示例:在事件评估器中定义匹配器(logback-examples/src/main/resources/chapters/filters/evaluatorWithMatcher.xml)*查看为.groovy

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

使用* evaluatorWithMatcher.xml *运行:

Java Chapters.filters.FilterEvents src/main/java/chapters/filters/evaluatorWithMatcher.xml

we obtain:

260 [main] INFO 的 chapters.filters.FilterEvents-日志记录语句 0 264 [main] INFO 的 chapters.filters.FilterEvents-日志记录语句 2264 [main] INFO 的 chapters.filters.FilterEvents-日志记录语句 4 266 [main] ERROR 的 chapters.filters .FilterEvents-结算语句 6 266 [main] INFO 章节。filters.FilterEvents-日志记录语句 8

如果需要定义其他匹配器,可以通过添加更多的<matcher>元素来实现。

TurboFilters

TurboFilter个对象都扩展了TurboFilter抽象类。像常规过滤器一样,它们使用三元逻辑返回对日志记录事件的评估。

总体而言,它们的工作方式与前面提到的过滤器非常相似。但是,FilterTurboFilter对象之间有两个主要区别。

TurboFilter个对象绑定到日志记录上下文。因此,它们不仅在使用给定的附加程序时被调用,而且在每次发出日志记录请求时都被调用。它们的范围比附加附加过滤器的范围大。

更重要的是,它们在创建LoggingEvent对象之前被调用。 TurboFilter对象不需要实例化日志记录事件即可过滤日志记录请求。这样,即使在事件创建之前,turbo 过滤器也旨在对日志事件进行高性能过滤。

实现自己的 TurboFilter

要创建自己的TurboFilter组件,只需扩展TurboFilter抽象类。如前所述,实现自定义过滤器对象时,开发自定义TurboFilter仅要求实现decide()方法。在下一个示例中,我们创建一个稍微复杂一些的过滤器:

例如:基本自定义TurboFilter(logback-examples/src/main/java/chapters/filters/SampleTurboFilter.java)

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

上面的TurboFilter接受包含特定标记的事件。如果找不到所述标记,则过滤器将责任传递给链中的下一个过滤器。

为了提供更大的灵 Active,可以在配置文件中指定要测试的标记,因此可以指定 getter 和 setter 方法。我们还实现了start()方法,以检查是否在配置过程中指定了该选项。

这是一个使用我们新创建的TurboFilter的示例配置。

*示例:基本的自定义TurboFilter配置(logback-examples/src/main/resources/chapters/filters/sampleTurboFilterConfig.xml)*以.groovy 格式查看

<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 附带了几个可供使用的TurboFilter类。 MDCFilter检查 MDC 中是否存在给定值,而DynamicThresholdFilter允许基于 MDC 密钥/级别阈值关联进行过滤。另一方面,MarkerFilter检查是否存在与日志记录请求关联的特定标记。

这是使用MDCFilterMarkerFilter的示例配置。

*示例:MDCFilterMarkerFilter配置(logback-examples/src/main/resources/chapters/filters/turboFilters.xml)*以.groovy 格式查看

<configuration>

  <turboFilter class="ch.qos.logback.classic.turbo.MDCFilter">
    <MDCKey>username</MDCKey>
    <Value>sebastien</Value>
    <OnMatch>ACCEPT</OnMatch>
  </turboFilter>
	
  <turboFilter class="ch.qos.logback.classic.turbo.MarkerFilter">
    <Marker>billing</Marker>
    <OnMatch>DENY</OnMatch>
  </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>

您可以通过发出以下命令来查看此配置的运行情况:

Java Chapters.filters.FilterEvents src/main/java/chapters/filters/turboFilters.xml

如我们先前所见,FilterEvents应用程序发出 10 个日志记录请求,编号为 0 到 9.除了请求 3 和 6,所有请求的级别均为* INFO ,与分配给根 Logger 的级别相同。第三请求以 DEBUG 级别发出,该级别低于有效级别。但是,由于 MDC 密钥“用户名”在第三个请求之前被设置为“ sebastien”,而在此之后被删除,因此MDCFilter专门接受该请求(并且仅接受该请求)。以 ERROR *级别发出的第六个请求被标记为“计费”。因此,它被 MarkerFilter(配置中的第二个 Turbine 过滤器)拒绝。

因此,使用上面显示的* turboFilters.xml *文件配置的FilterEvents应用程序的输出为:

2006 -12-04 15:17:22,859 [main] INFO pages.filters.FilterEvents-记录语句 0 2006-12-04 15:17:22,875 [main] INFO pages.filters.FilterEvents-记录语句 1 2006-12- 04 15:17:22,875 [主要] INFO pages.filters.FilterEvents-记录语句 2 2006-12-04 15:17:22,875 [主要]调试 chapters.filters.FilterEvents-记录语句 3 2006-12-04 15:17 :22,875 [main] INFO pages.filters.FilterEvents-日志记录语句 4 2006-12-04 15:17:22,875 [main] INFO chapters.filters.FilterEvents-日志记录语句 5 2006-12-04 15:17:22,875 [main] ] INFO chapters.filters.FilterEvents-日志记录语句 7 2006-12-04 15:17:22,875 [main] INFO CHAPTER..filters.FilterEvents-日志记录语句 8 2006-12-04 15:17:22,875 [main] INFO 章。 filter.FilterEvents-日志记录语句 9

可以看到,无论如何,如果我们只遵循整体* INFO *级别,则不应显示第三条请求,因为它符合第一个TurboFilter要求并被接受。

另一方面,应该已经显示了第六个请求,即* ERROR 级请求。但是它满足了第二个TurboFilter,其 OnMatch 选项设置为 DENY *。因此,没有显示第 6 个请求。

DuplicateMessageFilter

DuplicateMessageFilter值得单独介绍。该过滤器检测重复的消息,并在超过一定重复次数后丢弃重复的消息。

为了检测重复,此过滤器在消息之间使用简单的字符串相等性。它不会检测非常相似的消息(仅相差几个字符)。例如,如果您编写:

logger.debug("Hello "+name0);
logger.debug("Hello "+name1);

假设name0name1具有不同的值,则两个“ Hello”消息将被视为无关。根据用户需求,将来的发行版可能会检查字符串的相似性,从而消除相似但不相同的消息的重复。

请注意,在参数化日志记录的情况下,仅考虑原始消息。例如,在接下来的两个请求中,原始消息即“ Hello{}”是相同的,因此被视为重复。

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

允许的重复次数可以由 AllowedRepetitions 属性指定。例如,如果该属性设置为 1,则将删除同一消息的第二次及以后出现。同样,如果将该属性设置为 2,则将删除同一消息的第 3 次及以后出现。默认情况下,AllowedRepetitions 属性设置为 5.

为了检测重复,此过滤器需要在内部缓存中保留对旧消息的引用。此缓存的大小由 CacheSize 属性确定。默认情况下,它设置为 100.

*示例:DuplicateMessageFilter配置(logback-examples/src/main/resources/chapters/filters/duplicateMessage.xml)*以.groovy 格式查看

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

因此,使用* duplicateMessage.xml *配置的FilterEvents应用程序的输出为:

2008 -12-19 15:04:26,156 [main] INFO pages.filters.FilterEvents-记录语句 0 2008-12-19 15:04:26,156 [main] INFO chapters.filters.FilterEvents-记录语句 1 2008-12- 19 15:04:26,156 [main] INFO pages.filters.FilterEvents-记录语句 2 2008-12-19 15:04:26,156 [main] INFO pages.filters.FilterEvents-记录语句 4 2008-12-19 15:04 :26,156 [main] INFO pages.filters.FilterEvents-记录语句 5 2008-12-19 15:04:26,171 [main]错误 chapters.filters.FilterEvents-帐单语句 6

“日志记录语句 0”是消息“日志记录语句\ {}”的第一个出现。 “ logging 语句 1”是第一个“重复”,“ logging 语句 2”是第二个重复。有趣的是,级别 DEBUG 的“ logging 语句 3”是第三重复,即使后来由于基本选择规则而被丢弃。这可以通过以下事实来解释:turbo 过滤器先于其他类型的过滤器(包括基本选择规则)被调用。因此,DuplicateMessageFilter将“记录语句 3”视为重复,而忽略了它将在处理链中进一步下降的事实。 “日志记录声明 4”是第四个重复,“日志记录声明 5”是第五个重复。因为默认情况下仅允许重复 5 次,所以删除了语句 6 及其以后的语句。

In logback-access

Logback-access 提供了经典 logback 可用的大多数功能。特别是,Filter个对象可用,并且以与经典 logback 对象相对应的方式工作,但有一个明显的不同。logback访问过滤器代替LoggingEvent个实例,而不是AccessEvent个实例。目前,logback-access 附带有限数量的以下所述过滤器。如果您想建议其他过滤器,请联系 logback-dev 邮件列表。

CountingFilter

借助CountingFilter类,logback-access 可以提供有关对 Web 服务器的访问的统计数据。初始化后,CountingFilter将自己作为 MBean 注册到平台的 JMX 服务器上。然后,您可以查询该 MBean 以获取统计数据,例如按分钟,小时,天,周或月平均。还可以提供其他统计信息,例如前一周,天,小时或月的计数以及总计数。

以下* logback-access.xml *配置文件声明了CountingFilter

<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应用程序检查CountingFilter在平台的 JMX 服务器上维护的各种统计信息。

通过 jconsole 进行 CountingFilter

EvaluatorFilter

EvaluatorFilter是封装EventEvaluator的通用过滤器。顾名思义,EventEvaluator评估给定事件是否满足给定条件。在匹配和不匹配时,主机EvaluatorFilter将分别返回由 onMatch 或 onMismatch 属性指定的值。注意,EvaluatorFilter先前已在经典的 logback(see above)上下文中进行了讨论。本文主要是先前讨论的重复。

注意EventEvaluator是一个抽象类。您可以通过对EventEvaluator进行子类化来实现自己的事件评估逻辑。 Logback-access 附带了一个名为JaninoEventEvaluator的具体实现。它采用任意 Java 语言布尔表达式作为评估标准。我们将此类 Java 语言块称为“ 评估表达式”。评估表达式为事件过滤提供了极大的灵 Active。 JaninoEventEvaluator需要Janino library。请参阅安装文档的corresponding section

评估表达式是在配置文件的解释过程中即时编译的。作为用户,您不必担心实际的管道问题。但是,您有责任确保 Java 语言表达式返回一个布尔值,即它的计算结果为 true 或 false。

评估表达式是在当前访问事件上评估的。 Logback-access 自动以变量名 event 导出当前的AccessEvent实例。您可以通过event变量读取与 HTTP 请求以及 HTTP 响应相关的各种数据。请参阅AccessEvent 类的源代码以获取确切列表。

下一个 logback-access 配置文件说明了基于404 (未找到) HTTP 响应代码的过滤。导致 404 的每个请求都将打印在控制台上。

示例:访问评估程序(logback-examples/src/main/resources/chapters/filters/accessEventEvaluator.xml)

<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 错误的请求,但那些请求 CSS 文件的请求除外。

示例 6.10:访问评估程序(logback-examples/src/main/resources/chapters/filters/accessEventEvaluator2.xml)

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