mdc是slf4j提供的线程绑定map,用于全链路日志追踪;需在web入口生成traceid、rpc/http调用透传、异步任务继承上下文,并在日志格式中渲染%x{traceid},同时避免遗漏清理和混用日志框架。

“map 指令”不是标准日志框架(如 Logback、Log4j)或主流追踪系统(如 SkyWalking、OpenTelemetry)中的原生概念。生产环境中实现基于请求 ID 的全链路日志追踪,核心依赖的是 MDC(Mapped Diagnostic Context)——它本质就是一个线程绑定的 Map<string string></string>,用于在日志输出时动态注入上下文字段(比如 traceId)。所谓“利用 map 指令”,实际是指正确使用 MDC 的 put()、get()、remove() 等操作,完成 traceId 的生成、透传与清理。
一、MDC 是真正的“map”载体,不是配置语法指令
MDC 是 SLF4J 提供的静态工具类,底层维护一个 ThreadLocal<map></map>。它不靠 XML 或 YAML 里的 “map:” 配置生效,而是靠代码调用驱动:
-
MDC.put("traceId", "a1b2c3d4")—— 把 traceId 绑定到当前线程 -
log.info("order created")—— 日志格式中写%X{traceId}即可自动渲染 -
MDC.remove("traceId")—— 请求结束必须清理,否则线程复用会污染后续请求
二、生产集成关键三步:生成 → 透传 → 渲染
以 Spring Boot 为例,需覆盖 Web 层、RPC 调用、异步任务三大场景:
-
Web 入口统一生成:用 Filter 或 Interceptor,在请求开始时检查 HTTP 头(如
X-Trace-ID),有则复用,无则生成(推荐IdUtil.fastSimpleUUID().substring(0,16)或UUID.randomUUID().toString().replace("-", "")),并MDC.put("traceId", id) -
下游服务透传:HTTP 调用用 RestTemplate/Feign 时,通过拦截器自动添加
X-Trace-ID请求头;Dubbo 调用通过自定义Filter将 MDC 中的 traceId 写入RpcContext,提供方再从RpcContext取出并写回 MDC -
异步任务继承上下文:线程池执行前,手动将主线程 MDC 内容复制到子线程(可用
new ThreadPoolTaskExecutor()+setThreadFactory包装,或使用 Hutool 的ThreadUtil.execAsync()自动继承)
三、日志格式与存储需配套支持
仅写入 MDC 不够,日志输出和采集系统必须能识别并保留该字段:
- Logback 配置示例:
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - traceId=%X{traceId} - %msg%n</pattern> - 集中式日志平台(如 ELK、阿里云 SLS)需将
traceId设为 keyword 类型字段,支持精确查询与聚合 - 响应体中返回 traceId(如
{"data": {}, "traceId": "a1b2c3d4"}),方便前端报错时提供线索
四、避免常见陷阱
很多线上问题源于细节疏漏:
- 忘记在 Filter/Interceptor 的
finally块中MDC.clear()或MDC.remove("traceId"),导致线程复用后日志混串 - 定时任务(@Scheduled)或消息监听器(@RabbitListener)未主动调用
MDCTraceUtils.addTraceId(),造成无 traceId 日志孤岛 - Feign 客户端未配置
RequestInterceptor,HTTP 调用丢失 traceId 头 - 日志框架混用(如同时引入 log4j-over-slf4j 和 slf4j-log4j12),导致 MDC 不生效










