mdc捕获traceid是最轻量可靠的方式,需在请求入口生成唯一id并写入mdc,日志模板显式引用%x{traceid},跨线程需手动传递mdc内容,字段名应统一定义常量。

直接用 MDC 捕获当前线程的 TraceId,是自研插件中最轻量、最可靠的方式。它不依赖外部组件,也不需要改业务代码,只要在请求入口生成一次 traceId 并塞进 MDC,后续同一线程内所有日志自动携带该标识。
TraceId 必须在请求入口生成并写入 MDC
入口点(如 SpringMVC 的 Filter、WebMvcConfigurer 的拦截器、或网关层)是唯一可信的生成时机。此时需:
- 生成全局唯一 traceId(推荐 UUID 或雪花 ID,避免冲突)
- 调用
MDC.put("traceId", traceId)将其绑定到当前线程 - 确保响应返回前调用
MDC.clear()防止线程复用污染
日志模板必须显式引用 MDC 中的 traceId
Logback 或 Log4j2 的 pattern 需包含 %X{traceId} 占位符,例如:
其中 :- 表示为空时显示空字符串,避免日志里出现 %X{traceId} 原样输出。不配置这个,MDC 写了也白写。
跨线程场景必须主动传递 MDC 内容
@Async、线程池、CompletableFuture 等会创建新线程,原线程的 MDC 不会自动继承。解决方案是包装 Runnable/Callable:
- 获取当前 MDC 内容:
Map<string string> copy = MDC.getCopyOfContextMap()</string> - 在线程执行前还原:
MDC.setContextMap(copy) - 执行结束后调用
MDC.clear()
Spring 已提供 ThreadPoolTaskExecutor 的子类扩展点,可统一注入该逻辑,无需每个异步方法手动处理。
插件中避免硬编码 key 名,统一管理 traceId 字段名
不要在各处写死 "traceId" 字符串。定义常量类,例如:
这样后续若需支持多字段(如 spanId、rpcType),只需扩展常量,且能保证所有模块使用同一 key,避免因拼写差异导致日志丢失 traceId。










