Java日誌

對於一個應用程序來說日誌記錄是必不可少的一部分。線上問題追蹤,基於日誌的業務邏輯統計分析等都離不日誌。java領域存在多種日誌框架,目前常用的日誌框架包括Log4j 1,Log4j 2,Commons Logging,Slf4j,Logback,Jul。

參考:

https://www.cnblogs.com/chenhongliang/p/5312517.html

https://www.jianshu.com/p/190c56429ec4

Java常用日誌框架類別

  • Log4j Apache Log4j是一個基於Java的日誌記錄工具。它是由Ceki Gülcü首創的,現在則是Apache軟件基金會的一個項目。 Log4j是幾種Java日誌框架之一。

  • Log4j 2 Apache Log4j 2是apache開發的一款Log4j的升級產品。

  • Commons Logging Apache基金會所屬的項目,是一套Java日誌接口,之前叫Jakarta Commons Logging,後更名爲Commons Logging。

  • Slf4j 類似於Commons Logging,是一套簡易Java日誌門面,本身並無日誌的實現。(Simple Logging Facade for Java,縮寫Slf4j)。

  • Logback 一套日誌組件的實現(Slf4j陣營)。

  • Jul (Java Util Logging),自Java1.4以來的官方日誌實現。

看了上面的介紹是否會覺得比較混亂,這些日誌框架之間有什麼異同,都是由誰在維護,在項目中應該如何選擇日誌框架,應該如何使用? 下文會逐一介紹。

Java常用日誌框架歷史

  • 1996年早期,歐洲安全電子市場項目組決定編寫它自己的程序跟蹤API(Tracing API)。經過不斷的完善,這個API終於成爲一個十分受歡迎的Java日誌軟件包,即Log4j。後來Log4j成爲Apache基金會項目中的一員。

  • 期間Log4j近乎成了Java社區的日誌標準。據說Apache基金會還曾經建議Sun引入Log4j到java的標準庫中,但Sun拒絕了。

  • 2002年Java1.4發佈,Sun推出了自己的日誌庫JUL(Java Util Logging),其實現基本模仿了Log4j的實現。在JUL出來以前,Log4j就已經成爲一項成熟的技術,使得Log4j在選擇上佔據了一定的優勢。

  • 接着,Apache推出了Jakarta Commons Logging,JCL只是定義了一套日誌接口(其內部也提供一個Simple Log的簡單實現),支持運行時動態加載日誌組件的實現,也就是說,在你應用代碼裏,只需調用Commons Logging的接口,底層實現可以是Log4j,也可以是Java Util Logging。

  • 後來(2006年),Ceki Gülcü不適應Apache的工作方式,離開了Apache。然後先後創建了Slf4j(日誌門面接口,類似於Commons Logging)和Logback(Slf4j的實現)兩個項目,並回瑞典創建了QOS公司,QOS官網上是這樣描述Logback的:The Generic,Reliable Fast&Flexible Logging Framework(一個通用,可靠,快速且靈活的日誌框架)。

  • 現今,Java日誌領域被劃分爲兩大陣營:Commons Logging陣營和Slf4j陣營。
    Commons Logging在Apache大樹的籠罩下,有很大的用戶基數。但有證據表明,形式正在發生變化。2013年底有人分析了GitHub上30000個項目,統計出了最流行的100個Libraries,可以看出Slf4j的發展趨勢更好:

    java_populor_jar

  • Apache眼看有被Logback反超的勢頭,於2012-07重寫了Log4j 1.x,成立了新的項目Log4j 2, Log4j 2具有Logback的所有特性。

java常用日誌框架關係

  • Log4j 2與Log4j 1發生了很大的變化,Log4j 2不兼容Log4j 1。

  • Commons Logging和Slf4j是日誌門面(門面模式是軟件工程中常用的一種軟件設計模式,也被稱爲正面模式、外觀模式。它爲子系統中的一組接口提供一個統一的高層接口,使得子系統更容易使用)。Log4j和Logback則是具體的日誌實現方案。可以簡單的理解爲接口與接口的實現,調用者只需要關注接口而無需關注具體的實現,做到解耦。

  • 比較常用的組合使用方式是Slf4j與Logback組合使用,Commons Logging與Log4j組合使用。

  • Logback必須配合Slf4j使用。由於Logback和Slf4j是同一個作者,其兼容性不言而喻。

Commons Logging與Slf4j實現機制對比

Commons Logging實現機制

Commons Logging是通過動態查找機制,在程序運行時,使用自己的ClassLoader尋找和載入本地具體的實現。詳細策略可以查看commons-logging-*.jar包中的org.apache.commons.logging.impl.LogFactoryImpl.java文件。由於Osgi不同的插件使用獨立的ClassLoader,Osgi的這種機制保證了插件互相獨立, 其機制限制了Commons Logging在Osgi中的正常使用。

Slf4j實現機制

Slf4j在編譯期間,靜態綁定本地的Log庫,因此可以在Osgi中正常使用。它是通過查找類路徑下org.slf4j.impl.StaticLoggerBinder,然後在StaticLoggerBinder中進行綁定。

項目中選擇日誌框架選擇

如果是在一個新的項目中建議使用Slf4j與Logback組合,這樣有如下的幾個優點。

  • Slf4j實現機制決定Slf4j限制較少,使用範圍更廣。由於Slf4j在編譯期間,靜態綁定本地的LOG庫使得通用性要比Commons Logging要好。

  • Logback擁有更好的性能。Logback聲稱:某些關鍵操作,比如判定是否記錄一條日誌語句的操作,其性能得到了顯著的提高。這個操作在Logback中需要3納秒,而在Log4J中則需要30納秒。LogBack創建記錄器(logger)的速度也更快:13毫秒,而在Log4J中需要23毫秒。更重要的是,它獲取已存在的記錄器只需94納秒,而Log4J需要2234納秒,時間減少到了1/23。跟JUL相比的性能提高也是顯著的。

  • Commons Logging開銷更高

