在 Spring Boot中實現包含上下文資訊的JSON日誌? - zachelrath

banq發表於2021-11-12

在除錯棘手的問題時,要使日誌發揮作用,上下文是至關重要的。每條日誌都應該始終包含與請求相關的租戶、使用者、訂單等的關鍵細節。日誌還必須是結構化的,以便可被日誌聚合器(如DataDog、StackDriver、Kibana等)解析和搜尋。

當我開始與Spring合作時,我希望實現與日誌相關的兩個關鍵目標。

  •     在與特定API請求相關的所有日誌訊息中自動包含關鍵的請求環境細節
  •     為每個API請求記錄一個結構化的訊息,提供關於請求(如端點、查詢引數)和響應(如狀態程式碼、持續時間)的基本細節。

具體來說:在審查日誌資訊時,通常會有一些與特定請求相關的關鍵 "上下文 "資訊,這些資訊最好是出現在與請求相關的每條日誌資訊中,但每個請求都不一樣。

例如,在認證的應用程式中,在所有的日誌訊息中包含使用者ID或租戶ID(在多租戶應用程式中),在試圖快速診斷一個問題時非常有幫助,因為你可以找到一個給定的日誌訊息,抓住租戶ID或使用者ID,然後使用這些來搜尋與該使用者相關的其他錯誤,或檢查該租戶是否有一些奇怪的配置設定,等等。或者,對於內部服務,你可能有一個訂單號,為了診斷處理該訂單的某個步驟的問題,你想過濾你的日誌,只顯示與給定訂單號相關的日誌。

 

我感到驚訝的是,我發現描述如何在Spring Boot中具體實現這些目標的資源很少。

此應用程式提供瞭如何使用 Log4j2 在 Spring Boot 應用程式中進行結構化、上下文日誌記錄的示例。結構化的JSON格式日誌如下案例:

{
  "instant" : {
    "epochSecond" : 1636639189,
    "nanoOfSecond" : 190833000
  },
  "thread" : "http-nio-8080-exec-1",
  "level" : "INFO",
  "loggerName" : "com.zachelrath.springboot.structuredloggingdemo.OrderController",
  "message" : "Getting items",
  "endOfBatch" : false,
  "loggerFqcn" : "org.apache.logging.log4j.spi.AbstractLogger",
  "contextMap" : {
    "orderNumber" : "123",
    "retailer" : "acme"
  },
  "threadId" : 24,
  "threadPriority" : 5
}
{
  "instant" : {
    "epochSecond" : 1636639189,
    "nanoOfSecond" : 193742000
  },
  "thread" : "http-nio-8080-exec-1",
  "level" : "INFO",
  "loggerName" : "com.zachelrath.springboot.structuredloggingdemo.OrderController",
  "message" : "Got items, returning",
  "endOfBatch" : false,
  "loggerFqcn" : "org.apache.logging.log4j.spi.AbstractLogger",
  "contextMap" : {
    "orderNumber" : "123",
    "retailer" : "acme"
  },
  "threadId" : 24,
  "threadPriority" : 5
}

點選標題檢視Github程式碼,主控制器位於此處:OrderController

 

執行: mvn spring-boot:run

 

為什麼使用Log4j2?

如果你熟悉Spring Boot,你可能會想,既然Spring Boot使用Logback作為其日誌框架,為什麼還要使用Log4j2?

我不會去爭論各種Java日誌庫的利弊,因為已經有很多其他的文章都是這樣寫的,但簡單的答案是這樣的。Log4j2的非同步日誌在高交易量方面大大超過了Logback,同時提供了一個開發者友好的API,相當容易使用。因此,我現在的公司已經將Log4j2標準化,用於高吞吐量的應用。

 

原理實現

Logback和Log4j2都支援一種叫做 "對映的診斷上下文"(Mapped Diagnostic Context)的功能,即MDC,每個執行緒都有一個字典/對映的上下文,然後你可以根據需要從中讀取和寫入。儲存線上程的MDC中的資料可以使用%X變數進行記錄。你可以記錄所有的MDC,也可以只記錄特定的資訊,例如,如果你的日誌模式包括%X{orderNumber},那麼只有執行緒的MDC中 "orderNumber "鍵的值會被記錄。由於每個網路請求都是由不同的執行緒來處理的,這就給了我們想要的東西。

 

相關文章