I'm using slf4j with either log4j 2.0 or logback as the implementation. For example, my servlet has a logger with level ERROR, and my server spawns 100 threads of the servlet. I will get a list of special users at runtime. When I detect some of the special users connected in. I want to change the log level for those special users/threads to DEBUG, and leave other threads' log level unaffected (still ERROR).
I know the TurboFilter in logback and DynamicThresholdFilter in log4j 2.0, but since I will only get the special users list at runtime, I cannot use them.
Here is my application:
package com.example.logging;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.http.HttpServlet;
import org.slf4j.*;
public class App extends HttpServlet {
private final Logger Logger = LoggerFactory.getLogger(App.class);
Map<String, String> map = new HashMap<String, String>();
public App() {
map.put("user1", "DEBUG");
map.put("user2", "DEBUG");
map.put("user3", "ERROR");
}
public void writeToLogFile(String userName) {
if (map.containsKey(userName)) {
// do something so that I can change the logger to the corresponding log level
}
Logger.error(userName + " error message");
// the logger is of level ERROR, so by default, this log event will not happen
// but I want it to happen for special users
if (Logger.isDebugEnabled()) {
Logger.debug(userName + " debug message");
}
}
}
Here is my log configuration in log4j2.xml
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="ERROR">
<Appenders>
<Console name="Console" target="SYSTEM_OUT">
<PatternLayout pattern="%-5level %class{36} %M %msg%xEx%n" />
</Console>
</Appenders>
<Loggers>
<Logger name="com.example.logging.App" level="ERROR" additivity="false">
<AppenderRef ref="Console" />
</Logger>
<Root level="DEBUG">
<AppenderRef ref="Console" />
</Root>
</Loggers>
</Configuration>
If I call the methods below:
App myApp = new App();
// assume the below 4 methods are called concurrently
myApp.writeToLogFile("user1");
myApp.writeToLogFile("user2");
myApp.writeToLogFile("user3");
myApp.writeToLogFile("user4");
The expected output should be:
ERROR com.example.logging.App writeToLogFile - user1 error message
DEBUG com.example.logging.App writeToLogFile - user1 debug message
ERROR com.example.logging.App writeToLogFile - user2 error message
DEBUG com.example.logging.App writeToLogFile - user2 debug message
ERROR com.example.logging.App writeToLogFile - user3 error message
ERROR com.example.logging.App writeToLogFile - user4 error message
I've met the same problem, and I end up using my own filter by making changes to DynamicThresholdFilter
Changes to the application:
Here is the newly defined filter based on DynamicThresholdFilter, let's call it DynamicThresholdUserFilter, you can compare it to the source code of DynamicThresholdFilter
Add the DynamicThresholdUserFilter and package name to your configuration file
The newly defined filter is pretty similar to DynamicThresholdFilter. The difference is DynamicThresholdFilter uses the predefined level in configuration file as the dynamic threshold, while this filter uses the level programmatically defined in the map.
While the already existing answer might work (haven't tried it personally), after intensive searching, I found a very easy and neat trick to do what you are requesting.
The DynamicThresholdFilter can be used with conditions to switch the log level at run time. This, combined with log4j2's ThreadContext, you can do quite nifty things.
You would have to populate a particular key in the ThreadContext at the beginning of a server call processing (somewhere in
doFilter
method of yourHttpServlet
class) based on your custom logic of user names. This would look something like:Then in your log4j2.xml file, you put this as a global filter, right below the root
Configuration
tag:Now based on the value of the key
customLogLevel
in theThreadContext
that you set at the beginning of a call, all the log calls in that thread will have log level corresponding to the matchingKeyValuePair
line. So in the example above, all log calls in the thread would have level asDEBUG
.