日誌是Java程序員日常開發當中必須要接觸的一個環節。Java的日誌五花八門,有各種各樣的實現,現在梳理一下Java日誌相關的框架,及其使用使用細節。我們在日常使用過程中,主要會接觸到SLF4J、Logback、Log4j1、Log4j2、JCL和J.U.L等日誌框架。
對於日誌框架的最佳實踐,推薦使用使用Log Facade,而不是具體Log Implementation,即在代碼中直接使用 Log4j-API 或者 SLF4J(不推薦繼續使用 JCL)的類,而不推薦使用Log4j1,Log4j2,Logback或者J.U.L的類(配置文件當中配置好即可)。
Log4j-API/Log4j-core類似於SLF4J/Logback,在Log4j2的官方文檔當中宣稱比Logback性能高很多LINK,其實現方案類似於SLF4J/Logback,因此,在這裏主要分析SLF4J與不同的日誌框架的關聯使用情況。
1. SLF4J承接不同的日誌框架(日誌框架—->SLF4J)
當項目是使用多種日誌API時,可以統一適配到SLF4J,中間使用SLF4J或者第三方提供的日誌適配器適配到SLF4J,SLF4J再底層用開發者想用的一個日誌框架來進行日誌系統的實現,從而達到了多種日誌的統一實現。其中的技術實現大體有一下兩種方式:
- 重寫上游類的實現。比如jcl-over-slf4j和log4j-over-slf4j,爲了承接log4j 1,重寫了log4j 1 的Logger和LogFactory類。
- 對接上游類的擴展方案。比如jul-to-slf4j和log4j-to-slf4j,爲了承接J.U.L,實現了繼承java.util.logging.Handler的SLF4JBridgeHandler。
適配器名稱 | 原日框架 | 提供方 | 刪除的依賴 | 實現方式 | 備註 |
---|---|---|---|---|---|
jcl-over-slf4j | apache commons-logging | SLF4J | API:刪除commons-logging 實現:刪除非SLF4J實現的實現類,如SLF4J採用Logback的實現,那麼就需要刪除Log4j1等實現 |
jcl-over-slf4j重寫了commons-logging的Log和LogFactory類,做了不同的實現 | JCL 是一個Log Facade,只提供 Log API,不提供實現,然後有 Adapter 來使用 Log4j 或者 JUL 作爲Log Implementation,是Ceki Gulcu 認爲 JCL 的 API 設計得不好,容易讓使用者寫出性能有問題的代碼,所以重新設計了SLF4J替代JCL。 |
jul-to-slf4j | java jdk-logging | SLF4J | API:刪除非SLF4J之外的其他Log Facade | jul-to-slf4j 下有 SLF4JBridgeHandler實現,系統啓動的時候調用SLF4JBridgeHandler.removeHandlersForRootLogger();刪除所有的Logger,然後調用SLF4JBridgeHandler.install();裝載上SLF4J | JUL是JDK自帶的log功能,雖然是官方自帶的log lib,但是由於性能問題和功能等問題,使用不廣泛 |
log4j-over-slf4j | apache log4j 1 | SLF4J | API:刪除非SLF4J之外的其他Log Facade 實現:刪除非SLF4J實現的實現類,如SLF4J採用Logback的實現,那麼就需要刪除Log4j1等實現 |
log4j-over-slf4j 重寫了log4j 1 的Logger和LogFactory類,做了不同的實現 | Log4j 在設計上非常優秀,對後續的 Java Log 框架有長久而深遠的影響。Log4j 的短板在於性能,在Logback 和 Log4j2 出來之後,Log4j的使用也減少了 |
log4j-to-slf4j | apache Log4j 2 | Log4j 2 | API:刪除非SLF4J之外的其他Log Facade 實現:刪除非SLF4J實現的實現類,如SLF4J採用Logback的實現,那麼就需要刪除Log4j2等實現 |
log4j-to-slf4j 使用OSGI SPI的形式爲org.apache.logging.log4j.spi.Provider提供了SLF4J的實現 | Log4j 2的性能很好,是未來之星!官方文檔 |
2. SLF4J對接不同的日誌框架實現(SLF4J—->日誌框架)
SLF4J適配不同的日誌實現,是通過不同的適配器實現的。
- 在1.8.0-alpha0以前版本採用實現org.slf4j.impl.StaticLoggerBinder的方式實現,
- 在1.8.0-alpha0之後的版本採用提供org.slf4j.spi.SLF4JServiceProvider的SPI實現的方式實現。
顯然,第一中方式不夠優雅,所以Ceki Gulcu在2017年3月21日對SFL4J的提交過程當中刪除org.slf4j.impl.StaticLoggerBinder,增加了org.slf4j.spi.SLF4JServiceProvider,將原來的查找實現的方式變成了查找SPI服務的方式,但是最新的SPI方案並沒有做對久的org.slf4j.impl.StaticLoggerBinder方式的兼容,這個動作欠妥的,比如SLF4J與Logback的配合上就有問題,類似的問題會出現在其他日誌實現當中。
SLF4J 1.8.0以前版本 | SLF4J 1.8.0以後版本 |
---|---|
Logback 1.3.0以前版本 | 正常使用 |
Logback 1.3.0以後版本 | 無法正常使用 |
log4j-slf4j-impl是的Log4j 2提供的對SLF4J的實現,由於是第三方提供的方案,所以其採用了實現org.slf4j.impl.StaticLoggerBinder的方案進行擴展。
2.1 適配器名稱說明
適配器名稱 | 目標實現 | 提供方 | 備註 |
---|---|---|---|
slf4j-jdk14 | jdk-logging J.U.L | SLF4J | |
logback-classic | Logback | Logback | Logback與SLF4J都是Ceki Gulcu的作品,Logback默認帶SLF4J的適配 |
slf4j-jcl | apache commons-logging | SLF4J | |
slf4j-log4j12 | Log4j 1 | SLF4J | |
log4j-slf4j-impl | Log4j 2 | Log4j 2 | 官方文檔 |