## Chapter 6: Filters

Authors: Ceki Gülcü, Sébastien Pennec

| Copyright © 2000-2006, QOS.ch This work is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 2.5 License .

As we have seen, logback has several built-in ways for filtering log requests, including the context-wide filter, logger-level selection rule and appender filters. These provide high performance filtering for the most commonly encountered cases. These filters are largely inspired from Linux ipchains or iptables as they are called in more recent Linux kernels. Logback filters are based on ternary logic allowing them to be assembled or chained together to compose an arbitrarily complex filtering policy.

There are two main types of filters, namely `Filter` and `TurboFilter`.

## Logback Classic

`Filter` objects all implement the `Filter` abscract class. The `decide(Object event)` method is passed a newly created `LoggingEvent` object.

### Filter chains

This abstract class assumes that filters be organized in a linear chain. Its member field next points to the next filter in the chain, or `null` if there are no further filters in the chain. Figure 6.1 depicts a sample filter chain consisting of three filters.

A sample filter chain

Filters are based on ternary logic. The `decide(Object event)` method of each filter is called in sequence. This method returns one of the enumerations `FilterReply.DENY`, `FilterReply.NEUTRAL` or `FilterReply.ACCEPT`. If the returned value is `FilterReply.DENY`, then the log event is dropped immediately without consulting the remaining filters. If the value returned is `FilterReply.NEUTRAL`, then the next filter in the chain is consulted. If there are no further filters to consult, then the logging event is processed normally. If the returned value is `FilterReply.ACCEPT`, then the logging event is processed immediately skipping the remaining filters.

In logback, `Filter` objects can only be added to `Appender` instances. By adding filters to an appender you can filter events by various criteria, such as the contents of the log message, the contents of the MDC, the time of day or any other part of the logging event.

The criteria can be specified using an expression evaluator. The `EvaluatorFilter` class uses an `EventEvaluator` to decide wether to accept or deny the request. This allows unprecedented flexibility in the way that you can affect the logging event's filtering.

### Event Evaluators

Events evaluators allow the user to enter java expressions, using components of a logging event, and to check each logging event against the compiled expression.

Let's see a sample configuration.

Example 6.1: Basic event evaluator usage (logback-examples/src/main/java/chapter6/basicEventEvaluator.xml)

    <configuration>

      <appender name="STDOUT"
        class="ch.qos.logback.core.ConsoleAppender">
        <filter class="ch.qos.logback.core.filter.EvaluatorFilter">
          <evaluator name="myEval">
            <expression>message.contains("important")</expression>
          </evaluator>
          <OnMismatch>NEUTRAL</OnMismatch>
          <OnMatch>ACCEPT</OnMatch>
        </filter>
        <layout class="ch.qos.logback.classic.PatternLayout">
          <pattern>
            %-4relative [%thread] %-5level %logger - %msg%n
          </pattern>
        </layout>
      </appender>

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

The bold part in the previous configuration adds an `EvaluatorFilter` to a `ConsoleAppender`. An `EventEvaluator` is then given to the filter. The expression element contains a recognizable java expression. Notice that the message variable is not defined anywhere. Logback provides access to the internal components of a logging event and lets the user build her expression at will.

The variables available to the `EventEvaluator` are descripbed below:

Name | Type | Description
--- | --- | ---
event | `LoggingEvent` | The newly created logging event.
message | `String` | The message created with the logging request.
logger | `LoggerRemoteView` | This object can be treated like a usual logger. In case the logging event is serialized and sent to a remote machine, the usual logger object is dropped and replaced by a `LoggerRemoteView` object, which performs much better over the wire.
level | `int` | The int value corresponding to the level. To help create easily expressions involving levels, the default value DEBUG, INFO, WARN and ERROR are also available. Thus, using level > INFO is a correct expression.
timeStamp | `long` | The timestamp corresponding to the logging event's creation.
marker | `Marker` | The `Marker` object associated with the logging request.
mdc | `Map` | A map containing all the MDC values at the time of the creation of the logging event. A value can be access by using the following expression: mdc.get("myKey").
throwable | `Throwable` | The exception that was passed to the logger when it was requested.

The behaviour of the filter is also defined by its OnMatch and OnMismatch options. The configuration specifies thanks to these elements the replies that the `EvaluatorFilter` must give once its expression has been evaluated. The example above returns a positive reply when the message of the logging event contains the String important. If this String is not found in the message, then the filter lets the next filter evaluate whether this logging event should be accepted, or rejected.

### TurboFilters

`TurboFilter` objects all extend the `TurboFilter` abstract class. Like the usual filters, they use ternary logic to return their evaluation of the logging event.

Overall, they work much like the previously mentionned filters. However, there are two main differences between `Filter` and `TurboFilter` objects.

`TurboFilter` objects are tied to the logging context. Hence, they are called not only when a given appender is used, but each and every time a logging request is issued. Their scope is wider than appender-attached filters.

They are called before the `LoggingEvent` object creation. Their decision is made based on some of the logging event's components. They require no logging event instanciation, nor any other treatement to provide their filtering functionnalities. They are much more performant than the usual `Filter` objects.