# 在使Commons Logging時爲了減少構建日誌信息的開銷,通常的做法是
if(log.isDebugEnabled()){
  log.debug("User name: " +
    user.getName() + " buy goods id :" + good.getId());
}

# 在Slf4j陣營,你只需這麼做:
log.debug("User name:{} ,buy goods id :{}", user.getName(),good.getId());

# 也就是說,Slf4j把構建日誌的開銷放在了它確認需要顯示這條日誌之後,減少內存和Cup的開銷,使用佔位符號,代碼也更爲簡潔
  • Logback文檔免費。Logback的所有文檔是全面免費提供的,不象Log4J那樣只提供部分免費文檔而需要用戶去購買付費文檔。

Slf4j用法

Slf4j與其它日誌組件的關係說明

  • Slf4j的設計思想比較簡潔,使用了Facade設計模式,Slf4j本身只提供了一個slf4j-api-version.jar包,這個jar中主要是日誌的抽象接口,jar中本身並沒有對抽象出來的接口做實現。
  • 對於不同的日誌實現方案(例如Logback,Log4j...),封裝出不同的橋接組件(例如logback-classic-version.jar,slf4j-log4j12-version.jar),這樣使用過程中可以靈活的選取自己項目裏的日誌實現。

Slf4j與其它日誌組件調用關係圖

slf4j-bind

Slf4j與其他各種日誌組件的橋接說明

jar包名 說明
slf4j-log4j12-1.7.13.jar Log4j1.2版本的橋接器,你需要將Log4j.jar加入Classpath。
slf4j-jdk14-1.7.13.jar java.util.logging的橋接器,Jdk原生日誌框架。
slf4j-nop-1.7.13.jar NOP橋接器,默默丟棄一切日誌。
slf4j-simple-1.7.13.jar 一個簡單實現的橋接器,該實現輸出所有事件到System.err. 只有Info以及高於該級別的消息被打印,在小型應用中它也許是有用的。
slf4j-jcl-1.7.13.jar Jakarta Commons Logging 的橋接器. 這個橋接器將Slf4j所有日誌委派給Jcl。
logback-classic-1.0.13.jar(requires logback-core-1.0.13.jar) Slf4j的原生實現,Logback直接實現了Slf4j的接口,因此使用Slf4j與Logback的結合使用也意味更小的內存與計算開銷
  • 具體的接入方式參見下圖
    slf4j-concrete-bindings1

 

 

 

1.日誌體系

 

日誌分層


日誌體系大致如上圖所示,我們的系統會直接與接口層交互。當然也可以直接使用具體的日誌實現,比如logback,但是按照面向接口編程的理念,建議不要在系統中直接使用具體日誌系統的代碼,否則後續若要更換日誌系統,會相當麻煩。

 

2.bridge層

 

 

先盜slf4j官網一張圖

concrete-bindings.png

 

其中Adaptation layer是bridge層。爲什麼需要bridge?其實slf4j(slf4j-api.jar)只提供了一個門面,並沒有具體的實現。像最左邊的那一列,如果我們的系統中只引入了slf4j-api.jar,那麼日誌無法輸出。若想正常輸出日誌,還需引入真正寫日誌到文件的jar包。

圖中深藍色的日誌框架並不需要bridge層就可以使用slf4j進行日誌打印,原因在於這些日誌框架都直接實現了slf4j。而log4j和jdk log並沒有實現slf4j(廢話,jdk的log怎麼可能依賴於第三方的框架。。。),因此需要一箇中間層去轉化一下。

總結一下,充當bridge層的jar包:slf4j-log412.jar 和 slf4j-jdk14.jar

3.其他框架轉接到slf4j

 

 

也是先盜圖一張

legacy.png

 

假設系統中使用了jcl作爲了門面,那麼對jcl api的調用如何轉化爲對slf4j api的調用?(常見的Spring框架的日誌框架就是jcl)

slf4j提供了jar包將別的日誌api的調用轉調到slf4j的api上。
jcl-over-slf4j.jar : jcl >>> slf4j
log4j-over-slf4j.jar : log4j >>> slf4j
jul-to-slf4j.jar : jdk api >>> slf4j
前兩個包需要分別替換commons-logging.jar和log4j.jar。

在上述橋接、轉換過程中,有一個限制就是轉調到slf4j的日誌框架不能與當前slf4j橋接的日誌框架相同。舉個例子,系統使用slf4j和log4j打印日誌,我們又引入了log4j-over-slf4j.jar去把log4j轉調到slf4j上,這就會出現如下遞歸調用情況:
業務系統 ---> slf4j api ---> log4j api --->轉調到 slf4j api ---> log4j api --->....

4.log4j2與slf4j

先區別幾個包
slf4j-log4j12.jar: slf4j提供的slf4j到log4j1.x的bridge;
log4j-over-slf4j.jar: slf4j提供的log4j轉調slf4j,一般是在業務系統直接調用了log4j的api,但是想轉調到slf4j,再通過別的日誌框架進行日誌輸出的情況;
log4j-slf4j-impl.jar: log4j2提供的slf4j到log4j2.x的bridge;

5.總結

若業務系統中使用的是日誌門面,則參考concrete-bindings.png;
若業務系統沒有使用日誌門面,但是想更換爲別的具體日誌系統,則參考legacy.png,先轉調到slf4j api,再通過別的日誌系統輸出日誌。



 

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