MDC+InheritableThreadLocal和spring cloud sleuth
在微服務架構中,日志鏈路追蹤(Logback + Distributed Tracing) 是一個關鍵需求,主要用于跟蹤請求在不同服務間的調用鏈路,便于排查問題。常見的實現方案有兩種:
手動方案(MDC + InheritableThreadLocal)
自動化方案(Spring Cloud Sleuth + Zipkin/Jaeger)
下面從 Logback 日志集成 的角度,對比這兩種方案的實現方式、優缺點及適用場景。
- 手動方案:MDC + InheritableThreadLocal
核心組件
MDC(Mapped Diagnostic Context)
Logback 提供的線程本地存儲,用于存放日志變量(如 traceId)。
日志輸出時自動攜帶 MDC 中的字段(需配置 %X{traceId})。
InheritableThreadLocal
解決異步線程(如線程池、@Async)無法繼承 MDC 的問題。
實現步驟
(1) 定義 TraceContext(管理 traceId)
public class TraceContext {private static final InheritableThreadLocal<String> TRACE_ID = new InheritableThreadLocal<>();public static void setTraceId(String traceId) {TRACE_ID.set(traceId);MDC.put("traceId", traceId); // 存入 MDC,Logback 自動輸出}public static String getTraceId() {return TRACE_ID.get();}public static void clear() {TRACE_ID.remove();MDC.remove("traceId");}
}
(2) 攔截器設置 traceId(HTTP 請求入口)
public class TraceInterceptor implements HandlerInterceptor {@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {String traceId = request.getHeader("X-Trace-Id") != null ? request.getHeader("X-Trace-Id") : UUID.randomUUID().toString();TraceContext.setTraceId(traceId);return true;}@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {TraceContext.clear(); // 防止內存泄漏}
}
(3) Logback 配置(輸出 traceId)
<!-- logback-spring.xml -->
<configuration><appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"><encoder><pattern>%d{HH:mm:ss.SSS} [%thread] [%X{traceId}] %-5level %logger{36} - %msg%n</pattern></encoder></appender><root level="INFO"><appender-ref ref="STDOUT" /></root>
</configuration>
日志示例:
14:25:30.456 [http-nio-8080-exec-1] [abc123] INFO com.example.demo.Controller - Request received
(4) 異步線程支持(線程池需額外處理)
// 普通線程
new Thread(() -> {log.info("Async task"); // 能繼承 traceId
}).start();// 線程池需使用 TransmittableThreadLocal(阿里開源庫)
ExecutorService executor = Executors.newCachedThreadPool();
executor.submit(() -> {log.info("ThreadPool task"); // 默認會丟失 traceId!
});
TransmittableThreadLocal vs InheritableThreadLocal
- 自動化方案:Spring Cloud Sleuth + Logback
核心組件
Spring Cloud Sleuth
自動生成 traceId 和 spanId,并通過 MDC 輸出到日志。
支持 HTTP(Feign/RestTemplate)、MQ(Kafka/RabbitMQ)、gRPC 等自動傳播。
Logback 集成
Sleuth 自動填充 MDC,無需手動管理。
實現步驟
(1) 引入依賴
<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-sleuth</artifactId>
</dependency>
<!-- 可選:上報到 Zipkin -->
<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-sleuth-zipkin</artifactId>
</dependency>
(2) Logback 配置(自動攜帶 traceId)
<configuration><appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"><encoder><pattern>%d{HH:mm:ss.SSS} [%thread] [%X{traceId:-}] %-5level %logger{36} - %msg%n</pattern></encoder></appender><root level="INFO"><appender-ref ref="STDOUT" /></root>
</configuration>
日志示例(Sleuth 自動填充 traceId 和 spanId):
<configuration><appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"><encoder><pattern>%d{HH:mm:ss.SSS} [%thread] [%X{traceId:-}] %-5level %logger{36} - %msg%n</pattern></encoder></appender><root level="INFO"><appender-ref ref="STDOUT" /></root>
</configuration>
(3) 跨服務調用(自動傳播 traceId)
HTTP(Feign):自動添加 X-B3-TraceId Header。
MQ(Kafka):消息頭自動攜帶追蹤信息。
-
對比總結
-
推薦選擇
簡單項目:使用 MDC + InheritableThreadLocal(或 TransmittableThreadLocal)。
微服務架構:直接上 Spring Cloud Sleuth(或 OpenTelemetry),減少維護成本。