如何在Java Spring @Async线程中记录TRACE-ID
解决Spring异步方法传递MDC上下文(TRACE-ID)的问题
问题根源
Spring的@Async默认线程池不会复制父线程的MDC(映射诊断上下文)内容,导致异步线程无法获取父请求中的TRACE-ID和X-CLIENT-ID,日志中对应字段为空。
解决方案
方法一:自定义异步线程池自动传递MDC
创建配置类,重写线程池的任务提交逻辑,将父线程的MDC上下文复制到异步线程:
import org.slf4j.MDC; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.scheduling.annotation.AsyncConfigurer; import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; import java.util.Map; import java.util.concurrent.Executor; @Configuration public class AsyncConfig implements AsyncConfigurer { @Override public Executor getAsyncExecutor() { ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); executor.setCorePoolSize(5); executor.setMaxPoolSize(10); executor.setQueueCapacity(25); executor.setThreadNamePrefix("CustomAsync-"); executor.initialize(); // 包装线程池,传递MDC上下文 return runnable -> { Map<String, String> mdcContext = MDC.getCopyOfContextMap(); executor.execute(() -> { try { if (mdcContext != null) { MDC.setContextMap(mdcContext); } runnable.run(); } finally { MDC.clear(); } }); }; } }
方法二:手动传递上下文(单方法场景)
针对特定异步方法,手动将父线程MDC上下文作为参数传入,再在异步方法中设置:
- 调用方代码:
@Service public class ParentService { private final SculptService sculptService; public ParentService(SculptService sculptService) { this.sculptService = sculptService; } public void handleRequest() { // 获取当前MDC上下文 Map<String, String> mdcContext = MDC.getCopyOfContextMap(); // 传入异步方法 sculptService.asyncMethod(mdcContext); } }
- 异步方法类:
@Service public class SculptService { @Async public void asyncMethod(Map<String, String> mdcContext) { try { if (mdcContext != null) { MDC.setContextMap(mdcContext); } // 执行异步逻辑,日志将输出父请求的TRACE-ID log.info("Some log..."); } finally { MDC.clear(); } } }
方法三:使用Spring内置MdcTaskDecorator(Spring 4.3+)
利用Spring提供的MdcTaskDecorator简化配置,自动传递MDC:
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.scheduling.annotation.EnableAsync; import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; import org.springframework.task.support.MdcTaskDecorator; import java.util.concurrent.Executor; @Configuration @EnableAsync public class AsyncConfig { @Bean(name = "asyncExecutor") public Executor asyncExecutor() { ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); executor.setCorePoolSize(5); executor.setMaxPoolSize(10); executor.setQueueCapacity(25); executor.setThreadNamePrefix("MdcAsync-"); // 添加MDC装饰器 executor.setTaskDecorator(new MdcTaskDecorator()); executor.initialize(); return executor; } }
在异步方法上指定使用该线程池:
@Async("asyncExecutor") public void asyncMethod() { log.info("Some log..."); }
验证效果
配置完成后,异步方法日志将显示父请求的TRACE-ID和X-CLIENT-ID,示例:
2022-11-18 15:23:32,123 [MdcAsync-1] TRACE-ID=[dcb803cb074a4e71ab4f9f43aa3c2ca1] X-CLIENT-ID=[mfs-reformer] INFO a.a.m.r.s.SculptService#55 - Some log...
内容的提问来源于stack exchange,提问作者Rakesh Malik
相关产品推荐
相关产品推荐

