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