本系列第 12 篇。一次请求跨多个方法,日志散在各处,靠 traceId 串起来。这就是 MDC 的用武之地。
MDC 是什么
MDC(Mapped Diagnostic Context)是一个线程级的 Map ,你放进去的键值对,会在这个线程后续所有日志里自动可用。最典型的用法就是 traceId。
基本用法
java
import org.slf4j.MDC;
// 请求入口(拦截器 / Filter)放入
MDC.put("traceId", UUID.randomUUID().toString());
try {
// ...业务处理,期间所有日志都自动带上 traceId
} finally {
MDC.clear(); // 一定要清,否则线程池复用会串味
}
Pattern 里输出
xml
<pattern>%d [%thread] %-5level %X{traceId} %logger - %msg%n</pattern>
%X{traceId} 就会取出 MDC 里的 traceId。这样同一次请求的所有日志都带同一个 id,在日志平台里一搜就能看到完整链路。
最大的坑:跨线程不传递
MDC 底层是 ThreadLocal,跨线程/线程池不会自动传递。当你用了:
@Async异步方法- 线程池
ExecutorService CompletableFuture
子线程里的日志会丢失 traceId。
解决办法
- 阿里 TransmittableThreadLocal(TTL) :专门解决线程池间 ThreadLocal 传递,配合
TtlExecutors包装线程池即可。 - 手动传递 :提交任务前取出 MDC,在子线程开头
MDC.setContextMap(...),结束时清理。
实战建议
- 在网关或 Filter 统一生成 traceId,若上游已带(如 HTTP header)则复用,实现跨服务链路串联。
- 记得
finally里MDC.clear(),线程池场景尤其重要,否则会"串味"------A 请求的 traceId 出现在 B 请求日志里。
上一篇:接入日志平台
下一篇:生产环境配置建议清单