mdc 是 slf4j 基于 threadlocal 实现的线程隔离上下文机制,用于注入 traceid 等标识以支持链路追踪;需在请求入口写入、结束时 clear,并手动或借助框架(如 sleuth)处理跨线程透传与日志格式配置。

MDC(Mapped Diagnostic Context)是 SLF4J 提供的一种线程绑定的上下文机制,适合在多线程环境下为每个请求注入唯一标识(如 traceId),让日志自动携带该信息,便于链路追踪和问题定位。关键在于:MDC 是基于 ThreadLocal 实现的,天然线程隔离,但需注意线程切换时的上下文传递。
在 Web 请求入口注入 MDC 信息
以 Spring Boot 为例,通常在拦截器或过滤器中生成并写入 traceId:
- 使用 UUID 或 Snowflake 等方式生成唯一 traceId
- 调用
MDC.put("traceId", traceId)写入当前线程上下文 - 务必在请求结束时调用
MDC.clear(),避免线程复用导致脏数据(尤其 Tomcat 使用线程池)
跨线程传递 MDC 上下文
当业务逻辑触发新线程(如 new Thread()、ThreadPoolExecutor、CompletableFuture)时,子线程默认无法继承父线程的 MDC。解决方法:
- 手动复制:在提交任务前获取当前 MDC 的副本(
MDC.getCopyOfContextMap()),在线程内用MDC.setContextMap(...)恢复 - 使用支持 MDC 透传的线程池包装类(如 Logbook、Logback 的
AsyncAppender自带处理,或自定义ThreadFactory) - 对
CompletableFuture,推荐用supplyAsync(Supplier, executor)并配合自定义异步执行器,或使用copyMdcToFuture()工具方法封装
配置日志格式输出 MDC 字段
在 logback.xml 中通过 %X{traceId} 引用 MDC 值:
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %X{traceId} - %msg%n</pattern>
若未设置 traceId,该位置将为空;也可设默认值:%X{traceId:-N/A}
结合 Sleuth/Zipkin 自动管理 MDC
如果项目已引入 Spring Cloud Sleuth,它会自动注入 traceId 和 spanId 到 MDC,并处理线程池透传(基于 TraceRunnable、TraceCallable 等装饰器)。此时只需确保:
- 依赖包含
spring-cloud-starter-sleuth(新版已整合进spring-cloud-starter) - 日志 pattern 中使用
%X{traceId}和%X{spanId} - 禁用 Sleuth 的日志自动埋点(如需精简)可通过
spring.sleuth.log.slf4j.enabled=false
不复杂但容易忽略的是线程清理和异步透传——MDC 不是“设一次就永远有效”,每次线程切换都得显式处理或借助框架兜底。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











