專案中引進這玩意,排查紀錄檔又快又準!

2022-12-20 18:08:29

大家好,我是三友~~

背景

隨著微服務盛行,很多公司都把系統按照業務邊界拆成了很多微服務,在排錯查紀錄檔的時候,因為業務鏈路貫穿著很多微服務節點,導致定位某個請求的紀錄檔以及上下游業務的紀錄檔會變得有些困難。

這時候可能有的小夥伴就會想到使用SkyWalking,Pinpoint等分散式追蹤系統來解決,並且這些系統通常都是無侵入性的,同時也會提供相對友好的管理介面來進行鏈路Span的查詢,但是搭建分散式追蹤系統還是需要一定的成本的,所以本文要說的並不是這些分散式追蹤系統,而是一款簡單、易用、幾乎零侵入、適合中小型公司使用的紀錄檔追蹤框架TLog。

TLog簡介

TLog提供了一種最簡單的方式來解決紀錄檔追蹤問題,TLog會自動的對你的紀錄檔進行打標籤,幫你自動生成traceId貫穿你微服務的一整條鏈路,在排查紀錄檔的時候,可以根據traceId來快速定位請求處理的鏈路。

TLog不收集紀錄檔,只在對你原來列印的紀錄檔上增強,將請求鏈路資訊traceId繫結到你列印的紀錄檔上。當出現微服務中那麼多節點的情況,官方推薦使用TLog+紀錄檔收集方案來解決。當然分散式追蹤系統其實是鏈路追蹤一個最終的解決方案,如果專案中已經上了分散式追蹤系統,那TLog並不適用。

如下圖,是ELK配合TLog,快速定位請求處理的鏈路的範例。

TLog接入

1、接入步驟

1.1、引入依賴
<dependency>
    <groupId>com.yomahub</groupId>
    <artifactId>tlog-all-spring-boot-starter</artifactId>
    <version>1.5.0</version>
</dependency>
1.2、替換logback設定項

到這其實就已經完成了設定。

1.3、測試

這裡是通過slf4j的LoggerFactory獲取Logger物件,因為logback適配了slf4j,最終會通過logback來輸出紀錄檔。

從這可以看出,11794076298070144 就是本次紀錄檔輸出的時候生成的一個請求的traceId,在排查紀錄檔的時候就可以通過這個traceId去搜尋出整個請求的鏈路紀錄檔。

2、TLog接入方式

TLog總共提供了三種方式接入專案

  • Javaagent接入方式
  • 位元組碼注入方式
  • 紀錄檔框架介面卡方式

上面案例的接入方式其實是屬於紀錄檔框架介面卡方式,並且是對於Logback框架的適配。TLog除了適配了Logback框架,還適配了Log4j框架和Log4j2框架,專案中可自行選擇。

Javaagent接入方式和位元組碼注入方式相比與紀錄檔框架介面卡方式對程式碼的入侵性更小,但是這兩種方式僅僅只支援SpringBoot專案,並且相較於紀錄檔框架介面卡的方式,MDC和非同步紀錄檔功能並不支援,所以要想完整體驗TLog的功能,還是建議選擇紀錄檔框架介面卡方式,紀錄檔框架介面卡方式其實接入也很快,其實也就是修改一下組態檔的事。

項目環境相容對比
專案環境相容對比
特性支援對比
特性支援對比

TLog的基本原理

1、紀錄檔標籤

前面在介紹TLog的時候,提到TLog會自動的對你的紀錄檔進行打標籤,這個標籤就是紀錄檔標籤,一個紀錄檔標籤最多可以包含如下資訊:

  • preApp:介面呼叫方服務名
  • preHost:介面呼叫方Host
  • preIp:介面呼叫方ip
  • currIp:當前服務ip
  • traceId:鏈路id,呼叫方如果傳遞就是傳遞的值,不傳遞就會重新生成
  • spanId:鏈路spanId

預設是按照如下labelPattern進行資料拼接生成紀錄檔標籤,所以預設只打出spanId和traceId。

這也就是上面為什麼範例中會輸出 <0><11794076298070144> 這種格式的原因,前面的0其實就是spanId。

如果你想改變紀錄檔標籤輸出其它資訊或者輸出的順序,只需要在SpringBoot組態檔中設定紀錄檔標籤的生成樣式就行。

tlog.pattern=[$preApp][$preIp][$spanId][$traceId]

2、TLogContext

