Logback: the reliable, generic, fast and flexible logging framework. Copyright (C) 1999-2015, QOS.ch. All rights reserved. This program and the accompanying materials are dual-licensed under either the terms of the Eclipse Public License v1.0 as published by the Eclipse Foundation or (per the licensee's choosing) under the terms of the GNU Lesser General Public License version 2.1 as published by the Free Software Foundation.
/** * Logback: the reliable, generic, fast and flexible logging framework. * Copyright (C) 1999-2015, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under * either the terms of the Eclipse Public License v1.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) * * under the terms of the GNU Lesser General Public License version 2.1 * as published by the Free Software Foundation. */
package ch.qos.logback.classic.turbo; import ch.qos.logback.classic.Logger; import ch.qos.logback.classic.Level; import ch.qos.logback.core.spi.FilterReply; import org.slf4j.Marker; import org.slf4j.MDC; import java.util.Map; import java.util.HashMap;
This filter allows for efficient course grained filtering based on criteria such as product name or company name that would be associated with requests as they are processed.

This filter will allow you to associate threshold levels to a key put in the MDC. This key can be any value specified by the user. Furthermore, you can pass MDC value and level threshold associations, which are then looked up to find the level threshold to apply to the current logging request. If no level threshold could be found, then a 'default' value specified by the user is applied. We call this value 'levelAssociatedWithMDCValue'.

If 'levelAssociatedWithMDCValue' is higher or equal to the level of the current logger request, the decide() method returns the value of onHigherOrEqual, if it is lower then the value of onLower is returned. Both 'onHigherOrEqual' and 'onLower' can be set by the user. By default, 'onHigherOrEqual' is set to NEUTRAL and 'onLower' is set to DENY. Thus, if the current logger request's level is lower than 'levelAssociatedWithMDCValue', then the request is denied, and if it is higher or equal, then this filter decides NEUTRAL letting subsequent filters to make the decision on the fate of the logging request.

The example below illustrates how logging could be enabled for only individual users. In this example all events for logger names matching "com.mycompany" will be logged if they are for 'user1' and at a level higher than equals to DEBUG, and for 'user2' if they are at a level higher than or equal to TRACE, and for other users only if they are at level ERROR or higher. Events issued by loggers other than "com.mycompany" will only be logged if they are at level ERROR or higher since that is all the root logger allows.

<configuration>
  <appender name="STDOUT"
            class="ch.qos.logback.core.ConsoleAppender">
    <layout class="ch.qos.logback.classic.PatternLayout">
      <Pattern>TEST %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</Pattern>
    </layout>
  </appender>
  
  <turboFilter class="ch.qos.logback.classic.turbo.DynamicThresholdFilter">
    <Key>userId</Key>
    <DefaultThreshold>ERROR</DefaultThreshold>
    <MDCValueLevelPair>
      <value>user1</value>
      <level>DEBUG</level>
    </MDCValueLevelPair>
    <MDCValueLevelPair>
      <value>user2</value>
      <level>TRACE</level>
    </MDCValueLevelPair>
  </turboFilter>
  
  <logger name="com.mycompany" level="TRACE"/>
  
  <root level="ERROR" >
    <appender-ref ref="STDOUT" />
  </root>
</configuration>
In the next configuration events from user1 and user2 will be logged regardless of the logger levels. Events for other users and records without a userid in the MDC will be logged if they are ERROR level messages. With this configuration, the root level is never checked since DynamicThresholdFilter will either accept or deny all records.
<configuration>
  <appender name="STDOUT"
            class="ch.qos.logback.core.ConsoleAppender">
    <layout class="ch.qos.logback.classic.PatternLayout">
       <Pattern>TEST %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</Pattern>
    </layout>
  </appender>
  
  <turboFilter class="ch.qos.logback.classic.turbo.DynamicThresholdFilter">
    <Key>userId</Key>
    <DefaultThreshold>ERROR</DefaultThreshold>
    <OnHigherOrEqual>ACCEPT</OnHigherOrEqual>
    <OnLower>DENY</OnLower>
    <MDCValueLevelPair>
      <value>user1</value>
      <level>TRACE</level>
    </MDCValueLevelPair>
    <MDCValueLevelPair>
      <value>user2</value>
      <level>TRACE</level>
    </MDCValueLevelPair>
  </turboFilter>
  
  <root level="DEBUG" >
    <appender-ref ref="STDOUT" />
  </root>
</configuration>
Author:Ralph Goers, Ceki Gülcü
/** * This filter allows for efficient course grained filtering based on criteria * such as product name or company name that would be associated with requests * as they are processed. * * <p> This filter will allow you to associate threshold levels to a key put in * the MDC. This key can be any value specified by the user. Furthermore, you * can pass MDC value and level threshold associations, which are then looked up * to find the level threshold to apply to the current logging request. If no * level threshold could be found, then a 'default' value specified by the user * is applied. We call this value 'levelAssociatedWithMDCValue'. * * <p> If 'levelAssociatedWithMDCValue' is higher or equal to the level of the * current logger request, the * {@link #decide(Marker, Logger, Level, String, Object[], Throwable) decide()} * method returns the value of {@link #getOnHigherOrEqual() onHigherOrEqual}, * if it is lower then the value of {@link #getOnLower() onLower} is returned. * Both 'onHigherOrEqual' and 'onLower' can be set by the user. By default, * 'onHigherOrEqual' is set to NEUTRAL and 'onLower' is set to DENY. Thus, if * the current logger request's level is lower than * 'levelAssociatedWithMDCValue', then the request is denied, and if it is * higher or equal, then this filter decides NEUTRAL letting subsequent filters * to make the decision on the fate of the logging request. * * <p> The example below illustrates how logging could be enabled for only * individual users. In this example all events for logger names matching * "com.mycompany" will be logged if they are for 'user1' and at a level higher * than equals to DEBUG, and for 'user2' if they are at a level higher than or * equal to TRACE, and for other users only if they are at level ERROR or * higher. Events issued by loggers other than "com.mycompany" will only be * logged if they are at level ERROR or higher since that is all the root logger * allows. * * <pre> * &lt;configuration&gt; * &lt;appender name="STDOUT" * class="ch.qos.logback.core.ConsoleAppender"&gt; * &lt;layout class="ch.qos.logback.classic.PatternLayout"&gt; * &lt;Pattern>TEST %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n&lt;/Pattern> * &lt;/layout&gt; * &lt;/appender&gt; * * &lt;turboFilter class=&quot;ch.qos.logback.classic.turbo.DynamicThresholdFilter&quot;&gt; * &lt;Key&gt;userId&lt;/Key&gt; * &lt;DefaultThreshold&gt;ERROR&lt;/DefaultThreshold&gt; * &lt;MDCValueLevelPair&gt; * &lt;value&gt;user1&lt;/value&gt; * &lt;level&gt;DEBUG&lt;/level&gt; * &lt;/MDCValueLevelPair&gt; * &lt;MDCValueLevelPair&gt; * &lt;value&gt;user2&lt;/value&gt; * &lt;level&gt;TRACE&lt;/level&gt; * &lt;/MDCValueLevelPair&gt; * &lt;/turboFilter&gt; * * &lt;logger name="com.mycompany" level="TRACE"/&gt; * * &lt;root level="ERROR" &gt; * &lt;appender-ref ref="STDOUT" /&gt; * &lt;/root&gt; * &lt;/configuration&gt; * </pre> * * In the next configuration events from user1 and user2 will be logged * regardless of the logger levels. Events for other users and records without a * userid in the MDC will be logged if they are ERROR level messages. With this * configuration, the root level is never checked since DynamicThresholdFilter * will either accept or deny all records. * * <pre> * &lt;configuration&gt; * &lt;appender name="STDOUT" * class="ch.qos.logback.core.ConsoleAppender"&gt; * &lt;layout class="ch.qos.logback.classic.PatternLayout"&gt; * &lt;Pattern>TEST %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n&lt;/Pattern> * &lt;/layout&gt; * &lt;/appender&gt; * * &lt;turboFilter class=&quot;ch.qos.logback.classic.turbo.DynamicThresholdFilter&quot;&gt; * &lt;Key&gt;userId&lt;/Key&gt; * &lt;DefaultThreshold&gt;ERROR&lt;/DefaultThreshold&gt; * &lt;OnHigherOrEqual&gt;ACCEPT&lt;/OnHigherOrEqual&gt; * &lt;OnLower&gt;DENY&lt;/OnLower&gt; * &lt;MDCValueLevelPair&gt; * &lt;value&gt;user1&lt;/value&gt; * &lt;level&gt;TRACE&lt;/level&gt; * &lt;/MDCValueLevelPair&gt; * &lt;MDCValueLevelPair&gt; * &lt;value&gt;user2&lt;/value&gt; * &lt;level&gt;TRACE&lt;/level&gt; * &lt;/MDCValueLevelPair&gt; * &lt;/turboFilter&gt; * * &lt;root level="DEBUG" &gt; * &lt;appender-ref ref="STDOUT" /&gt; * &lt;/root&gt; * &lt;/configuration&gt; * </pre> * * @author Ralph Goers * @author Ceki G&uuml;lc&uuml; */
public class DynamicThresholdFilter extends TurboFilter { private Map<String, Level> valueLevelMap = new HashMap<String, Level>(); private Level defaultThreshold = Level.ERROR; private String key; private FilterReply onHigherOrEqual = FilterReply.NEUTRAL; private FilterReply onLower = FilterReply.DENY;
Get the MDC key whose value will be used as a level threshold
Returns:the name of the MDC key.
/** * Get the MDC key whose value will be used as a level threshold * * @return the name of the MDC key. */
public String getKey() { return this.key; }
See Also:
  • setKey
/** * @see setKey */
public void setKey(String key) { this.key = key; }
Get the default threshold value when the MDC key is not set.
Returns:the default threshold value in the absence of a set MDC key
/** * Get the default threshold value when the MDC key is not set. * * @return the default threshold value in the absence of a set MDC key */
public Level getDefaultThreshold() { return defaultThreshold; } public void setDefaultThreshold(Level defaultThreshold) { this.defaultThreshold = defaultThreshold; }
Get the FilterReply when the effective level is higher or equal to the level of current logging request
Returns:FilterReply
/** * Get the FilterReply when the effective level is higher or equal to the * level of current logging request * * @return FilterReply */
public FilterReply getOnHigherOrEqual() { return onHigherOrEqual; } public void setOnHigherOrEqual(FilterReply onHigherOrEqual) { this.onHigherOrEqual = onHigherOrEqual; }
Get the FilterReply when the effective level is lower than the level of current logging request
Returns:FilterReply
/** * Get the FilterReply when the effective level is lower than the level of * current logging request * * @return FilterReply */
public FilterReply getOnLower() { return onLower; } public void setOnLower(FilterReply onLower) { this.onLower = onLower; }
Add a new MDCValuePair
/** * Add a new MDCValuePair */
public void addMDCValueLevelPair(MDCValueLevelPair mdcValueLevelPair) { if (valueLevelMap.containsKey(mdcValueLevelPair.getValue())) { addError(mdcValueLevelPair.getValue() + " has been already set"); } else { valueLevelMap.put(mdcValueLevelPair.getValue(), mdcValueLevelPair.getLevel()); } } /** * */ @Override public void start() { if (this.key == null) { addError("No key name was specified"); } super.start(); }
This method first finds the MDC value for 'key'. It then finds the level threshold associated with this MDC value from the list of MDCValueLevelPair passed to this filter. This value is stored in a variable called 'levelAssociatedWithMDCValue'. If it null, then it is set to the
Params:
  • marker –
  • logger –
  • level –
  • s –
  • objects –
  • throwable –
@{link#defaultThreshold} value. If no such value exists, then
Returns:FilterReply - this filter's decision
/** * This method first finds the MDC value for 'key'. It then finds the level * threshold associated with this MDC value from the list of MDCValueLevelPair * passed to this filter. This value is stored in a variable called * 'levelAssociatedWithMDCValue'. If it null, then it is set to the * * @{link #defaultThreshold} value. * * If no such value exists, then * * * @param marker * @param logger * @param level * @param s * @param objects * @param throwable * * @return FilterReply - this filter's decision */
@Override public FilterReply decide(Marker marker, Logger logger, Level level, String s, Object[] objects, Throwable throwable) { String mdcValue = MDC.get(this.key); if (!isStarted()) { return FilterReply.NEUTRAL; } Level levelAssociatedWithMDCValue = null; if (mdcValue != null) { levelAssociatedWithMDCValue = valueLevelMap.get(mdcValue); } if (levelAssociatedWithMDCValue == null) { levelAssociatedWithMDCValue = defaultThreshold; } if (level.isGreaterOrEqual(levelAssociatedWithMDCValue)) { return onHigherOrEqual; } else { return onLower; } } }