最近排查線上問題,無意中發現了Logger堵塞的情況,排查的同時也做下總結,做個筆記,以防備用。
先上圖,看下實際堵塞的情況

從圖中可以清楚的看到標黃的都在 waiting to lock <0x000000054011c380> 這個鎖,這個鎖被標紅線程持有,如果標紅的線程處理業務邏輯不夠快,哪其它線程就會一直處在 BLOCKED 中。
關於org.apache.logging.log4j.core.appender.OutputStreamManager的說明,可以查看官方文檔 點擊查看,關於Write重載的幾個方法如下

都有排它鎖,這樣在一個線程持有的時候,如果因為業務邏輯復雜、或者網絡延遲 等等 都有機會造成線上發生的堵塞情況。
下面說下自己整理的主要優化方案
- 升級版本
a) Logback
Logback與Log4j的對比可以看官方文檔 點擊查看,如果升級到Logback記得要開啟異步功能,進一步提高性能,異步配置如下
<appender name ="async" class= "ch.qos.logback.classic.AsyncAppender">
<discardingThreshold >0</discardingThreshold>
<queueSize>512</queueSize>
<appender-ref ref ="file"/>
</appender>
l discardingThreshold
默認情況下,當BlockingQueue還有20%容量,他將丟棄TRACE、DEBUG和INFO級別的event,只保留WARN和ERROR級別的event。為了保持所有的events,設置該值為0。
l queueSize
BlockingQueue的最大容量,默認情況下,大小為256。
l appender-ref
關聯的appender
b) Log4f2
是log4j的升級版,詳細資料可以查看官方文檔,點擊查看,我比較喜歡功能就是支持lambda,延遲加載就是利用這個功能實現的
2. 延遲加載
延遲加載說直白點就是按需加載,只有在用到的時候觸發。你是否在項目中經常這樣寫日志
logger.info("消息提示:"+e.getMessage()+",業務數據:"+e.getData()+",級別別:"+e.getLevel().getName());
或者這樣
logger.info("消息提示:{},業務數據:{},級別別:{}", e.getMessage(), e.getData(), e.getLevel().getName());
不管你用上面兩個示例中的哪種寫法,運行的時候,不管設置的級別是多少,都會執行e的方法,如果有序列化的情況會更糟糕。理想情況是根據設置的日志級別,只在真正調用的時候再執行,這時可以用如下寫法,做到按需索取日志
logger.info("消息提示:{},業務數據:{},級別別:{}", () -> e.getMessage(), () -> e.getData(), () -> e.getLevel().getName());
3. 根據環境設置不同的級別
根據程序運行的環境設置不同的日志級別,一般分為開發、測試、預上線、生產 環境,日志級別應該逐步提高,避免日志的IO頻繁讀寫提高性能,建議設置為debug、info、warn、warn
