Skip to content
Advertisement

How to change log level only for current thread with Logback

In Logback, the log level of a logger can be changed with setLevel() method. But in Logback, because the loggers are singleton, the call of setLevel() method will affect to all other threads that use same logger.

Now I have a class used in a web application like this:

class FooService {
    private void insertRecord(Foo foo) {
        // insert one record to DB
    }

    public void insertOne(Foo foo) {
        insertRecord(foo);
    }

    public void insertMany(List<Foo> foos) {
        // I want to stop logging here
        for (Foo foo: foos) {
            insertRecord(foo);
        }
        // I want to resume logging here
    }
}

And in Spring ApplicationConfig.xml:

<bean id="traceAdvice"
  class="org.springframework.aop.interceptor.CustomizableTraceInterceptor">
  <property name="enterMessage" 
    value="Entering $[targetClassShortName].$[methodName]($[argumentTypes])[$[arguments]]" />
  <property name="exitMessage" 
    value="Exiting  $[targetClassShortName].$[methodName] with return value $[returnValue], took $[invocationTime]ms" />
  <property name="exceptionMessage" 
    value="Exception thrown in $[targetClassShortName].$[methodName] : $[exception]" />
</bean>

<aop:config>
  <aop:pointcut id="pointcut-service" 
    expression="execution(* my.app.service..*Service.*(..))" />
  <aop:advisor advice-ref="traceAdvice" pointcut-ref="pointcut-service" />
</aop:config>

I want to log call of insertRecord from insertOne method. On the other hand, in insertMany method, I want to stop logging just before the loop (because it might output enormous amount of logs), and resume logging just after the loop. But if you call setLevel() just before the loop, the change of log level will affect to the other logger used in other threads. In this situation, I think you will get decifient logs on the other threads.

My question is: How to change the log level only for the logger used in current thread?

Advertisement

Answer

I found a solution. You can use MDC and TurboFilter for this purpose. MDC is thread-local and change to MDC does not affect to other threads.

For example, if you want to stop all the logging activity, you have to add a definition of MDCFilter to logback.xml (Note that <turboFilter> tag cannot be a child of an <appender> tag, and should be a child of <configuration> tag):

<configuration>
    <turboFilter class="ch.qos.logback.classic.turbo.MDCFilter">
      <MDCKey>tracing</MDCKey>
      <Value>off</Value>
      <OnMatch>DENY</OnMatch>
    </turboFilter>
    ......
</configuration>

And you can on/off logging by put/remove a key and a value to MDC like this (Note that you should consider about exception in actual usage):

class FooService {
    private void insertRecord(Foo foo) {
        // insert one record to DB
    }

    public void insertOne(Foo foo) {
        insertRecord(foo);
    }

    public void insertMany(List<Foo> foos) {
        // I want to stop logging here
        MDC.put("tracing", "off");
        for (Foo foo: foos) {
            insertRecord(foo);
        }
        // I want to resume logging here
        MDC.remove("tracing");
    }
}

Or, if you want to stop TRACE/DEBUG/INFO/WARN log but leave ERROR log active, you can use DynamicThresholdFilter:

<configuration>
    <turboFilter class="ch.qos.logback.classic.turbo.DynamicThresholdFilter">
      <Key>tracing</Key>
      <DefaultThreshold>TRACE</DefaultThreshold>
      <MDCValueLevelPair>
        <value>off</value>
        <level>ERROR</level>
      </MDCValueLevelPair>
    </turboFilter>
    ......
</configuration>
User contributions licensed under: CC BY-SA
3 People found this is helpful
Advertisement