Overview
一個(gè)在生產(chǎn)環(huán)境里運(yùn)行的程序如果沒(méi)有日志是很讓維護(hù)者提心吊膽的,有太多雜亂又無(wú)意義的日志也是令人傷神环鲤。程序出現(xiàn)問(wèn)題時(shí)候,從日志里如果發(fā)現(xiàn)不了問(wèn)題可能的原因是很令人受挫的。本文想討論的是如何在Java程序里寫(xiě)好日志季蚂。
一般來(lái)說(shuō)日志分為兩種:業(yè)務(wù)日志和異常日志,使用日志我們希望能達(dá)到以下目標(biāo):
- 對(duì)程序運(yùn)行情況的記錄和監(jiān)控琅束;
- 在必要時(shí)可詳細(xì)了解程序內(nèi)部的運(yùn)行狀態(tài)扭屁;
- 對(duì)系統(tǒng)性能的影響盡量小涩禀;
Java日志框架
Log4j 或 Log4j 2
Apache的開(kāi)源項(xiàng)目料滥,通過(guò)使用Log4j,我們可以控制日志信息輸送的目的地是控制臺(tái)艾船、文件葵腹、GUI組件、甚至是套接口服務(wù)器屿岂、NT的事件記錄器践宴、UNIX Syslog守護(hù)進(jìn)程等;用戶也可以控制每一條日志的輸出格式爷怀;通過(guò)定義每一條日志信息的級(jí)別阻肩,用戶能夠更加細(xì)致地控制日志的生成過(guò)程。這些可以通過(guò)一個(gè)配置文件(XML或Properties文件)來(lái)靈活地進(jìn)行配置运授,而不需要修改程序代碼烤惊。Log4j 2則是前任的一個(gè)升級(jí)乔煞,參考了Logback的許多特性;Logback
- Logback是由log4j創(chuàng)始人設(shè)計(jì)的又一個(gè)開(kāi)源日記組件撕氧。logback當(dāng)前分成三個(gè)模塊:logback-core,logback- classic和logback-access瘤缩。logback-core是其它兩個(gè)模塊的基礎(chǔ)模塊。logback-classic是log4j的一個(gè)改良版本伦泥。此外logback-classic完整實(shí)現(xiàn)SLF4J API使你可以很方便地更換成其它日記系統(tǒng)如log4j或JDK14 Logging剥啤;java.util.logging
- JDK內(nèi)置的日志接口和實(shí)現(xiàn),功能比較簡(jiǎn)不脯;Slf4j
- SLF4J是為各種Logging API提供一個(gè)簡(jiǎn)單統(tǒng)一的接口府怯,從而使用戶能夠在部署的時(shí)候配置自己希望的Logging API實(shí)現(xiàn);Apache Commons Logging
- Apache Commons Logging (JCL)希望解決的問(wèn)題和Slf4j類(lèi)似防楷。
選項(xiàng)太多了的后果就是選擇困難癥牺丙,我的看法是沒(méi)有最好的,只有最合適的复局。在比較關(guān)注性能的地方冲簿,選擇Logback或自己實(shí)現(xiàn)高性能Logging API可能更合適;在已經(jīng)使用了Log4j的項(xiàng)目中亿昏,如果沒(méi)有發(fā)現(xiàn)問(wèn)題峦剔,繼續(xù)使用可能是更合適的方式;我一般會(huì)在項(xiàng)目里選擇使用Slf4j, 如果不想有依賴(lài)則使用java.util.logging或框架容器已經(jīng)提供的日志接口角钩。
Java日志最佳實(shí)踐
定義日志變量
日志變量往往不變吝沫,最好定義成final static,變量名用大寫(xiě)递礼。
日志分級(jí)
Java的日志框架一般會(huì)提供以下日志級(jí)別惨险,缺省打開(kāi)info級(jí)別,也就是debug脊髓,trace級(jí)別的日志在生產(chǎn)環(huán)境不會(huì)輸出辫愉,在開(kāi)發(fā)和測(cè)試環(huán)境可以通過(guò)不同的日志配置文件打開(kāi)debug級(jí)別。
-
fatal
- 嚴(yán)重的将硝,造成服務(wù)中斷的錯(cuò)誤 -
error
- 其他錯(cuò)誤運(yùn)行期錯(cuò)誤 -
warn
- 警告信息恭朗,如程序調(diào)用了一個(gè)即將作廢的接口,接口的不當(dāng)使用袋哼,運(yùn)行狀態(tài)不是期望的但仍可繼續(xù)處理等 -
info
- 有意義的事件信息冀墨,如程序啟動(dòng)闸衫,關(guān)閉事件涛贯,收到請(qǐng)求事件等 -
debug
- 調(diào)試信息,可記錄詳細(xì)的業(yè)務(wù)處理到哪一步了蔚出,以及當(dāng)前的變量狀態(tài) -
trace
- 更詳細(xì)的跟蹤信息
基本的Logger編碼規(guī)范
-
在一個(gè)對(duì)象中通常只使用一個(gè)Logger對(duì)象弟翘,Logger應(yīng)該是static final的虫腋,只有在少數(shù)需要在構(gòu)造函數(shù)中傳遞logger的情況下才使用private final。
private static final Logger LOGGER=LoggerFactory.getLogger(Main.class);
-
輸出Exceptions的全部Throwable信息稀余,因?yàn)閘ogger.error(msg)和logger.error(msg,e.getMessage())這樣的日志輸出方法會(huì)丟失掉最重要的StackTrace信息悦冀。
LOGGER.error("error", e.getMessage()); //錯(cuò)誤 LOGGER.error(e.getMessage()); //錯(cuò)誤 LOGGER.error(e.getMessage(), e); //正確 LOGGER.error(e); //正確
-
不允許記錄日志后又拋出異常,因?yàn)檫@樣會(huì)多次記錄日志睛琳,只允許記錄一次日志盒蟆。
try{ // do something }catch(Exception e){ LOGGER.error(e.getMessage(), e); throw new Exception("error"); }
-
不允許出現(xiàn)System print(包括System.out.println和System.error.println)語(yǔ)句。
try{ // do something }catch(Exception e){ System.out.println(e.getMessage()); System.error.println(e.getMessage()); }
-
不允許出現(xiàn)printStackTrace师骗。
try{ // do something }catch(Exception e){ e. printStackTrace(); }
-
日志性能的考慮历等,如果代碼為核心代碼,執(zhí)行頻率非常高辟癌,則輸出日志建議增加判斷寒屯,尤其是低級(jí)別的輸出<debug、info黍少、warn>寡夹。
debug日志太多后可能會(huì)影響性能,有一種改進(jìn)方法是:
if(LOGGER.isDebugEnable()){ LOGGER.debug("do something"); }
但更好的方法是Slf4j提供的最佳實(shí)踐:
LOGGER.debug("do something {}", content);
一方面可以減少參數(shù)構(gòu)造的開(kāi)銷(xiāo)厂置,另一方面也不用多寫(xiě)兩行代碼菩掏。
-
有意義的日志
通常情況下在程序日志里記錄一些比較有意義的狀態(tài)數(shù)據(jù):程序啟動(dòng),退出的時(shí)間點(diǎn)农渊;程序運(yùn)行消耗時(shí)間患蹂;耗時(shí)程序的執(zhí)行進(jìn)度;重要變量的狀態(tài)變化砸紊。
初次之外传于,在公共的日志里規(guī)避打印程序的調(diào)試或者提示信息。