一、日志框架的分類
- 門面型日志框架:
- JCL: Apache基金會所屬的項目,是一套Java日志接口,之前叫Jakarta Commons Logging,后更名為Commons Logging
- SLF4J: 是一套簡易Java日志門面,本身並無日志的實現。(Simple Logging Facade for Java,縮寫Slf4j)
- 記錄型日志框架:
- JUL: JDK中的日志記錄工具,也常稱為JDKLog、jdk-logging,自Java1.4以來的官方日志實現。
- Log4j: 一個具體的日志實現框架。
- Log4j2: 一個具體的日志實現框架,是LOG4J1的下一個版本,與Log4j 1發生了很大的變化,Log4j 2不兼容Log4j 1。
- Logback:一個具體的日志實現框架,和Slf4j是同一個作者,但其性能更好。
二、發展歷程
要搞清楚它們的關系,就要從它們是在什么情況下產生的說起。我們按照時間的先后順序來介紹。
Log4j
在JDK 1.3及以前,Java打日志依賴System.out.println(), System.err.println()或者e.printStackTrace(),Debug日志被寫到STDOUT流,錯誤日志被寫到STDERR流。這樣打日志有一個非常大的缺陷,即無法定制化,且日志粒度不夠細。
於是, Gülcü 於2001年發布了Log4j,后來成為Apache 基金會的頂級項目。Log4j 在設計上非常優秀,對后續的 Java Log 框架有長久而深遠的影響,它定義的Logger、Appender、Level等概念如今已經被廣泛使用。Log4j 的短板在於性能,在Logback 和 Log4j2 出來之后,Log4j的使用也減少了。
J.U.L
受Logj啟發,Sun在Java1.4版本中引入了java.util.logging,但是j.u.l功能遠不如log4j完善,開發者需要自己編寫Appenders(Sun稱之為Handlers),且只有兩個Handlers可用(Console和File),j.u.l在Java1.5以后性能和可用性才有所提升。
JCL(commons-logging)
由於項目的日志打印必然選擇兩個框架中至少一個,這時候,Apache的JCL(commons-logging)誕生了。JCL 是一個Log Facade,只提供 Log API,不提供實現,然后有 Adapter 來使用 Log4j 或者 JUL 作為Log Implementation。
在程序中日志創建和記錄都是用JCL中的接口,在真正運行時,會看當前ClassPath中有什么實現,如果有Log4j 就是用 Log4j, 如果啥都沒有就是用 JDK 的 JUL。
這樣,在你的項目中,還有第三方的項目中,大家記錄日志都使用 JCL 的接口,然后最終運行程序時,可以按照自己的需求(或者喜好)來選擇使用合適的Log Implementation。如果用Log4j, 就添加 Log4j 的jar包進去,然后寫一個 Log4j 的配置文件;如果喜歡用JUL,就只需要寫個 JUL 的配置文件。如果有其他的新的日志庫出現,也只需要它提供一個Adapter,運行的時候把這個日志庫的 jar 包加進去。
不過,commons-logging對Log4j和j.u.l的配置問題兼容的並不好,使用commons-loggings還可能會遇到類加載問題,導致NoClassDefFoundError的錯誤出現。

到這個時候一切看起來都很簡單,很美好。接口和實現做了良好的分離,在統一的JCL之下,不改變任何代碼,就可以通過配置就換用功能更強大,或者性能更好的日志庫實現。
這種簡單美好一直持續到SLF4J出現。
SLF4J & Logback
SLF4J(Simple Logging Facade for Java)和 Logback 也是Gülcü 創立的項目,目的是為了提供更高性能的實現。
從設計模式的角度說,SLF4J 是用來在log和代碼層之間起到門面作用,類似於 JCL 的 Log Facade。對於用戶來說只要使用SLF4J提供的接口,即可隱藏日志的具體實現,SLF4J提供的核心API是一些接口和一個LoggerFactory的工廠類,用戶只需按照它提供的統一紀錄日志接口,最終日志的格式、紀錄級別、輸出方式等可通過具體日志系統的配置來實現,因此可以靈活的切換日志系統。
Logback是log4j的升級版,當前分為三個目標模塊:
- logback-core:核心模塊,是其它兩個模塊的基礎模塊
- logback-classic:是log4j的一個改良版本,同時完整實現 SLF4J API 使你可以很方便地更換成其它日記系統如log4j 或 JDK14 Logging
- logback-access:訪問模塊與Servlet容器集成提供通過Http來訪問日記的功能,是logback不可或缺的組成部分
Logback相較於log4j有更多的優點:
- 更快的執行速度
- 更充分的測試
- logback-classic 非常自然的實現了SLF4J
- 使用XML配置文件或者Groovy
- 自動重新載入配置文件
- 優雅地從I/O錯誤中恢復
- 自動清除舊的日志歸檔文件
- 自動壓縮歸檔日志文件
- 謹慎模式
- Lilith
- 配置文件中的條件處理
- 更豐富的過濾
更詳細的解釋參見官網:https://logback.qos.ch/reasonsToSwitch.html
到這里,你可能會問:Apache 已經有了個JCL,用來做各種Log lib統一的接口,如果 Gülcü 要搞一個更好的 Log 實現的話,直接寫一個實現就好了,為啥還要搞一個和SLF4J呢?
原因是Gülcü 認為 JCL 的 API 設計得不好,容易讓使用者寫出性能有問題的代碼。關於這點,你可以參考這篇文章獲得更詳細的介紹:https://zhuanlan.zhihu.com/p/24272450
現在事情就變復雜了。我們有了兩個流行的 Log Facade,以及三個流行的 Log Implementation。Gülcü 是個追求完美的人,他決定讓這些Log之間都能夠方便的互相替換,所以做了各種 Adapter 和 Bridge 來連接:

可以看到甚至 Log4j 和 JUL 都可以橋接到SLF4J,再通過 SLF4J 適配到到 Logback!需要注意的是不能有循環的橋接,比如下面這些依賴就不能同時存在:
- jcl-over-slf4j 和 slf4j-jcl
- log4j-over-slf4j 和 slf4j-log4j12
- jul-to-slf4j 和 slf4j-jdk14
然而,事情在變得更麻煩!
Log4j2
現在有了更好的 SLF4J 和 Logback,慢慢取代JCL 和 Log4j ,事情到這里總該大統一圓滿結束了吧。然而維護 Log4j 的人不這樣想,他們不想坐視用戶一點點被 SLF4J / Logback 蠶食,繼而搞出了 Log4j2。
Log4j2 和 Log4j1.x 並不兼容,設計上很大程度上模仿了 SLF4J/Logback,性能上也獲得了很大的提升。Log4j2 也做了 Facade/Implementation 分離的設計,分成了 log4j-api 和 log4j-core。
現在好了,我們有了三個流行的Log 接口和四個流行的Log實現,如果畫出橋接關系的圖來回事什么樣子呢?

看到這里是不是感覺有點暈呢?是的,我也有這種感覺。同樣,在添加依賴的時候,要小心不要有循環依賴。