在java异常处理中结合mdc打印带traceid的日志,需在请求入口(如filter)写入traceid到mdc,catch块中用log.error("msg", e)完整输出异常和mdc信息,logback配置pattern含%x{traceid},异步场景需手动传递mdc上下文。

在 Java 的异常处理中结合 MDC(Mapped Diagnostic Context)打印带 TraceId 的错误日志,核心是:在异常被捕获时,确保当前线程的 MDC 中已存有 TraceId,并在日志语句中主动输出它;同时注意 MDC 的生命周期管理,避免子线程或异步场景下丢失。
确保 TraceId 已写入 MDC
TraceId 通常在请求入口(如 Spring MVC 的拦截器、WebFilter 或 Sleuth/Logback 的自动注入)中生成并放入 MDC。若未使用分布式追踪框架,需手动设置:
MDC.put("traceId", UUID.randomUUID().toString());推荐在统一入口(如 Filter 或 HandlerInterceptor)中完成,保证每个请求都有唯一且一致的 traceId。
在 catch 块中直接使用 MDC 打印日志
SLF4J 日志框架会自动读取 MDC 中的 key(如 traceId),只要日志 pattern 配置了对应占位符,所有日志(包括异常堆栈)都会带上 traceId。关键在于:不要只打印 message,而要完整记录 Throwable:
- ✅ 正确写法(自动包含 MDC 和完整堆栈):
log.error("订单支付失败", e); - ❌ 错误写法(丢失堆栈、MDC 可能不生效):
log.error("订单支付失败: " + e.getMessage());
Logback 的 logback-spring.xml 中需配置 pattern 包含 %X{traceId},例如:
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - traceId:%X{traceId} - %msg%n%ex</pattern>注意异步与线程切换场景
MDC 是基于 ThreadLocal 实现的,子线程或线程池中默认不继承父线程的 MDC 内容。常见问题:
- 使用
CompletableFuture异步处理时,需手动传递 MDC: - Spring 中可配合
@Async+ 自定义ThreadPoolTaskExecutor,重写execute方法自动复制 MDC。
Map<string string> context = MDC.getCopyOfContextMap();
CompletableFuture.runAsync(() -> {
if (context != null) MDC.setContextMap(context);
try {
// 业务逻辑
} finally {
MDC.clear();
}
});</string>配合异常全局处理器统一处理
在 Spring Boot 中,可通过 @ControllerAdvice 捕获全局异常,并确保此时 MDC 仍有效:
@ExceptionHandler(Exception.class)
public ResponseEntity<string> handleException(Exception e) {
log.error("全局异常捕获", e); // 此处 MDC 仍可用(同一线程)
return ResponseEntity.status(500).body("服务异常");
}</string>注意:该 handler 必须运行在原始请求线程中(非新启线程),否则 MDC 为空。Spring 默认满足这一点。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











