Fallback appender for Logback's SiftingAppender

958 views Asked by At

In the case of a SiftingAppender setup, is there a way to reference a fallback appender when the MDC key is set to the default value?

For instance, I want to use a file appender when the MDC key is set to a value that is different than the default, but log to the console when the MDC key is set to the default value.

The only solution I see right now is to subclass the SiftingAppender class and override the append(E event) logic to fallback to a default appender if the MDC key is set to the default value.

1

There are 1 answers

1
glytching On

You can filter your SiftingAppender and your ConsoleAppender with mutually exclusive conditions by using Logback filters.

Example filter declarations:

  • Using Groovy to express the 'contains default MDC key' condition:

    <filter class="ch.qos.logback.core.filter.EvaluatorFilter">
        <!-- GEventEvaluator requires Groovy -->
         <evaluator
            class="ch.qos.logback.classic.boolex.GEventEvaluator">
           <expression>
             mdc.get("servicekey") == null
           </expression>
         </evaluator>
         <OnMismatch>NEUTRAL</OnMismatch>
         <OnMatch>DENY</OnMatch>
    </filter>
    
  • Using Janino to express the 'contains default MDC key' condition:

    <filter class="ch.qos.logback.core.filter.EvaluatorFilter">
         <!-- JaninoEventEvaluator requires Janino -->
         <evaluator
           class="ch.qos.logback.classic.boolex.JaninoEventEvaluator">
           <expression>
             mdc.get("yourMdcKey") == null
           </expression>
        </evaluator>
        <OnMismatch>NEUTRAL</OnMismatch>
        <OnMatch>DENY</OnMatch>
    </filter>  
    

Assuming you choose the Janino filter then your appenders would be declared as follows::

<appender name="FILE" class="ch.qos.logback.core.FileAppender">
    <filter class="ch.qos.logback.core.filter.EvaluatorFilter">
         <evaluator class="ch.qos.logback.classic.boolex.JaninoEventEvaluator">
           <expression>
             mdc.get("yourMdcKey") == null
           </expression>
        </evaluator>
        <OnMismatch>NEUTRAL</OnMismatch>
        <OnMatch>DENY</OnMatch>
    </filter> 

    ...

</appender> 

<appender name="SIFTER" class="ch.qos.logback.classic.sift.SiftingAppender">
    <filter class="ch.qos.logback.core.filter.EvaluatorFilter">
         <evaluator class="ch.qos.logback.classic.boolex.JaninoEventEvaluator">
           <expression>
             mdc.get("yourMdcKey") == null
           </expression>
        </evaluator>
        <OnMismatch>NEUTRAL</OnMismatch>
        <OnMatch>DENY</OnMatch>
    </filter> 

    ...

</appender> 

Note: in the above examples I have used mdc.get("yourMdcKey") == null / mdc.get("yourMdcKey") != null to express this conditional: is the MDC key set to the default value?. It's likely that your default value is not null but perhaps some populated value, if so then you would alter the conditional statement accordingly.

This approach allows you to declare a gate in front of your appenders and this gate applies the following test:

  • For FileAppender if the MDC key is not populated then ignore the event otherwise accept the event
  • For SiftingAppender if the MDC key is not populated then ignore the event otherwise accept the event