java日誌系統-- slf4j + log4j

java日誌系統-- slf4j + log4j


  在學校學習過程中,或者自己剛開始編程時,我經常通過System.out來輸出各種結果。由於程序的規模很小,再者不是線上的應用,所以這種方法簡單、快捷有效。
在學習Hadoop過程中,第一次接觸log4j,但是當時並沒有在意這些細節。
  工作後,遇到問題或者調試時會通過日誌來看程序的運行狀態,尤其是把error stack輸出後,能夠很快的定位問題,對於解決問題起到很大的作用。
  這篇文章,通過“爲什麼使用 SLF4J 而不是 Log4J 來做 Java 日誌 ”這樣的問題來帶入,再通過slf4+log4j來實踐日誌配置。其他的日誌記錄庫,如java.util.logging、logback等,待有時間在進行學習和總結。


1. slf4j-api、slf4j-log4j12以及log4j之間的關係

    slf4j : Simple Logging Facade for Java,爲java提供的簡單日誌門面,更底層一點說就是接口。允許用戶以自己的洗好,在工程中通過slf4j接入不同的日誌系統。更直觀一點,slf4j是個數據線,一端嵌入程序,另一端連接日誌系統,從而實現將程序中的信息導入到日誌系統並記錄。
    
    由此可見,slf4j就是衆多接口的集合,不負責具體的日誌實現,只在編譯時負責尋找合適的日誌系統進行綁定。具體的接口全部都在slf4j-api中定義。
    下圖比較清晰地描述他們之間的關係。

 (1) 應用層使用slf4j-api作爲日誌接入的接口;
    (2) 編譯時,slf4j-api中public final class LoggerFactory類中 private final static void bind()方法會尋找具體的日誌實現類綁定,主要通過 StaticLoggerBinder.getSingleton() 語句調用。
    (3) slf4j-log4j12 是連接slf4j-api 和log4j的中間適配器,它實現了slf4j-api中StaticLoggerBinder接口,從而使得在編譯時綁定的是slf4j-log4j12的getSingleton()方法。
    (4) log4j是具體的日誌系統,通過slf4j-log4j12初始化log4j,達到最終日誌輸出。


2. 爲什麼使用 SLF4J 而不是 Log4J 來做 Java 日誌

(1) 最爲重要的原因

    由第一個問題,我們可以知道slf4j就是各種接口的集合,對外暴露相同的接口,用戶可以使用自己指定的日誌系統。一句話總結,slf4j讓你的代碼獨立於任何特定的日誌系統API。
    這樣的特性,尤其適合於公共的庫的開發。例如,你的工程使用log4j,需要包含一個common-utils的庫,而這個庫還依賴logback日誌記錄庫,那麼你的工程還需要包含他們。然而,如果common-utils庫使用slf4j,那麼你可以繼續使用你的日誌記錄庫,而不需要痛哭地添加和維護另一個新的日誌記錄框架。

(2) 佔位符 {}

    佔位符功能與String.format()方法中的%s非常近似,因爲他在運行時才提取所提供的真正的字符串,而且這個佔位符一般不需要指定參數的類型(相當方便,不過如果需要輸出指定格式,還是需要自己拼接)。減少了字符串的拼接,進而減少String對象所需要的資源。

(3) isXXXEnabled()

    在使用log4j時,由於java沒有那麼方便的文檔級別的預編譯方法,如下的代碼是不是很無聊?

if (logger.isDebugEnabled()) {
    logger.debug("Processing trade with id: " + id + " symbol: " + symbol);
}

    而使用slf4j,可以更簡潔的達到同樣的效果,提高代碼的可讀性。

logger.debug("Processing trade with id: {} and symbol : {} ", id, symbol);


下面的SLF4J日誌方法的代碼,來自於slf4j-log4j12-1.6.1.jar包裏的Log4j的適配器類Log4jLoggerAdapter.

public void debug(String format, Object arg1, Object arg2) { if (logger.isDebugEnabled()) {
      FormattingTuple ft = MessageFormatter.format(format, arg1, arg2);
      logger.log(FQCN, Level.DEBUG, ft.getMessage(), ft.getThrowable());
    }
}

3. slf4j + log4j來做日誌

