全局异常处理中结合mdc打印追踪id的关键是确保异常日志输出时mdc中已存在有效traceid:需在请求入口(如filter/interceptor)提前注入并清理,日志pattern显式配置%x{traceid},异步线程须手动传递mdc上下文。

全局异常处理中结合 MDC 打印追踪 ID,核心不是“往异常对象里塞 traceId”,而是确保异常日志输出时,MDC 中已存在且有效的 traceId。只要 traceId 在异常发生前已被正确注入当前线程的 MDC,并在日志 pattern 中配置了 %X{traceId},log.error(e) 就会自动带上 traceId —— 包括完整的堆栈信息。
确保 traceId 已提前注入 MDC
traceId 必须在请求刚进入时就写入,不能等到异常发生才补。常见方式有:
- 用 Filter(推荐):在 doFilter 中生成或透传 traceId,调用 chain.doFilter 前 put,finally 中 remove
- 用 Interceptor:在 preHandle 中 set,在 afterCompletion 中 clear(注意:若 controller 抛异常,afterCompletion 仍会执行)
- Spring Boot + Sleuth:默认已自动完成,无需手动 set/clear
日志格式必须显式引用 MDC 字段
仅设置 MDC 不够,logback-spring.xml 或 log4j2.xml 中的 pattern 需包含 %X{traceId}(或 %X{traceId}、%X{logId} 等你实际使用的 key):
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
Log4j2 用户用 ThreadContext.put("traceId", id),对应 pattern 写 %X{traceId};SLF4J + Logback 用户用 MDC.put("traceId", id),写法一致。
异步线程需手动传递 MDC 上下文
线程池执行任务时,子线程默认不继承父线程的 MDC。若异常发生在 @Async 方法、CompletableFuture 或自定义线程中,需显式复制:
- 使用包装后的线程池(如 ThreadPoolTaskExecutor 的 setThreadFactory),在创建线程时拷贝 MDC
- 或在提交任务前手动保存:Map
context = MDC.getCopyOfContextMap(); - 再在子线程开头调用:MDC.setContextMap(context);,结束时 MDC.clear();
全局异常处理器中可增强错误标记
@ControllerAdvice 中的 @ExceptionHandler 方法本身就在主线程中,traceId 通常已就位。此时建议:
- 直接使用 log.error("业务异常:{}", msg, e) —— traceId 和堆栈都会自动打印
- 若需上报 APM(如 SkyWalking),调用 TraceContext.captureException(e) 显式打 error tag
- 避免重复 set traceId:除非你确定此处 MDC 为空(比如异步回调入口),否则不要在 handler 里再 put