TLogContext是TLog是一個核心的元件,這個元件內部是使用了TransmittableThreadLocal來傳遞traceId、preApp等資訊。

當有一個請求過來的時候,會從解析出traceId、preApp等資訊,然後設定到TransmittableThreadLocal中,之後就可以在整個呼叫鏈路中從TLogContext中獲取到traceId等資訊。

3、TLogRPCHandler

這個元件是用來處理呼叫方傳遞的traceId、preApp等資訊,設定到TLogContext和MDC中,同時根據紀錄檔標籤的格式生成紀錄檔標籤。

第三方框架的適配

在實際專案中,一個請求處理過程可能會出現以下情況

  • 非同步執行緒處理
  • 跨服務呼叫
  • MQ呼叫

那麼對於這些情況來說,traceId應該需要在非同步執行緒、跨服務、MQ等中傳遞,以便更好地排查一個請求的處理鏈路。

而TLog對於以上可能出現的情況都做了大量的適配,保證traceId能夠在非同步執行緒、微服務間、MQ等中能夠正確傳遞。

1、非同步執行緒

1.1 一般非同步執行緒

所謂的一般非同步執行緒就是指直接通過new Thread的方法來建立非同步執行緒,然後來執行,這種方式TLog是天然支援攜帶traceId的,如圖。

執行結果

從這可以看出這種非同步方式的確成功傳遞了traceId。

1.2 執行緒池

對於執行緒池來說,其實預設也是支援傳遞traceId,但是由於執行緒池中的執行緒是可以複用了,為了保證執行緒間的資料互不干擾,需要使用TLogInheritableTask將提交的任務進行包裝。

ThreadPoolExecutor pool =
        new ThreadPoolExecutor(121, TimeUnit.SECONDS, new LinkedBlockingQueue<>(10));
pool.execute(new TLogInheritableTask() {
    @Override
    public void runTask() {
      logger.info("非同步執行");
    }
});

上述程式碼的寫法會有點耦合,每次提交任務都需要建立一個TLogInheritableTask,比較麻煩,可以按如下寫法進行簡化。

TLogThreadPoolExecutor
TLogThreadPoolExecutor

自己寫個TLogThreadPoolExecutor繼承ThreadPoolExecutor,重寫execute方法(submit最終也會呼叫execute方法執行),然後將提交的任務統一包裝成TLogInheritableTask,這樣需要使用執行緒池的地方直接建立TLogThreadPoolExecutor就可以了,就不需要在提交任務的時候建立TLogInheritableTask了。

ThreadPoolExecutor pool =
        new TLogThreadPoolExecutor(121, TimeUnit.SECONDS, new LinkedBlockingQueue<>(10));
pool.execute(() -> logger.info("非同步執行"));

2、對RPC框架的支援

除了對非同步執行緒的支援,TLog也支援常見的Dubbo,Dubbox,OpenFeign三大RPC框架,在SpringBoot專案中不需要任何設定,只需要引入依賴就可以實現traceId在服務之間的傳遞

2.1 對Dubbo和Dubbox的支援

對於Dubbo和Dubbox的支援是基於Dubbo的Filter擴充套件點來的

TLog通過SPI機制擴充套件Filter,在消費者傳送請求前從TLogContext獲取到traceId,然後將traceId和其它呼叫者資料設定請求資料中,服務提供者在處理請求的時候,也會經過Filter,從請求中獲取到traceId等資訊,然後設定到TLogContext中,從而實現了traceId在dubbo的消費者和提供者之間的傳遞。

2.2 對OpenFeign的支援

對於OpenFeign的支援其實也是通過Feign提供的擴充套件點RequestInterceptor來實現的

傳送請求之前,從TLogContext獲取到traceId,將traceId等資訊新增到請求頭中,然後就可以通過Http請求將traceId等資訊傳遞。

當被呼叫方接收到請求之後,會經過TLogWebInterceptor這個攔截器進行攔截,從請求頭中獲取到這些引數,設定到TLogContext中。

3、對常用Http框架的支援

除了一些RPC框架,TLog也對一些Http框架進行了適配,比如

  • HttpClient
  • Okhttp
  • hutool-http
  • RestTemplate
  • forest

使用這些Http框架也可以實現traceId的傳遞

其實這些框架的適配跟Feign的適配都是大同小異,都是基於這些Http框架各自提供的擴充套件點進行適配的,將traceId等資訊放到請求頭中,這裡都不舉例了,具體的使用方法可以在官網檢視。

