异步导出接口里日志打印 traceId=null,同一次请求在 Controller 里有值,到了 @Async 方法和线程池里全是 null,按 traceId 只能搜到前 3 行日志。日志里能对上时间点,这种反而好排。
MDC 底层是 ThreadLocal,值只跟着创建它的线程走。任务提交到线程池后由工作线程执行,那个线程的 MDC 是空的,日志框架取不到就输出 null。线程复用还会把上一条请求的 traceId 带给下一条。这一点先确认清楚,后面就不用反复试。
Map<String,String> ctx = MDC.getCopyOfContextMap();
executor.submit(() -> {
MDC.setContextMap(ctx);
try { handle(); } finally { MDC.clear(); }
});
这次卡住的地方就在「MDC 上下文丢失」这一环,单独把它拎出来验证,别和其他改动混在同一次发布里。偶发问题先统计频率,一小时几次和一天一次,查法完全不同。
改完顺手把文档补一句,省得下次自己重新推一遍。
处理上按这个顺序:提交任务前把 MDC 的上下文复制成一个 Map;包装 Runnable 在 run 开头 set 结尾 clear 防止串号;线程池改用 TtlRunnable 或 TaskDecorator 统一处理 别在每个调用点手写;用两个并发请求验证 traceId 不会互相覆盖。
结果对不对很好判断,异步方法里的日志打出同一个 traceId,两条并发请求的 ID 不串。