(1) 引用

    通過上面的介紹,除了引用slf4j之外,還需要作爲適配器的slf4j-log4j12和具體實現的log4j的jar包。具體版本需要看使用的log4j版本和slf4j版本。
    如果通過Maven來管理項目依賴,那就簡單多了,只需要在pom文件中引用slf4j-log4j12的後,編譯時會自動引入它所依賴的其他jar包。

    <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-log4j12</artifactId>
        <version>1.6.1</version>
    </dependency> 

(2) log4j的配置

 log4j可以通過程序代碼配置,也可以通過配置文件配置,顯然配置文件的方式更加靈活。
   log4j支持兩種配置文件格式,一種是XML格式的文件,一種是java properties(key=value)。配置文件需要放置在classpath路徑下,而且XML文件的優先級更高

    key/value形式的更有助於說明,這裏使用properties文件作爲例子介紹log4j的配置。

   
a. 配置根Logger: 
        
log4j.rootLogger = [ level ] , appenderName, appenderName, …
        其中level是日誌記錄的優先級,appenderName是定義的Appenders,可以指定多個輸出目的地。

    b. 配置Appender:
log4j.appender.appenderName = fully.qualified.name.of.appender.class
log4j.appender.appenderName.option1 = value1
…
log4j.appender.appenderName.option = valueN

        Appender常用屬性:
        Threshold=WARN        - 指定日誌級別
        ImmediateFlush=true   - 默認true,立即輸出
        Target=System.err    -  默認情況下是:System.out,指定輸出控制檯
        File=mylog.txt              - 指定消息輸出到mylog.txt文件
        Append=false              - 默認true,指定是否追加
        MaxFileSize=100KB    - 後綴可以是KB, MB 或者是 GB,指定日誌文件的大小,到達時,將會自動滾動,原來的內容放置到mylog.log.1文件中
        MaxBackupIndex=2       - 指定滾動文件的最大值
        DatePattern='.yyyy-ww'  - 每週滾動一次。
            1)'.'yyyy-MM: 每月
            2)'.'yyyy-ww: 每週 
            3)'.'yyyy-MM-dd: 每天
            4)'.'yyyy-MM-dd-a: 每天兩次
            5)'.'yyyy-MM-dd-HH: 每小時
            6)'.'yyyy-MM-dd-HH-mm: 每分鐘
        
    c. 配置Layout:    Layout是和Appender綁定的,不同的Appender可以有不同的輸出格式
     
log4j.appender.appenderName.layout = fully.qualified.name.of.layout.class
log4j.appender.appenderName.layout.option1 = value1
…
log4j.appender.appenderName.layout.option = valueN

    當使用org.apache.log4j.PatternLayout來自定義信息格式時,可以使用 log4j.appender.A1.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %p -%m%n 來格式化信息 
    %c 輸出所屬類的全名,可寫爲 %c{Num} ,Num類名輸出的範圍 如:"com.sun.aaa.classB", %C{2}將使日誌輸出輸出範圍爲:aaa.classB 
    %d 輸出日誌時間其格式爲 可指定格式 如 %d{HH:mm:ss}等 
    %l 輸出日誌事件發生位置,包括類目名、發生線程,在代碼中的行數 
    %n 換行符 
    %m 輸出代碼指定信息,如info(“message”),輸出message 
    %p 輸出日誌的優先級,即 FATAL ,ERROR 等 
    %r 輸出從啓動到顯示該條日誌信息所耗費的時間(毫秒數) 
    %t 輸出產生該日誌事件的線程名

[%-5p %d{yyyy-MM-dd HH:mm:ss.SSS}] [%t] %l [%m]%n
是我們使用的日誌格式。


    對於xml的配置,通過引用和參考的形式給出,
    http://www.cnblogs.com/kevin-yuan/archive/2012/11/23/2784610.html
    https://www.mkyong.com/logging/log4j-xml-example/



引用和參考
http://woshixy.blog.51cto.com/5637578/1371420
https://www.oschina.net/translate/why-use-sl4j-over-log4j-for-logging
http://blog.csdn.net/yycdaizi/article/details/8276265

//配置
https://my.oschina.net/exit/blog/182445
https://www.mkyong.com/logging/log4j-xml-example/
http://www.cnblogs.com/ITEagle/archive/2010/04/23/1718365.html

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章