作者:明明如月學長 鏈接:https://juejin.cn/post/7117046503616544804 一、背景 在日常開發中,通常為了方便調試、方便查問題,會列印很多 INFO 級別的日誌。 隨著訪問量越來越大,一不小心,某個日誌文件一天的 size 就大於了某個閾值(如 5G),於是,收 ...
作者:明明如月學長
鏈接:https://juejin.cn/post/7117046503616544804
一、背景
在日常開發中,通常為了方便調試、方便查問題,會列印很多 INFO 級別的日誌。
隨著訪問量越來越大,一不小心,某個日誌文件一天的 size 就大於了某個閾值(如 5G),於是,收到了優化日誌大小的告警,一定時間內不優化反饋給你主管,囧...
日誌過大容易導致一些運維操作消耗機器性能,如日誌文件檢索、數據採集、磁碟清理等。
那麼,日誌瘦身哪些常見的思路呢? 本文結合某個具體案例談談我的看法。
推薦一個開源免費的 Spring Boot 最全教程:
二、日誌瘦身方法論
2.1 只列印必要的日誌
有時候為了方便測試,臨時列印很多 INFO 級別日誌。對於這種日誌,等項目上線前,可以將非必要的日誌刪除或者調整為 DEBUG 級別。
但有些場景下有些日誌可列印為 DEBUG 也可列印為 INFO,列印成 INFO 級別占空間,列印成 DEBUG 級別線上查問題的時候又需要用到,腫麽辦?
我們可以對日誌工具類進行改造,支持上下文傳遞某個開關時(正常調用沒有這個開關,通過公司的 Tracer 或者 RPC上下文傳遞),可以臨時將 DEBUG日誌提升為 INFO級別。 偽代碼如下:
if(log.isDebugEnable()){
log.debug(xxx);
}else if(TracerUtils.openDebug2Info()){
log.info("【debug2info】"+xxx);
}
這樣,可以將一些糾結是否要列印成 INFO 日誌的 log 列印成 DEBUG 級別,查問題時自動提升為INFO 日誌。 為了避免誤會,區分 DEBUG 提升 INFO 的日誌和普通 INFO 日誌,加上 類似【debug2info】 日誌首碼。
當然,你也可以搞一些其他騷操作,這裡只是舉個例子,請自行舉一反三。
2.2 合併列印
有些可以合併的日誌,可以考慮合併。
如在同一個方法前後都列印了 INFO 日誌:
INFO [64 位traceId] XXXService 執行前 size =10 INFO [64 位traceId] XXXService 執行後 size =4
可以合併成一條:
INFO [64 位traceId] XXXService 執行前 size =10 執行後 size =4
2.3 簡化&縮寫&壓縮
某個日誌非常有必要,但是列印的對象有些大,如果可以滿足問題排查需求的情況下,我們可以:
(1)選擇只列印其 ID
(2)創建一個只保留關鍵欄位的日誌專用對象,轉化為日誌專用對象,再列印。
(3)可以用縮寫,如 write 簡化為 w, read 簡化為 r, execute 簡化為e 等;比如 pipeline 中有 20個核心 bean ,列印日誌時可以使用不同的編號替代 bean 全稱,如 S1,S2 ,雖然沒那麼直觀,但既可以查問題,又降低了日誌量。
三、優化案例
3.1 場景描述
一個業務場景涉及很多 bean, 為了復用一些通用邏輯,這些 bean 都繼承自某個抽象類。 在抽象類中,定義了執行 bean 前後的一些通用邏輯,如執行前後列印當前 pipeline 中 item 的數量。 最後一個 bean 執行完結果轉換後需要列印出結果。
3.2 優化分析
3.2.1 只列印必要日誌
(1)由於當前 bean 執行前 相當於前一個 bean 執行後,因此只列印執行後的日誌就可以,執行前的INFO 日誌可以刪除或者改為 DEBUG (只列印必要日誌)
(2)通常問題只出現在執行前後 size 不一致的情況下,因此執行後列印日誌前可以加個判斷,如果執行前後 size 相同則不列印。(只列印必要日誌) 偽代碼如下:
if(sizeBefore != sizeAfter){
log.info("service:{}, 前size:{},後size:{}", getName(),sizeBefore, sizeAfter)
}
推薦一個開源免費的 Spring Boot 最全教程:
這招效果很明顯,因為大多數 bean 的執行前後 size 是相同的,就不會列印這條日誌。 而假設之前有 20 個,這條日誌就需要列印 20次,改進後可能只需要列印 2-3 次。
3.2.2 日誌合併
(2)為了方便查問題還需要列印執行前的 size ,那麼將執行前的 size 記錄在記憶體中,列印執行後日誌時多列印出執行前的 size。(合併列印) 偽代碼如下:
log.info("service:{}, 執行前size:{}", getName(),sizeBefore)
log.info("service:{}, 執行後size:{}", getName(),sizeBefore, sizeAfter)
合併後
log.info("service:{}, 前size:{},後size:{}", getName(),sizeBefore, sizeAfter)
3.2.3 日誌精簡
對於最終結果,將結果對象(如 XXDTO)轉化為只包括關鍵信息,如 id, title 的日誌對象(XXSimpleLogDTO),轉化為日誌對象後再列印。
log.info("resultId:{}",result.getId());
或者
log.info("result:{}",toSimpleLog(result));
3.3 效果評估
該日誌一天產生 5 G 左右,這裡百分之80% 左右都是列印執行前後的 size,10%左右是列印最終結果, 還有一些其他的日誌。 經過上述方法優化後,每天日誌量不足 1G。
在滿足排查問題的需要,又實現日誌瘦身之間進行了取捨。
四、總結
日誌瘦身需要進行權衡,保留排查問題的必要日誌情況下儘可能精簡。
可以採用刪除不必要日誌,合併日誌,日誌簡化等方式進行優化。
我們還可以進行一些騷操作,支持線上 DEBUG 臨時提升 INFO (當然也可以使用 arthas )來輔助我們查問題。
你還有哪些不錯的日誌瘦身技巧? 歡迎在文章末尾評論和我交流。
近期熱文推薦:
1.1,000+ 道 Java面試題及答案整理(2022最新版)
4.別再寫滿屏的爆爆爆炸類了,試試裝飾器模式,這才是優雅的方式!!
覺得不錯,別忘了隨手點贊+轉發哦!