1. 程式人生 > >怎麼把CAT客戶端的RootMessageId記錄到每條日誌中?

怎麼把CAT客戶端的RootMessageId記錄到每條日誌中?

什麼是RootMessageId?

為了理解RootMessageId先簡單介紹一下CAT的資料結構設計。CAT客戶端會將所有訊息都封裝為一個完整的訊息樹(MessageTree),訊息樹可能包括Transaction、Event、Heartbeat、Metric等型別的訊息。具體如下:

  • Transaction:適合記錄跨越系統邊界的程式訪問行為,比如遠端呼叫,資料庫呼叫,也適合執行時間較長的業務邏輯監控,Transaction用來記錄一段程式碼的執行時間和次數
  • Event:用來記錄一件事發生的次數,比如記錄系統異常,它和transaction相比缺少了時間的統計,開銷比transaction要小
  • Heartbeat:表示程式內定期產生的統計資訊, 如CPU利用率, 記憶體利用率, 連線池狀態, 系統負載等
  • Metric:用於記錄業務指標、指標可能包含對一個指標記錄次數、記錄平均值、記錄總和,業務指標最低統計粒度為1分鐘

歡迎關注微信公眾號:萬貓學社,每週一分享Java技術乾貨。

其中,Transaction型別的訊息可作為訊息樹節點,而其他訊息只可作為訊息樹的葉子節點,也就是Transaction是一個可巢狀的遞迴結構。比如:

訊息樹的每一節點都有一個屬性messageId,用來唯一表示節點本身,其構成為:{domain}-{ip}-{timestamp}-{自增index}。另外還有兩個屬性,分別是parentMessageId, rootMessageId。parentMessageId表示父節點的messageId;rootMessageId則表示整個訊息樹的根節點的messageId。這兩個屬性在之後CAT的呼叫鏈分析與分散式呼叫鏈分析中發揮了關鍵作用。

歡迎關注微信公眾號:萬貓學社,每週一分享Java技術乾貨。

為什麼在日誌中記錄?

根據RootMessageId可以追蹤某一個請求的整個分散式呼叫鏈,結合每一條日誌快速定位耗費效能的癥結,做針對性的效能優化。更加方便地做效能優化,特別是TP95、TP99等指標。

遇到偶爾發生的bug,是最讓人頭疼的,只有先從日誌中找線索,但是在海量的日誌中找到出現bug的那一個請求是很困難的。有了上游API提供的RootMessageId,就可以快速過濾出那次請求的所有日誌, 更快速更方便地定位線上bug。

歡迎關注微信公眾號:萬貓學社,每週一分享Java技術乾貨。

在日誌的什麼地方記錄?

當然是每一句日誌上都記錄RootMessageId了。有的同學會說,這日誌也記錄的太多了。當發現線上問題無法定位時,你就會狠日誌太少了。其實記錄日誌不怕多,就怕不全。現在硬碟很便宜了,搞個幾T沒有問題,另外還可以設定日誌清理策略。

歡迎關注微信公眾號:萬貓學社,每週一分享Java技術乾貨。

怎麼記錄到日誌中?

前面說了那麼多,終於到了今天的壓軸大戲了。實現記錄到日誌有很多種方式,這裡使用的是MDC(Mapped Diagnostic Contexts)。顧名思義,其目的是為了便於我們診斷線上問題而出現的方法工具類,目前我們經常使用的logback和log4j都是支援的。

歡迎關注微信公眾號:萬貓學社,每週一分享Java技術乾貨。

只需要在每個請求的入口呼叫MDC.put方法,把rootMessageId賦值進去就可以了,是不是很簡單?示例程式碼:

//在Filter裡,從header裡獲取上下文資訊,包括messageId、parentMessageId、rootMessageId
CatContext catContext = new CatContext();
catContext.addProperty(Cat.Context.ROOT, request.getHeader(CatConstants.CAT_HTTP_HEADER_ROOT_MESSAGE_ID));
catContext.addProperty(Cat.Context.PARENT, request.getHeader(CatConstants.CAT_HTTP_HEADER_PARENT_MESSAGE_ID));
catContext.addProperty(Cat.Context.CHILD, request.getHeader(CatConstants.CAT_HTTP_HEADER_CHILD_MESSAGE_ID));
if (catContext.getProperty(Cat.Context.ROOT) == null) {
    //如果呼叫鏈的頂端,沒有上下文資訊,需要生成上下文資訊
    Cat.logRemoteCallClient(catContext);
} else {
    Cat.logRemoteCallServer(catContext);
}
MDC.put("traceId", catContext.getProperty(Cat.Context.ROOT));

如果你還不知道怎麼整合CAT呼叫鏈,可以看看之前的《SpringBoot整合CAT呼叫鏈例項》

然後,在設定日誌輸出格式的配置檔案裡增加[%X{traceId}]

Logback的xml配置示例:

<?xml version="1.0" encoding="UTF-8" ?>
<configuration scan="true">
    <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
        <layout class="ch.qos.logback.classic.PatternLayout">
            <pattern>[%d{yyyy-MM-dd HH:mm:ss.SSS}] [%thread] [%X{traceId}] [%-5level] [%-40.36logger{40}:%-4.4line] - %msg%n</pattern>
        </layout>
    </appender>

    <root level="INFO">
        <appender-ref ref="CONSOLE" />
    </root>
</configuration>

log4j的properties配置示例:

log4j.rootCategory=INFO,stdout,info,error
log4j.rootLooger=warn,stdout,info,error
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss,SSS} [%-5p] [%thread] [%X{traceId}]  method:%l - %m%n

歡迎關注微信公眾號:萬貓學社,每週一分享Java技術乾貨