4、對SpringCloud Gateway的支援

同樣的,TLog也適配了SpringCloud Gateway

原理也是一樣的,就是適配了Gateway的GlobalFilter,從請求頭中獲取traceId等資訊。

除了適配了Gateway閘道器,TLog也適配了Soul閘道器。

5、對MQ的支援

對於MQ的支援跟非同步執行緒差不多,需要將你傳送的訊息包裝成TLogMqWrapBean物件

TLogMqWrapBean
TLogMqWrapBean

傳送的時候直接傳送TLogMqWrapBean物件過去

TLogMqWrapBean<BizBean> tLogMqWrap = new TLogMqWrapBean(bizBean);
mqClient.send(tLogMqWrap);

TLogMqWrapBean會將traceId等資訊攜帶,消費者接受到TLogMqWrapBean,然後通過TLogMqConsumerProcessor處理業務訊息。

TLogMqConsumerProcessor.process(tLogMqWrapBean, new TLogMqRunner<BizBean>() {
    @Override
    public void mqConsume(BizBean o) {
     //業務操作
    }
});

如此就實現了traceId通過MQ傳遞。

在實際使用中,根據不同的MQ的型別,可以將訊息包裝成TLogMqWrapBean物件的過程和處理訊息的過程做統一的封裝處理,以減少傳送訊息和處理訊息對於TLog的耦合。

6、對任務框架的支援

TLog主要是支援一下四種任務框架

  • JDK Timer
  • Quartz框架
  • spring-scheduled
  • XXL-JOB框架

其中,spring-scheduled和XXL-JOB在SpringBoot環境底下是無需任務設定的,只需要引入依賴即可。

Timer在使用的時候需要將任務包裝成TLogTimerTask,Quartz需要把QuartzJobBean替換成TLogQuartzJobBean就可以了。

小總結

其實從上面的各種適配可以看出,其實本質都是一樣的,就是根據具體框架的擴充套件點,在傳送請求之前從TLogContext獲取到traceId,將traceId等呼叫者的資訊在請求中攜帶,然後被呼叫方解析請求,取出traceId和呼叫者資訊,設定到被呼叫方服務中的TLogContext中。

所以,如果一旦需要遇到官方還未適配的框架或者元件,可以參照上述適配過程進行適配即可。

最後

總的來說,TLog是一款非常優秀的紀錄檔追蹤的框架,很適合中小公司使用。這裡來總結一下TLog的特性

  • 通過對紀錄檔打標籤完成輕量級微服務紀錄檔追蹤
  • 提供三種接入方式:javaagent完全無侵入接入,位元組碼一行程式碼接入,基於組態檔的接入
  • 對業務程式碼無侵入式設計,使用簡單,10分鐘即可接入
  • 支援常見的log4j,log4j2,logback三大紀錄檔框架,並提供自動檢測,完成適配
  • 支援dubbo,dubbox,feign三大RPC框架
  • 支援Spring Cloud Gateway和Soul閘道器
  • 支援HttpClient和Okhttp等http呼叫框架標籤傳遞
  • 支援多種任務框架,JDK的TimerTask,Quartz,XXL-JOB,spring-scheduled
  • 支援紀錄檔標籤的自定義模板的設定,提供多個系統級埋點標籤的選擇
  • 支援非同步執行緒的追蹤,包括執行緒池,多級非同步執行緒等場景
  • 幾乎無效能損耗,快速穩定,經過壓測,損耗在0.01%

由於本文篇幅有限,無法全面對TLog進行講解,如果想深入瞭解該框架,可自行閱讀官網或者原始碼。

官網:https://tlog.yomahub.com
github地址:https://github.com/dromara/TLog

往期熱門文章推薦

兩萬字盤點那些被玩爛了的設計模式

寫出漂亮程式碼的45個小技巧

RocketMQ保姆級教學

擼了一個簡易的設定中心,順帶還給整合到了SpringCloud

三萬字盤點Spring/Boot的那些常用擴充套件點

RocketMQ的push消費方式實現的太聰明瞭

@Async註解的坑,小心

掃碼或者搜尋關注公眾號 三友的java日記 ,及時乾貨不錯過,公眾號致力於通過畫圖加上通俗易懂的語言講解技術,讓技術更加容易學習,回覆 面試 即可獲得一套面試真題。