淺談LOG日誌的寫法

來源:互聯網
上載者:User
文章來源於公司的大牛 1 Log的用途

不管是使用何種程式設計語言,日誌輸出幾乎無處不再。總結起來,日誌大致有以下幾種用途:

l  問題追蹤:通過日誌不僅僅包括我們程式的一些bug,也可以在安裝配置時,通過日誌可以發現問題。

l  狀態監控:通過即時分析日誌,可以監控系統的運行狀態,做到早發現問題、早處理問題。

l  安全審計:審計主要體現在安全上,通過對日誌進行分析,可以發現是否存在非授權的操作。 2 記錄Log的基本原則 2.1 日誌的層級劃分

Java日誌通常可以分為:error、warn、info、debug、trace五個層級。在J2SE中預定義的層級更多,分別為:SEVERE、WARNING、INFO、CONFIG、FINE、FINER、FINEST。兩者的對應大致如下:

Log4j、slf4j

J2se

使用情境

error

SEVERE

問題已經影響到軟體的正常運行,並且軟體不能自行恢複到正常的運行狀態,此時需要輸出該層級的錯誤記錄檔。

warn

WARNING

與業務處理相關的失敗,此次失敗不影響下次業務的執行,通常的結果為外部的輸入不能獲得期望的結果。

info

INFO

系統運行期間的系統運行狀態變化,或關鍵業務處理記錄等使用者或管理員在系統運行期間關注的一些資訊。

CONFIG

系統配置、系統運行環境資訊,有助於安裝實施人員檢查配置是否正確。

debug

FINE

軟體調試資訊,開發人員使用該層級的日誌發現程式運行中的一些問題,排除故障。

FINER

基本同上,但顯示的資訊更詳盡。

trace

FINEST

基本同上,但顯示的資訊更詳盡。

2.2 日誌對效能影響

不管是多麼優秀的日誌工具,在日誌輸出時總會對效能產生或多或少的影響,為了將影響降低到最低,有以下幾個準則需要遵守:

l  如何建立Logger執行個體:建立Logger執行個體有是否static的區別,在log4j的早期版本,一般要求使用static,而在高版本以及後來的slf4j中,該問題已經得到最佳化,擷取(建立)logger執行個體的成本已經很低。所以我們要求:對於可以預見的多數情況下單例啟動並執行class,可以不添加static首碼;對於可能是多例居多,尤其是需要頻繁建立的class,我們要求要添加static首碼

l  判斷記錄層級:

n對於可以預見的會頻繁產生的日誌輸出,比如for、while迴圈,定期執行的job等,建議先使用if對記錄層級進行判斷後再輸出。

n對於日誌輸出內容需要複雜的序列化,或輸出的某些資訊擷取成本較高時,需要對記錄層級進行判斷。比如日誌中需要輸出使用者名稱,而使用者名稱需要在日誌輸出時從資料庫擷取,此時就需要先判斷一下記錄層級,看看是否有必要擷取這些資訊。

l  優先使用參數,減少字串拼接:使用參數的方式輸出日誌資訊,有助於在效能和代碼簡潔之間取得平衡。當記錄層級限制輸出該日誌時,參數內容將不會融合到最終輸出中,減少了字串的拼接,從而提升執行效率。 2.3 什麼時候輸出日誌

日誌並不是越多越詳細就越好。在分析作業記錄,尋找問題時,我們經常遇到該出現的日誌沒有,無用的日誌一大堆,或者有效日誌被大量無意義的日誌資訊淹沒,尋找起來非常困難。那麼什麼時候輸出日誌呢。以下列出了一些常見的需要輸出日誌的情況,而且日誌的層級基本都是>=INFO,至於Debug層級日誌的使用情境,本節沒有專門列出,需要具體情況具體分析,但也是要追求“恰如其分”,不是越多越好。 2.3.1 系統啟動參數、環境變數

系統啟動的參數、配置、環境變數、System.Properties等資訊對於軟體的正常運行至關重要,這些資訊的輸出有助於安裝配置人員通過日誌快速定位問題,所以程式有必要在啟動過程中把使用到的關鍵參數、變數在日誌中輸出出來。在輸出時需要注意,不是一股腦的全部輸出,而是將軟體運行涉及到的配置資訊輸出出來。比如,如果軟體對jvm的記憶體參數比較敏感,對最低配置有要求,那麼就需要在日誌中將-Xms -Xmx -XX:PermSize這幾個參數的值輸出出來。 2.3.2 異常捕獲處

在捕獲異常處輸出日誌,大家在基本都能做到,唯一需要注意的是怎麼輸出一個簡單明了的日誌資訊。這在後面的問題問題中有進一步說明。 2.3.3 函數獲得期望之外的結果時

一個函數,尤其是供外部系統或遠程調用的函數,通常都會有一個期望的結果,但如果內部系統或輸出參數發生錯誤時,函數將無法返回期望的正確結果,此時就需要記錄日誌,日誌的基本通常是warn。需要特別說明的是,這裡的期望之外的結果不是說沒有返回就不需要記錄日誌了,也不是說返回false就需要記錄日誌。比如函數:isXXXXX(),無論返回true、false記錄日誌都不是必須的,但是如果系統內部無法判斷應該返回true還是false時,就需要記錄日誌,並且日誌的層級應該至少是warn。 2.3.4 關鍵操作

