I have a Struts application with log4j to display information about application.
The pattern to format log\'s output is as follows:
log4j.appender.RA
Extend PatternLayout
as below, and then specify MyPatternLayout
with $X{threadId}
in the format string.
This implementation uses ThreadLocal
to minimize the performance impact of calculating the Thread ID:
MyPatternLayout extends PatternLayout {
private final ThreadLocal<String> threadId = new ThreadLocal<String>() {
@Override
protected String initialValue() {
String t = Long.toString(Thread.currentThread().getId());
MDC.put("threadId", t);
return t;
}
};
@Override
public String format(LoggingEvent event) {
this.threadId.get();
return super.format(event);
}
}
Another elegant solution with log4j2 is to use org.apache.logging.log4j.core.pattern.LogEventPatternConverter
.
You can write a class like this
@Plugin(name = "ThreadIdConverter", category = "Converter")
@ConverterKeys({ "tid" })
public class ThreadIdConverter extends LogEventPatternConverter {
protected ThreadIdConverter(String name, String style) {
super(name, style);
}
@Override
public void format(LogEvent event, StringBuilder toAppendTo) {
toAppendTo.append(getThreadId());
}
protected String getThreadId() {
long id = Thread.currentThread().getId();
return Long.toHexString(id);
}
public static ThreadIdConverter newInstance(String[] options) {
return new ThreadIdConverter("tid", "tid");
}
}
In this way you are creating a new pattern tid
and you can use it when you define your appender's layout
<Appenders>
<Console name="console" target="SYSTEM_OUT">
<PatternLayout>
<Pattern>%d{dd-MMM HH:mm:ss.SSS} %-7level [%5tid] %logger - %message%n</Pattern>
</PatternLayout>
</Console>
</Appenders>
The last important thing to remember is how to activate your log4j2 plugin. To do it you have to add the package that contains your plugins in the log4j2 configuration file using the package
attribute on Configuration
node
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE Configuration>
<Configuration status="warn"
packages="my.package.logging.plugins">
<Appenders>
<Console name="console" target="SYSTEM_OUT">
<PatternLayout>
<Pattern>%d{dd-MMM HH:mm:ss.SSS} %-7level [%5tid] %logger - %message%n</Pattern>
</PatternLayout>
</Console>
</Appenders>
<Loggers>
<Root level="warn">
<AppenderRef ref="console" />
</Root>
<Logger name="my.package" level="trace" />
</Loggers>
</Configuration>
I implemented thread ID and thread priority for the upcoming 2.6. Tracking here: https://issues.apache.org/jira/browse/LOG4J2-1299
You can pick up a 2.6-SNAPSHOT build from the Apache snapshots repository: https://repository.apache.org/content/repositories/snapshots/
One possible solution is to create your own class which sits between your code and Log4J and appends the thread ID to every log message:
public class ThreadLogger
{
// Constructor declared private to prevent instantiation. Use static methods instead.
private ThreadLogger() {}
private static enum LogLevel
{
TRACE,
DEBUG,
INFO,
WARN,
ERROR
}
public static void trace(String message)
{
logMessage(message, LogLevel.ERROR);
}
public static void debug(String message)
{
logMessage(message, LogLevel.ERROR);
}
public static void info(String message)
{
logMessage(message, LogLevel.ERROR);
}
public static void warn(String message)
{
logMessage(message, LogLevel.WARN);
}
public static void error(String message)
{
logMessage(message, LogLevel.ERROR);
}
private static void logMessage(String message, LogLevel logLevel)
{
// Get the Log4J logger for the class that originally wanted to log the message
String callingClassName = Thread.currentThread().getStackTrace()[3].getClassName();
Class callingClass;
try
{
callingClass = Class.forName(callingClassName);
}
catch(ClassNotFoundException e)
{
String errorMessage = String.format("Could not reference class [%s]. Unable to log call!", callingClassName);
throw new RuntimeException(errorMessage);
}
Logger logger = Logger.getLogger(callingClass);
// Get the thread ID and place it in front of the logged message
long threadId = Thread.currentThread().getId();
String formattedMessage = String.format("[%s] %s", threadId, message);
// Log the message
switch(logLevel)
{
case TRACE:
logger.trace(formattedMessage);
break;
case DEBUG:
logger.debug(formattedMessage);
break;
case INFO:
logger.info(formattedMessage);
break;
case WARN:
logger.warn(formattedMessage);
break;
case ERROR:
logger.error(formattedMessage);
break;
}
}
}
Downsides:
1234 [main] INFO com.foo.bar.Baz - [1] Hello world on thread #1!
1234 [main] INFO com.foo.bar.Baz - [2] Hello world on thread #2!