Recently I came across the problem of ThreadLocal context in multithreaded environment.
If the Thread that handled a request uses an Executor
to asynchronously execute tasks, the ThreadLocal context is lost and information such as requestId will be be missing in the logs.
We can solve this using the Decorator pattern:
Wrap Runnable to preserve caller Thread’s context
Implement ExecutorDecorator that wraps every execution
Additional: Configure Spring’s @Async to use Decorator
executor Thread, which can be any Thread (e.g. from a pool) that executes the task
1. Make the runnable context aware
To pass the MDC of the caller Thread to the executor Thread, we get a copy of the ContextMap.
Then we set this context map before execution of the task and reset it after the execution.
2. Implement custom Executor that decorates every task
Please note that I also implement Spring’s TaskExecutor for usage below.
3. Addition: Configure Spring’s @Async to use Decorator
There is a small sample application on Github. It has a service, which
has an @Async method. After startup, the service is called 10 times with a specific MDC context containing the loop-count and
the caller’s threadId. The asynchronous service method prints its MDC to the log, which should contain the original mentioned properties.
Without our ContextAwareExecutorDecorator, we will encounter the initially described problem: the ThreadLocal context gets lost.
In that case the log looks like this:
If we now wrap the Executor into our ContextAwareExecutorDecorator, as described in Step 3, the problem will be solved.
The resulting log looks like this: