springboot3.2+jdk21 虚拟线程 使用MDC traceId追踪日志

springboot3.2发布了,配合jdk21使用虚拟线程,使用MDC + traceId追踪日志方法

关于虚拟线程和MDC traceId这里就不多说了,如果不清楚请自行查询资料

第一步,创建MdcVirtualThreadTaskExecutor 

/**
 * @author xxley
 * @date 2022/7/25 15:54
 */
public class MdcVirtualThreadTaskExecutor extends TaskExecutorAdapter {
    
    public MdcVirtualThreadTaskExecutor(Executor concurrentExecutor) {
        super(concurrentExecutor);
    }

    @Override
    public <T> Future<T> submit(Callable<T> callable) {
        Map<String, String> context = MDC.getCopyOfContextMap();
        return super.submit(() -> {
            if (context != null) {
                //将父线程的MDC内容传给子线程
                MDC.setContextMap(context);
            } else {
                //直接给子线程设置MDC
                // 这个方法是我自己封装的,实际是MDC.put("traceId", traceId);
                TraceIdContext.setMCData(null);
            }
            try {
                //执行任务
                return callable.call();
            } finally {
                MDC.clear();
            }
        });
    }

    @Override
    public void execute(Runnable runnable) {
        Map<String, String> context = MDC.getCopyOfContextMap();
        super.execute(() -> {
            if (context != null) {
                //将父线程的MDC内容传给子线程
                MDC.setContextMap(context);
            } else {
                //直接给子线程设置MDC  
                // 这个方法是我自己封装的,实际是MDC.put("traceId", traceId);
                TraceIdContext.setMCData(null);
            }
            try {
                //执行任务
                runnable.run();
            } finally {
                MDC.clear();
            }
        });
    }
}

第二步,配置AsyncConfig  

@Configuration
@EnableAsync
public class AsyncConfig  {

   @Bean
   public TaskExecutor taskExecutor(SimpleAsyncTaskExecutorBuilder executor) {
       SimpleAsyncTaskExecutor build = executor.build();
       return new MdcVirtualThreadTaskExecutor(build);
   }
}

此时使用@Async异步调用, 在异步方法中输出的日志就会携带主线程的traceId

虚拟线程不要池化,创建和销毁虚拟线程消耗资源很低,可以忽略不计。

这是我找到的一种方法,如果你有更好的方法,请告知一下。