Java日志記錄中很常見的五條規則
日志記錄是在軟件開發過程中常常需要考慮的關鍵因素。當產品運行出錯時,日志文件通常是我們進行錯誤分析的首要選擇。而且,在很多情況下,它們是我們手上唯一可以用來查明發生狀況和問題根本原因的信息。可見,正確記錄需要的信息是極其重要的。
以下5條日志規則,讓我們可以檢查和改進在代碼中操作日志記錄的方式。
同時也請注意,我們既不會討論怎么配置一個日志引擎,也不會相互比較。
規則1、日志是面向讀者的
日志消息不僅要對書寫(日志)代碼的人有意義,也應該對日志文件的讀者有意義。
這似乎是一條很明顯但卻經常違背的規則。
ERROR: Save failure - SQLException .....
舉個例子吧,我們來看看下面這條日志信息:
ERROR: Save failure - SQLException .....
保存什么呢?這條消息在開發者看來是能說明一些問題的,但是對于正在苦苦查看產品問題的可憐家伙來說,卻毫無用處。
RROR: Save failure- Entity=Person, Data=[id=123 surname="Mario"] - SQLException....
更合適的信息是這樣的:
RROR: Save failure- Entity=Person, Data=[id=123 surname="Mario"] - SQLException....
這就解釋了你想要存儲的東西(這里是一個 Person,是一個 JPA 實體)以及這個 Person 實例相關的內容。
請注意相關這個單詞,并不是指泛泛的全體:我們不應該讓無價值的信息使日志文件變得亂糟糟,比如說完整打印所有的實體字段。
通常,實體名字和其邏輯關鍵字足以識別在表格中的一條記錄了。
規則2、匹配日志等級和執行環境
在 Java 系統中提供的所有日志管理工具和引擎都有日志等級(ERROR、INFO……)的概念,這將有可能過濾掉等級過低的消息。
例如,Java util logging 使用如下的等級:SEVERE、WARN、INFO、FINE、FINER、FINEST(+ CONFIG 和 OFF)。相反,兩個***的日志管理工具, Apache Commons Logging 和 SLFJ 更傾向于如下的等級:FATAL、ERROR、WARN、INFO、DEBUG、TRACE。
日志過濾等級則需要取決于代碼的開發階段:成品與仍處在測試、集成環境下的代碼日志等級就不能相同。
更具體的來說,日志等級也應該參考代碼的歸屬情況。
一般而言,我們自己的應用程序代碼應該比使用的任何第三方開發庫擁有更詳細的日志記錄。
比如說,Apache 的通用調試消息出現在我們的日志文件中,就沒有多大意義。
我通常像這樣配置日志記錄:
-
成品階段: 我的代碼是 INFO 等級,第三方庫是 WARN。
-
測試、集成階段:我的代碼是 DEBUG 等級,第三方庫是 WARN(或者如果需要的話是 INFO)。
-
開發階段:任何有意義的信息。
注意:個人而言,我不建議使用 TRACE/FINEST 等級(我并不是唯一持這種觀點的人,可以參考 這里 的例子)。
我并沒有發現 DEBUG 和 TRACE 有多大的區別,而年輕團隊的成員常常苦惱于到底是使用 DEBUG 還是 TRACE 。
根據 KISS 原則,我建議只使用 RROR、WARN、INFO 和 DEBUG 等級。
規則3、提交前去除編碼幫助日志
編碼時,我們常常會使用 logger 或是 System.out 在代碼中添加日志消息,來更好地掌握應用程序在執行、調試期間發生的狀況。
void aMethod(String aParam) {
LOGGER.debug(“Enter in aMethod”);
if (“no”.equals(aParam)) {
LOGGER.debug(“User says no”);
….
比如這樣的代碼:
void aMethod(String aParam) {
LOGGER.debug(“Enter in aMethod”);
if (“no”.equals(aParam)) {
LOGGER.debug(“User says no”);
….
這些消息顯示被調用的方法并且備份內部變量及方法參數值,主要是為了追蹤應用程序的行為。這在非測試驅動開發中相當受歡迎。
但糟糕的是,一旦代碼發布(測試之后成為成品)這些消息通常就無用武之地了。
所以,這條規則簡單來說就是:一旦你已經完成開發工作,在將代碼提交到使用中的 SCM 系統(git、svn……)之前,要去除所有臨時的和不必要的日志消息。
這條規則并不是要求去除所有的 DEBUG 消息,只是針對那些在應用程序完成和發布后就沒有意義的消息,或者是說當我們有理由相信應用程序能正確運行時就失去意義的那些消息。
規則4、log DEBUG消息之前檢查日志等級
根據第2條規則,在產品日志中,我們只會顯示 ERROR、WARN、INFO 等級的消息,但是在代碼中我們也可以使用一些不會影響產品運行的 DEBUG 消息。
if ( LOGGER.isDebugEnabled((){
LOGGER.debug (…….)
}
每次你想要 log 一個 DEBUG 消息時(在使用了規則3后的留下的所有消息),需要在前面添加一個檢查來明確是否啟用了 DEBUG 日志:
if ( LOGGER.isDebugEnabled((){
LOGGER.debug (…….)
}
這種做法可以阻止代碼去創建日志消息和調用 logger,提高產品運行程序的效率。
規則5、了解你的 logger
我們使用 logger 方法的方式可能會帶來巨大的開銷:
-
創建消息字符串
-
組織包含在消息字符串中的數據
我們應該查閱所選擇的日志管理工具、引擎的 javadoc 文檔,了解使用它們 logger 的最有效的方法。
LOGGER.info(“Person name is “ + person.getName());
例如,我們可以創建一條這樣的消息:
LOGGER.info(“Person name is “ + person.getName());
這就創建了不必要的字符串實例。
LOGGER.info(“Person name is {}“, person.getName());
使用SLF4J,正確的用法應該是:
LOGGER.info(“Person name is {}“, person.getName());
這里的格式化字符串是常量,不可變消息只有在允許 logging 的情況下才會被創建。