背景
在項目中, 通過一個 orderId 字段來 貫穿 訂單的一個執行過程。 通過 這個 orderId 可以解決 90%的問題排查效率問題,也不需要去 去定義 在 分布式系統中的一個 業務 id。 在剛開始時,業務簡單,都是在 log.info 中 人工去寫:
存在兩個問題:
1、隨着代碼量越來越多,需要不斷重復的在 日志中 寫上 orderid,心累,同時 作為程序員,應該很 清楚 DRY 原則,
2、不同的人書寫的 格式可能 大不相同,,且容易 遺漏。
3、項目會 調用 一個sdk 的三方 應用,目前是沒有進行鏈路綁定的,這個目前還沒有特別的好的方案, 但對 sdk的調用 用AOP做了 包裝,輸出 request和response, 可以 通過 MDC 同 orderId 綁定,方便 異常排查
springboot + MDC 實現 鏈路追蹤
1、項目中實踐
logback配置文件修改
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<!-- 定義路徑 -->
<property name="LOG_PATH" value="logs"/>
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>[%d] [%level] [orderId:%X{orderId}] [uuid:%X{uuid}] [%X{xRequestId}] %logger - %m%n</pattern>
</encoder>
</appender>
<root level="INFO">
<appender-ref ref="CONSOLE"/>
</root>
</configuration>
寫了工具類
public class MdcUtil {
public static final String ORDER_ID = "orderId";
public static final String UUID = "uuid";
public static void setOrderId(String value) {
put(ORDER_ID, value);
}
public static void setUUid(String value) {
put(UUID, value);
}
public static String getOrderId() {
return MDC.get(ORDER_ID);
}
public static void put(String key, Object value) {
if (key != null) {
MDC.put(key, value.toString());
}
}
public static void clear() {
MDC.clear();
}
}
在 代碼中 目前沒有統一用filter 或者Intercepter 進行處理,在請求中,目前 並沒有統一,所以現在的方案是 針對每個 請求入口進行 侵入式操作。
MdcUtil.setOrderId(orderId);
MdcUtil.clear();
這樣對 sdk 進行aop處理的過程中,就能 綁定 orderId 日志輸出了。
2、注意點
1、實現的原理 其實比較簡單,通過與 Thread 綁定, 所以在進行 線程切換后,需要 處理。
並且 不要 忘了進行 clear,否則在大量請求的情況下 容易造成 ThreadLocal 的 內存溢出。
2、目前只是對關鍵的執行過程進行了處理。 后面可以對所有的請求 通過 一個 全局的tranceId進行 綁定,貫穿請求的生命周期。
3、原理
見參考文檔。
4、思考
1、目前的編碼方案,僅僅解決了 一些 代碼的重復書寫, 對 三方 調用進行aop 操作與 traceId 線程維度的關聯
2、針對沒有 業務標示的 請求, 目前沒有操作。 需要 在請求入口進行 traceId的生成,或者前端請求傳入,做成更通用的模版。 可以參考 sleuth,SkyWalking 這些開源的框架