一個在生產環境裏運行的程序若是沒有日誌是很讓維護者提心吊膽的,有太多雜亂又無心義的日誌也是使人傷神。程序出現問題時候,從日誌裏若是發現不了問題可能的緣由是很使人受挫的。本文想討論的是如何在Java程序裏寫好日誌。html
通常來講日誌分爲兩種:業務日誌和異常日誌,使用日誌咱們但願能達到如下目標:java
Java的日誌框架太多了。。。apache
選項太多了的後果就是選擇困難症,個人見解是沒有最好的,只有最合適的。在比較關注性能的地方,選擇Logback或本身實現高性能Logging API可能更合適;在已經使用了Log4j的項目中,若是沒有發現問題,繼續使用多是更合適的方式;我通常會在項目裏選擇使用Slf4j, 若是不想有依賴則使用java.util.logging或框架容器已經提供的日誌接口。api
日誌變量每每不變,最好定義成final static,變量名用大寫。緩存
Java的日誌框架通常會提供如下日誌級別,缺省打開info級別,也就是debug,trace級別的日誌在生產環境不會輸出,在開發和測試環境能夠經過不一樣的日誌配置文件打開debug級別。服務器
在程序裏要合理使用日誌分級:oracle
1 LOGGER.debug("entering getting content"); 2 String content =CacheManager.getCachedContent(); 3 if(content == null){ 4 5 //使用warn,由於程序還能夠繼續執行,但相似警告太多可能說明緩存服務不可用了,值得注意 6 LOGGER.warn("Got empty content from cache,need perform database lookup"); 7 8 Connection conn = ConnectionFactory.getConnection(); 9 if (conn=null) { 10 LOGGER.error("Can't get database connection, failed to return content");//儘可能提供詳細的信息,知道錯誤的緣由,而不能簡單的寫logger.error("failed") 11 }else{ 12 try{ 13 content = conn.query(...); 14 }catch ( IOException e ){ 15 //異常要記錄錯誤堆棧 16 LOGGER.error("Failed to perform database lookup", e ); 17 }finally{ 18 ConnectionFactory.releaseConnection(conn); 19 } 20 } 21 } 22 //調試的時候能夠知道方法的返回了 23 LOGGER.debug("returning content: "+ content); 24 return content;
1.在一個對象中一般只使用一個Logger對象,Logger應該是static final的,只有在少數須要在構造函數中傳遞logger的狀況下才使用private final。框架
static final logger_LOG=loggerFactory.getLogger(Main.class);
2.輸出Exceptions的所有Throwable信息,由於logger.error(msg)和logger.error(msg,e.getMessage())這樣的日誌輸出方法會丟失掉最重要的StackTrace信息。函數
void foo(){ try { // do something... }catch ( Exception e ){ _LOG.error(e.getMessage()); // 錯誤 _LOG.error("Bad things : ",e.getMessage()); // 錯誤 _LOG.error("Bad things : ",e); // 正確 } }
3.不容許記錄日誌後又拋出異常,由於這樣會屢次記錄日誌,只容許記錄一第二天志。性能
void foo() throws LogException{ try{ // do something... }catch ( Exception e ){ _LOG.error("Bad things : ", e); throw new LogException("Bad things : ",e); } }
4.不容許出現System print(包括System.out.println和System.error.println)語句。
void foo() { try{ // do something... }catch( Exception e ){ System.out.println(e.getMessage()); // 錯誤 System.err.println(e.getMessage()); // 錯誤 _LOG.error("Bad things : ",e ); // 正確 } }
5.不容許出現printStackTrace。
void foo() { try { // do something... }catch ( Exception e ) { e.printStackTrace(); // 錯誤 _LOG.error("Bad things : ", e ); //正確 } }
6.日誌性能的考慮,若是代碼爲核心代碼,執行頻率很是高,則輸出日誌建議增長判斷,尤爲是低級別的輸出<debug、info、warn>。
debug日誌太多後可能會影響性能,有一種改進方法是:
if (LOGGER.isDebugEnabled ()) { LOGGER.debug("returning content: "+ content); }
但更好的方法是Slf4j提供的最佳實踐:
LOGGER.debug("returning content: {}", content);
一方面能夠減小參數構造的開銷,另外一方面也不用多寫兩行代碼。
7.有意義的日誌
一般狀況下在程序日誌裏記錄一些比較有意義的狀態數據:程序啓動,退出的時間點;程序運行消耗時間;耗時程序的執行進度;重要變量的狀態變化。
除此以外,在公共的日誌裏規避打印程序的調試或者提示信息。
FAQ:參考