<a href="https://www.buymeacoffee.com/eduardoeljaiek" target="_blank"><img src="https://cdn.buymeacoffee.com/buttons/v2/default-blue.png" alt="Buy Me A Coffee" style="height: 35px !important;width: 125px !important;" ></a>
**Last Update**: 21.04.2024
***
### Overview
As you know, In our Spring Boot applications, [[Mapped Diagnostic Context | Mapped Diagnostic Context (MDC)]] values are used to enrich log entries with context-specific information. However, MDC values aren’t automatically propagated to asynchronous methods annotated with `@Async`. When I was dealing with this challenge, the first solution that came to me mind was **Manual Propagation**.
### The Problem with Manual Propagation
When you use the `@Async` annotation, the associated method runs in a different thread, which means the thread-local variables (like MDC) don’t automatically propagate. **Manually propagating MDC values in each @Async-annotated method**
**can be error-prone and tedious** because you need to handle the following steps repeatedly:
1. Capture the current thread's MDC before the method starts.
2. Set the captured MDC in the new asynchronous thread.
3. Ensure MDC values are cleared after execution to prevent memory leaks.
### Benefits of Using TaskDecorator
I found that Spring Team got me covered :). **It's possible to abstract the MDC handling logic and ensure consistent propagation**, implementing [org.springframework.core.task.TaskDecorator]([https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/core/task/TaskDecorator.html](https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/core/task/TaskDecorator.html) ) interface. Here are the main benefits:
1. **Centralized Logic:** The `TaskDecorator` implementation centralizes the logic for copying and setting the MDC, reducing code duplication and potential errors.
1. **Reusability:** You write the MDC propagation logic once and reuse it across all asynchronous tasks.
2. **Cleaner Code:** Your business logic remains clean and free from MDC handling boilerplate code.
3. **Maintainability:** Easier to maintain and debug MDC-related code since you have a single focal point for MDC handling.
### Setting the Task Decorator
Here’s an example of how to implement and use `TaskDecorator`:
```java
import org.slf4j.MDC;
import org.springframework.core.task.TaskDecorator;
@Component
public class MdcTaskDecorator implements TaskDecorator {
@Override
public Runnable decorate(Runnable runnable) {
// Copy the current MDC context
Map<String, String> contextMap = MDC.getCopyOfContextMap();
return () -> {
try {
// Set the MDC context if it's not null
if (contextMap != null) {
MDC.setContextMap(contextMap);
}
// Execute the original runnable
runnable.run();
} finally {
. // Clear the MDC context to avoid context leakage
MDC.clear();
}
};
}
}
```
When `MdcTaskDecorator` is annotated with `@Component`, Spring Boot's auto-configuration will automatically detect it and apply it.
#### Overriding the default Executor bean
Overriding the default Executor bean in a Spring application can be a good idea in several situations, particularly when you need to customize the execution of asynchronous tasks.
```java
@EnableAsync
@Configuration
class AsyncConfig {
@Bean
Executor getAsyncExecutor() {
var executor = new ThreadPoolTaskExecutor();
executor.setTaskDecorator(new MdcTaskDecorator());
executor.initialize();
return executor;
}
}
```
> [!Happy Coding]
> You can review a working example [here](https://github.com/ehayik/spring-boot-async-mdc).
***
**References**:
- [Logging MDC with @Async and TaskDecorator](https://stackoverflow.com/questions/45890181/logging-mdc-with-async-and-taskdecorator)