Logback classic ships with several `TurboFilter` classes ready for use. The `MDCFilter` check the presence of a given value in the MDC. On the other hand, `MarkerFilter` checks for the presence of a specific marker attached to the logging request.

Here is a sample configuration, using both `MDCFilter` and `MarkerFilter`.

Example 6.2: `MDCFilter` and `MarkerFilter` configuration (logback-examples/src/main/java/chapter6/turboFilters.xml)

    <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">
        <layout class="ch.qos.logback.classic.PatternLayout">
          <Pattern>%date [%thread] %-5level %logger - %msg%n</Pattern>
      </layout>
      </appender>

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

You can see this configuration in action by issuing the following command:

    java chapter6.FilterEvents src/java/main/chapter6/turboFilters.xml

The `FilterEvents` class creates 10 logging requests, each with its number from 0 to 9. All of the requests are of level INFO, just like the configured overall level, except for two requests. The 3rd request, is a DEBUG level corresponding to the key username. This obviously satisfies the first `TurboFilter` declared in the previous configuration file. The 6th request is a ERROR level request, which is issued along with the billing marker, to match the requests for the second declared `TurboFilter`.

Here is the output of the previous command:

    2006-12-04 15:17:22,859 [main] INFO  chapter6.FilterEvents - logging statement 0
    2006-12-04 15:17:22,875 [main] INFO  chapter6.FilterEvents - logging statement 1
    2006-12-04 15:17:22,875 [main] INFO  chapter6.FilterEvents - logging statement 2
    2006-12-04 15:17:22,875 [main] DEBUG chapter6.FilterEvents - logging statement 3
    2006-12-04 15:17:22,875 [main] INFO  chapter6.FilterEvents - logging statement 4
    2006-12-04 15:17:22,875 [main] INFO  chapter6.FilterEvents - logging statement 5
    2006-12-04 15:17:22,875 [main] INFO  chapter6.FilterEvents - logging statement 7
    2006-12-04 15:17:22,875 [main] INFO  chapter6.FilterEvents - logging statement 8
    2006-12-04 15:17:22,875 [main] INFO  chapter6.FilterEvents - logging statement 9

One can see that the 3rd request, who should not be displayed if we only followed the overall INFO level, appears anyway, because it matched the first `TurboFilter` requirements and was accepted.

On the other hand, the 6th request, that is a ERROR level request should have been displayed. But it satisfied the second `TurboFilter` whose OnMatch option is set to DENY. Thus, the 6th request was not displayed.

## Logback Access

Logback access benefits from most of the possibilities available to the classic module. `Filter` objects are available and work in the same way as their classic counterpart. They handle access' implementation of logging events: `AccessEvent`. On the other hand, `TurboFilter` objects are not available to the access module.

### Filters

`EvaluatorFilter` objects, with their expressions, are available to the access module. However, the variables that one can use to build an expression are different. Only the `AccessEvent` object can be used, by inserting the event variable in the expression. Although less wide than its classic counterpart, the access evaluation filter is just as powerfull. All the request and response components are reachable from the event variable.

Here is a sample configuration that will ensure that any 404 error will be displayed:

Example 6.3: Access Evaluator (logback-examples/src/main/java/chapter6/accessEventEvaluator.xml)

    <configuration>

      <appender name="STDOUT"
        class="ch.qos.logback.core.ConsoleAppender">
        <filter class="ch.qos.logback.core.filter.EvaluatorFilter">
          <evaluator name="myEval">
            <expression>event.getStatusCode() == 404</expression>
          </evaluator>
          <OnMismatch>NEUTRAL</OnMismatch>
          <OnMatch>ACCEPT</OnMatch>
        </filter>
        <layout class="ch.qos.logback.access.PatternLayout">
          <pattern>
            %h %l %u %t %r %s %b
          </pattern>
        </layout>
      </appender>

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

We might imagine a slightly more complex use of filters to ensure the display of 404 errors, but to prevent polluting the output with endless accesses to CSS files. Here is what such a configuration would look like:

Example 6.4: Access Evaluator (logback-examples/src/main/java/chapter6/accessEventEvaluator2.xml)

    <configuration>

      <appender name="STDOUT"
        class="ch.qos.logback.core.ConsoleAppender">
        <filter class="ch.qos.logback.core.filter.EvaluatorFilter">
          <evaluator name="Eval404">
            <expression>event.getStatusCode() == 404</expression>
          </evaluator>
          <OnMismatch>NEUTRAL</OnMismatch>
          <OnMatch>ACCEPT</OnMatch>
        </filter>
        <filter class="ch.qos.logback.core.filter.EvaluatorFilter">
          <evaluator name="EvalCSS">
            <expression>event.getRequestURI().contains("css")</expression>
          </evaluator>
          <OnMismatch>NEUTRAL</OnMismatch>
          <OnMatch>DENY</OnMatch>
        </filter>
        <layout class="ch.qos.logback.access.PatternLayout">
          <pattern>
            %h %l %u %t %r %s %b
          </pattern>
        </layout>
      </appender>

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