原文連結
https://tech.meituan.com/2022/07/29/tips-for-avoiding-log-blocking-threads.html
https://cloud.tencent.com/developer/article/1927375
1. 前言
日誌對程式的重要性不言而喻。它很“大”,我們在專案中經常透過日誌來記錄資訊和排查問題,相關程式碼隨處可見。它也很“小”,作為輔助工具,日誌使用簡單、上手快,我們通常不會花費過多精力耗在日誌上。但看似不起眼的日誌也隱藏著各種各樣的“坑”,如果使用不當,它不僅不能幫助我們,反而還可能降低服務效能,甚至拖垮我們的服務。
日誌導致執行緒Block的問題,相信你或許已經遇到過,對此應該深有體會;或許你還沒遇到過,但不代表沒有問題,只是可能還沒有觸發而已。本文主要介紹美團統一API閘道器服務Shepherd(參見《百億規模API閘道器服務Shepherd的設計與實現》一文)在實踐中所踩過的關於日誌導致執行緒Block的那些“坑”,然後再分享一些避“坑”經驗。
2. 背景
API閘道器服務Shepherd基於Java語言開發,使用業界大名鼎鼎的Apache Log4j2作為主要日誌框架,同時使用美團內部的XMD-Log SDK和Scribe-Log SDK對日誌內容進行處理,日誌處理整體流程如下圖1所示。業務列印日誌時,日誌框架基於Logger配置來決定把日誌交給XMDFile處理還是Scribe處理。其中,XMDFile是XMD-Log內部提供的日誌Appender名稱,負責輸出日誌到本地磁碟,Scribe是Scribe-Log內部提供的日誌Appender名稱,負責上報日誌到遠端日誌中心。
隨著業務的快速增長,日誌導致的執行緒Block問題愈發頻繁。比如呼叫後端RPC服務超時,導致呼叫方大量執行緒Block;再比如,業務內部輸出異常日誌導致服務大量執行緒Block等,這些問題嚴重影響著服務的穩定性。因此,我們結合專案在過去一段時間暴露出來的各種由於日誌導致的執行緒Block問題,對日誌框架存在的穩定性風險因素進行了徹底的排查和修復,並線上下、線上環境進行全方位驗證。在此過程中,我們總結了一些日誌使用相關的實踐經驗,希望分享給大家。
在進入正文前,首先介紹專案當時的執行環境和日誌相關配置資訊。
- JDK版本
java version "1.8.0_45"
Java(TM) SE Runtime Environment (build 1.8.0_45-b14)
Java HotSpot(TM) 64-Bit Server VM (build 25.45-b02, mixed mode)
- 日誌依賴版本
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
<version>2.7</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.7</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-slf4j-impl</artifactId>
<version>2.7</version>
</dependency>
- 日誌配置檔案
<?xml version="1.0" encoding="UTF-8"?>
<configuration status="warn">
<appenders>
<Console name="Console" target="SYSTEM_OUT" follow="true">
<PatternLayout pattern="%d{yyyy/MM/dd HH:mm:ss.SSS} %t [%p] %c{1} (%F:%L) %msg%n" />
</Console>
<XMDFile name="ShepherdLog" fileName="shepherd.log"/>
<!--XMDFile非同步磁碟日誌配置示例-->
<!--預設按天&按512M檔案大小切分日誌,預設最多保留30個日誌檔案。-->
<!--注意:fileName前會自動增加檔案路徑,只配置檔名即可-->
<XMDFile name="LocalServiceLog" fileName="request.log"/>
<Scribe name="LogCenterSync">
<!-- 在指定日誌名方面,scribeCategory 和 appkey 兩者至少存在一種,且 scribeCategory 高於 appkey。-->
<!-- <Property name="scribeCategory">data_update_test_lc</Property> -->
<LcLayout/>
</Scribe>
<Async name="LogCenterAsync" blocking="false">
<AppenderRef ref="LogCenterSync"/>
</Async>
</appenders>
<loggers>
<AsyncLogger name="com.sankuai.shepherd" level="info" additivity="false">
<AppenderRef ref="ShepherdLog"