Java日誌規範

       前言

一個在生產環境裏運行的程序若是沒有日誌是很讓維護者提心吊膽的,有太多雜亂又無心義的日誌也是使人傷神。程序出現問題時候,從日誌裏若是發現不了問題可能的緣由是很使人受挫的。本文想討論的是如何在Java程序裏寫好日誌。html

通常來講日誌分爲兩種:業務日誌和異常日誌,使用日誌咱們但願能達到如下目標:java

  1. 對程序運行狀況的記錄和監控;
  2. 在必要時可詳細瞭解程序內部的運行狀態;
  3. 對系統性能的影響儘可能小

    Java日誌框架

    Java的日誌框架太多了。。。apache

    1. Log4j 或 Log4j 2 - Apache的開源項目,經過使用Log4j,咱們能夠控制日誌信息輸送的目的地是控制檯、文件、GUI組件、甚至是套接口服務器、NT的事件記錄器、UNIX Syslog守護進程等;用戶也能夠控制每一條日誌的輸出格式;經過定義每一條日誌信息的級別,用戶可以更加細緻地控制日誌的生成過程。這些能夠經過一個配置文件(XML或Properties文件)來靈活地進行配置,而不須要修改程序代碼。Log4j 2則是前任的一個升級,參考了Logback的許多特性;
    2. Logback - Logback是由log4j創始人設計的又一個開源日記組件。logback當前分紅三個模塊:logback-core,logback- classic和logback-access。logback-core是其它兩個模塊的基礎模塊。logback-classic是log4j的一個改良版本。此外logback-classic完整實現SLF4J API使你能夠很方便地更換成其它日記系統如log4j或JDK14 Logging;
    3. java.util.logging - JDK內置的日誌接口和實現,功能比較簡;
    4. Slf4j - SLF4J是爲各類Logging API提供一個簡單統一的接口),從而使用戶可以在部署的時候配置本身但願的Logging API實現;
    5. Apache Commons Logging - Apache Commons Logging (JCL)但願解決的問題和Slf4j相似。

    選項太多了的後果就是選擇困難症,個人見解是沒有最好的,只有最合適的。在比較關注性能的地方,選擇Logback或本身實現高性能Logging API可能更合適;在已經使用了Log4j的項目中,若是沒有發現問題,繼續使用多是更合適的方式;我通常會在項目裏選擇使用Slf4j, 若是不想有依賴則使用java.util.logging或框架容器已經提供的日誌接口。api

    3.Java日誌最佳實踐

    定義日誌變量

    日誌變量每每不變,最好定義成final static,變量名用大寫緩存

    日誌分級

    Java的日誌框架通常會提供如下日誌級別,缺省打開info級別,也就是debug,trace級別的日誌在生產環境不會輸出,在開發和測試環境能夠經過不一樣的日誌配置文件打開debug級別。服務器

    1. fatal - 嚴重的,形成服務中斷的錯誤;
    2. error - 其餘錯誤運行期錯誤;
    3. warn - 警告信息,如程序調用了一個即將做廢的接口,接口的不當使用,運行狀態不是指望的但仍可繼續處理等;
    4. info - 有意義的事件信息,如程序啓動,關閉事件,收到請求事件等;
    5. debug - 調試信息,可記錄詳細的業務處理到哪一步了,以及當前的變量狀態;
    6. trace - 更詳細的跟蹤信息;

    在程序裏要合理使用日誌分級: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;

    基本的Logger編碼規範

           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:參考

1.7 Good Rules to Log Exceptions

2.5 common log mistakes

3.Java程序的日誌

相關文章
相關標籤/搜索