<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)