一看這個標題我想大家一定進來想“懟”我,你這不是小題大作嗎?在java中列印日志不是一件非常簡單的事情嗎?
在java中常用的日志級別為DEBUG、INFO、WARN、ERROR四個日志級別,通常開發環境開啟DEBUG,生產環境開啟INFO級別,采用主流的日志采集工具包諸如log4j、logback,
但日志輸出真的有這么簡單嗎?其實里面蘊含著很多的規范,或者是最佳實踐,并且還有一些非常有用設計技巧方便查詢關聯日志的技巧,容我慢慢道來,
輸出日志的終極目標:助力于快速定位問題、解決問題,
接下來將圍繞該目標,闡述一下日志相關的一些最佳實踐,
1、日志的基本規范
首先,我們還是簡單介紹一下常用的4個日志級別,并說明各個級別在使用時應該注意的問題,
-
DEBUG
輸出程式的除錯資訊,優雅的DEBUG日志可以讓我們在排查問題的時候,壓根就不需要使用開發工具的DEBUG斷點除錯功能,而是直接看Debug的輸出日志即可定位問題,- 列印請求、回應資料包,特別是入口處將所有請求引數列印
- 對核心方法,特別核心計算邏輯前后列印當時的輸入與輸出,并日志中顯示包含方法名稱,
- 對核心流程(回圈、分支)等條件判斷時輸出必要的入參于與回傳結果,清晰的展示程式的運行軌跡,
-
INFO
INFO日志我們通常用于記錄系統/組件的基本運行情況和運行狀態,特別適合列印一次性日志,例如核心類的啟動程序、狀態變更等資訊,輸出的內容一定要非常詳細,不要擔心影響性能,目前的主流日志輸出框架例如logback,其日志的列印基本都是基于異步的,性能已經非常高,無需擔心性能損耗,
-
WARN
警告級別,通常用于可預知但又不希望發生的情況,典型的使用場景是列印業務類例外日志,例如引數校驗不通過、權限不足,余額不足等用戶可處理的;再例如中間件開發時,有一些分支是我們不希望進入的,因為進入就代表性能差等場景,但這類例外不需要相關系統負責人干預就能得到處理的, -
ERROR
通常用來列印系統級別的日志,需要人為來干預,通常較大業務規模的公司都會將系統級別的例外(ERROR)接入監控告警中心,一旦持續發生多少條,錯誤率占比多少,將會觸發告警,相關負責人跟進處理,既然需要人為來干預,ERROR日志不僅要列印出錯誤堆疊,同時一定要主動列印出背景關系環境,至少可以列印出該例外所在方法的入參,盡量讓人能夠根據錯誤日志與背景關系,就能快速定位到具體的代碼行,
2、日志進階
上述是一些基本的日志使用規范,分布式已成為企業架構的標配,一個應用至少會部署2臺機器,當用戶反饋業務例外時嘗試去跟蹤日志時會面臨一個問題:去哪臺業務機器上去查詢日志,如果只有2臺還好辦,大不了一臺臺去嘗試,但如果有10臺,20臺甚至上百臺,在這樣輪詢幾乎不可能實作,那該如何處理呢?
經典的ELK架構如下圖所示:

通常,為了避免在每臺業務機器上部署一個logstash去抽日志,我們通常建議自定義一個log append,直接將日志寫入到kafka中,然后再掛logstash從kafka中抽取日志,寫入到es集群,然后通過kibana對日志進行可視化搜索,
然后我們查詢日志就變得類似這樣了:

本文并沒有打算探討ELK架構,這個后續應該會單獨展開詳細介紹,而是就算我們接入了ELK,從ELK可以統一查看根據關鍵字查詢日志了,該日志會包含所有服務器上的日志,比單獨一臺一臺去找依然方便了很多,
但這些日志其實是雜亂無章的,查詢出來的日志與日志之間沒有任何關聯性,而我們在解決特定問題時通常希望日志的**“隔離性”**,希望我們可以根據一個統一的關鍵字,例如請求號篩選出所有相關的日志,這樣對我們分析排查問題能起到極大的促進作用,
2.1 每條日志包含一個請求序
那我們如何將“請求號”統一寫入到日志檔案中,肯定不能要求在專案中去修改所有日志輸出到地方,手動去增加請求編號,
我們可以通過自定義一個append,在append中對用戶的日志統一進行二次加工,
logback、log4j都可以自定append,接下來以當前使用最廣logback舉例,和大家介紹一下自定義append,
1、繼承 AppenderBase 并初始化
首先需要繼承logback的append的基礎類:AppenderBase,入下圖所示:

其中有一個初始化方法start,通常的做法是先呼叫super.start()標記append啟動,然后可以在該方法中初始化kafka的訊息發送者物件,
2、重寫append方法

主要是從ILoggingEvent物件中獲取原始日志,然后我們對原始日志加以加工,加工代碼如下圖所示:

關鍵是reqeustId的獲取,這個通常會配置一個http filter,進入請求鏈中放入到執行緒本地變數中(ThreadLocal),然后在日志輸出時從執行緒背景關系環境中獲取,為了能在執行緒池等復雜環境下使用,通常可以使用(TransmittableThreadLocal),關于在執行緒池中傳遞資料,需要使用ttl框架,關于這塊的想象介紹可以查看筆者的另一篇博文:全鏈路壓測必備基礎組件之執行緒背景關系管理之“三劍客”
一鍵三連(關注、點贊、留言)是對我最大的鼓勵,
各位技術朋友們,我是《RocketMQ技術內幕》一書作者,CSDN2020博客之星TOP2,熱衷于中間件領域的技術分享,維護「中間件興趣圈」公眾號,旨在成體系剖析Java主流中間件,構建完備的分布式架構體系,歡迎大家大家關注我,第一時間獲得最新干貨文章,

??點擊下方卡片,關注彈出內容,回復「PDF」可領取海量學習資料,共同刷題進步,內互相監督,記錄成長!

轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/301512.html
標籤:java
