I have a multithreaded Java application, each thread being a class extending a base class extending Thread. One of the threads occasionally gives a method in the base class a large amount of machine-generated data, while the others only give small trickles of human-typed data. I want to log those human-typed messages on a higher loglevel than the machine data, but as the base class is part of all threads I cannot distinguish that in the code.
One solution is to inform the base class from the extended classes to log on a different level, but then I'd have to hardcode that knowledge into the application, which is ugly.
What I would like to do is "steer" this through my logback.xml config.
I wrote a small reproducer:
package x.y.z;
import org.slf4j.*;
public class Quickie {
static final Logger LOG = LoggerFactory.getLogger(Quickie.class);
public static void main(String[] args) throws Exception {
MyThread t1 = new MyThread("hi");
MyThread t2 = new MyThread("bye");
t1.start(); t2.start();
}
}
class MyThread extends Thread {
static final Logger LOG = LoggerFactory.getLogger(MyThread.class);
public MyThread(final String name) { this.setName(name); }
public void run() { logSomething(); }
public void logSomething() {
LOG.trace(getName()); LOG.error(getName());
}
}
This is the logback config:
<?xml version="1.0" encoding="UTF-8"?>
<configuration scan="true">
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%date{HH:mm:ss} %-6level %-10([%thread]) %logger{1}.%method:%line %message%n</pattern>
</encoder>
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>TRACE</level>
</filter>
</appender>
<root level="TRACE">
<appender-ref ref="STDOUT" />
</root>
</configuration>
With most importantly, the encoder pattern being:
<pattern>%date{HH:mm:ss} %-6level %-10([%thread]) %logger{1}.%method:%line %message%n</pattern>
As this shows the thread clearly:
11:16:09 TRACE [bye] c.t.k.t.MyThread.logSomething:24 bye
11:16:09 TRACE [hi] c.t.k.t.MyThread.logSomething:24 hi
11:16:09 ERROR [bye] c.t.k.t.MyThread.logSomething:24 bye
11:16:09 ERROR [hi] c.t.k.t.MyThread.logSomething:24 hi
... I would say there should be a way to configure my logback.xml have the "bye" thread only log on info or higher, while the "hi" thread logs from trace or higher? I spend half a day googling and reading logback documentation and I tried sifters and regex filters and lots of other examples, but they all just go on the message, apparently. Meaning that one solution could be to have a magical string appear in the messages and filter that out, but that too would be ugly.
So, is there a way to steer log levels per thread from within the log configuration without having to inject "specialized code" into the application?
I think you can use MDC and TurboFilter for this purpose.
Add a call of MDC.put()
in run()
method like this:
public void run() {
MDC.put("threadname", this.getName());
logSomething();
}
If you want to the "bye" thread only log on info or higher and the "hi" thread logs from trace or higher, you have to add a definition of DynamicThresholdFilter to logback.xml
:
<configuration>
<turboFilter class="ch.qos.logback.classic.turbo.DynamicThresholdFilter">
<Key>threadname</Key>
<!-- You can set default threshold as you like -->
<DefaultThreshold>TRACE</DefaultThreshold>
<MDCValueLevelPair>
<value>bye</value>
<level>info</level>
</MDCValueLevelPair>
<MDCValueLevelPair>
<value>hi</value>
<level>trace</level>
</MDCValueLevelPair>
</turboFilter>
......
</configuration>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With