Passing arbitrary object to ThreadContext.put() in Log4j

2k views Asked by At

I want to pass an arbitrary object to the ThreadContext.put() method like this:

MyObject originalMessage = new MyObject(...);
ThreadContext.put("originalMessage", originalMessage);

But that method only allows String as its second argument: docs here.

Is there a way to put an arbitrary object in log4j in Log4j's MDC? Or an alternative to this? What I want is to allow further executions of log.error() to automatically log the originalMessage without need of passing it as a parameter everywhere.

Also, I can't invoke toString() myself as I would be eagerly evaluating that method (which is quite heavy in this object), so I want its invocation to be delayed until it's needed, just like when you pass an arbitrary object to log.debug("{}", someObject).

1

There are 1 answers

0
DWilches On

I finally found a (hack) solution here and here.

Instead of this (which doesn't compile):

ThreadContext.put("originalMessage", originalMessage);

I can do:

final var threadContextMap = (ObjectThreadContextMap) ThreadContext.getThreadContextMap();
threadContextMap.putValue("originalMessage", originalMessage);

This works as long as you add this JVM option:

-Dlog4j2.threadContextMap=org.apache.logging.log4j.spi.CopyOnWriteSortedArrayThreadContextMap

Now, this looks hacky because getThreadContextMap() the method is supposed to return a read-only view of the internal map:

Returns a read-only view of the internal data structure used to store thread context key-value pairs ...

So this solution is casting that read-only map and, knowing the implementation is not read-only, modifying it ... not the most transparent solution but it works.