關鍵操作的日誌一般是INFO層級,如果數量、頻度很高,可以考慮使用DEBUG層級。以下是一些關鍵操作的舉例,實際的關鍵操作肯定不止這麼多。

n  刪除:刪除一個檔案、刪除一組重要資料庫記錄……

n  添加:和外系統互動時,收到了一個檔案、收到了一個任務……

n  處理:開始、結束一條任務……

n  …… 2.4 日誌輸出的內容

l  ERROR:錯誤的簡短描述,和該錯誤相關的關鍵參數,如果有異常,要有該異常的StackTrace。

l  WARN:警示的簡短描述,和該錯誤相關的關鍵參數,如果有異常,要有該異常的StackTrace。

l  INFO:言簡意賅地資訊描述,如果有相關動態關鍵資料,要一併輸出,比如相關ID、名稱等。

l  DEBUG:簡單描述,相關資料,如果有異常,要有該異常的StackTrace。

在日誌相關資料輸出的時要特別注意對敏感資訊的保護,比如修改密碼時,不能將密碼輸出到日誌中。 2.5 什麼時候使用J2SE內建的日誌

我們通常使用slf4j或log4j這兩個工具記錄日誌,那麼還需要使用J2SE的日誌架構嗎。當然需要,在我們編寫一些通用的工具類時,為了減少對第三方的jar包的依賴,首先要考慮使用java.util.logging。

考慮到slf4j等日誌架構提供了日誌bridge工具,為java.util.logging提供了Handler,所以普通應用的開發過程中也可以考慮使用J2SE自有日誌,這樣不但可以減少項目的編譯依賴,同時在應用實施時可以更靈活的選擇日誌的輸出工具包。
3 典型問題分析 3.1 該用日誌的地方不用


上圖對異常的處理直接使用e.printStackTrace()顯然是有問題的,正確的做法是:要麼通過日誌方式輸出錯誤資訊,要麼直接拋出異常,要麼建立新的自訂異常拋出。

另:對於靜態工具類函數中的異常處理,最簡單的方式就是不捕獲、不記錄日誌,直接向上拋出,如果認為異常類型太多,或者意義不明確,可以拋出自訂異常類的執行個體。
3.2 囉嗦重複、沒有重點


首先上面不應該有error層級的日誌。

其次在日誌中直接輸出e.toString(),為定位問題提供的資訊太少。

另外需要明確一點:日誌系統是一個多線程公用的系統,在兩行日誌輸出之間有可能會被插入其他線程的日誌記錄,不會按照我們的意願順序輸出,後面有更典型的例子。

 

最後,上面的日誌可以簡化為:

logger.debug(“從properties中...{}...{}...”,name, value, e);

logger.warn(“從properties中擷取{}發生錯誤:{}”,name, e.toString());

 

或者直接一句:

logger.warn(“從properties中...{}...{}...”,name, value, e);

 

或者更完美的:

 

if(logger.isDebugEnabled()){

logger.warn(“從properties中...{}...”, name, e);

}else{

logger.warn(“從properties中擷取{}發生錯誤:{}”, name, e.toString());

}

  3.3 日誌和異常處理的關係


首先上面的日誌資訊不夠充分,層級定義不夠恰當。

另外,既然將異常捕獲並記錄的日誌,就不應該重新將一個一模一樣的異常再次拋出去了。如果將異常再次拋出,那在上層肯定還需要對該異常進行處理,並記錄日誌,這樣就重複了。如果沒有特別原因,此處不應該捕獲異常。
3.4 System.out方式的日誌


上面的日誌形式十分隨意,只適合臨時的代碼調試,不允許提交到正式的程式碼程式庫中。

對於臨時調試日誌,建議在日誌的輸出資訊中添加一些特殊的連續字元,也可以用自己的名稱、代號,這樣可以在調試完畢後,提交代碼之前,方便地找到所有臨時代碼,一併刪除。 3.5 日誌資訊不明確


上面的“新增工作出錯。。。”既沒有記錄任務id,也沒有任務名稱,軟體部署後發現錯誤後,根據該日誌記錄不能確認哪一條任務錯誤,給進一步的分析原因帶來困難。

另外第二個紅圈中的問題有:要使用參數;一行日誌就可以了。

還有一些其他共性的錯誤,就不多說了。
3.6 忘記日誌輸出是多線程公用的


如果有另外一個線程正在輸出日誌,上面的記錄就會被打斷,最終顯示輸出和預想的就會不一致。正確的做法應是將這些資訊放到一行,如果需要換行可以考慮使用“\r”,如果內容較多,考慮增加if (logger.isDebugEnabled())進行判斷。而第二個例子中的輸出有System.out的習慣,相關內容應該一行完成。
3.7 多個參數的處理


對於多參的日誌輸出,可以考慮:

public void debug(String format, Object... arguments);

但是在使用多參時,會建立一個對象數組,也會有一定的消耗,為此,在對效能敏感的情境,可以增加對記錄層級的判斷。

 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.