(轉)Java 日誌框架解析(上) - 歷史演進

做爲Java程序員,幸運的是,Java 擁有功能和性能都很是強大的日誌庫;不幸的是,這樣的日誌庫有不止一個——相信每一個人都曾經迷失在JUL(Java Util Log), JCL(Commons Logging), Log4j, SLF4J, Logback,Log4j2 等等的迷宮中。在我見過的絕大多數項目中,都沒有可以良好的配置和使用日誌庫。程序員

這篇文章先講述Java常見日誌庫的歷史和關係,後續會講日誌使用的最佳實踐。讓咱們從頭(Java Util Log)開始提及吧。api

 

Java Util Log框架

簡稱JUL,是JDK 中自帶的log功能。雖然是官方自帶的log lib,JUL的使用確不普遍。主要緣由:異步

  1. JUL從JDK1.4 纔開始加入(2002年),當時各類第三方log lib已經被普遍使用了
  2. JUL早期存在性能問題,到JDK1.5上纔有了不錯的進步,但如今和Logback/Log4j2相比仍是有所不如
  3. JUL的功能不如Logback/Log4j2等完善,好比Output Handler就沒有Logback/Log4j2的豐富,有時候須要本身來繼承定製,又好比默認沒有從ClassPath里加載配置文件的功能

Log4j 1.x函數

Log4j 是在 Logback 出現以前被普遍使用的 Log Lib, 由 Gülcü 於2001年發佈,後來成爲Apache 基金會的頂級項目。Log4j 在設計上很是優秀,對後續的 Java Log 框架有長久而深遠的影響,也產生了Log4c, Log4s, Log4perl 等到其餘語言的移植。Log4j 的短板在於性能,在Logback 和 Log4j2 出來以後,Log4j的使用也減小了。性能

Commons Loggingurl

簡稱JCL,是Apache下面的項目。JCL 是一個Log Facade,只提供 Log API,不提供實現,而後有 Adapter 來使用 Log4j 或者 JUL 做爲Log Implementation。spa

就像以前所說,JDK如今帶了本身的JUL,而後又有第三方的 Log4j 等日誌庫存在,不一樣的項目可能各自使用了不一樣的日誌庫。若是你的項目依賴的其餘 lib 各自使用了不一樣的日誌庫,你想控制日誌行爲,就須要針對每一個日誌庫都寫一個配置文件,是否是很酸爽?debug

而後這個時候 JCL 就出現了。在程序中日誌建立和記錄都是用JCL中的接口,在真正運行時,會看當前ClassPath中有什麼實現,若是有Log4j 就是用 Log4j, 若是啥都沒有就是用 JDK 的 JUL。設計

這樣,在你的項目中,還有第三方的項目中,你們記錄日誌都使用 JCL 的接口,而後最終運行程序時,能夠按照本身的需求(或者喜愛)來選擇使用合適的Log Implementation。若是用Log4j, 就添加 Log4j 的jar包進去,而後寫一個 Log4j 的配置文件;若是喜歡用JUL,就只須要寫個 JUL 的配置文件。若是有其餘的新的日誌庫出現,也只須要它提供一個Adapter,運行的時候把這個日誌庫的 jar 包加進去。

到這個時候一切看起來都很簡單,很美好。接口和實現作了良好的分離,在統一的JCL之下,不改變任何代碼,就能夠經過配置就換用功能更強大,或者性能更好的日誌庫實現。

這種簡單美好一直持續到SLF4J出現。

 

SLF4J/Logback

SLF4J(The Simple Logging Facade for Java) 和 Logback 也是Gülcü 創立的項目,其創立主要是爲了提供更高性能的實現。其中,SLF4j 是相似於JCL 的Log Facade,Logback 是相似於Log4j 的 Log Implementation。

以前已經說過,Apache 有了個JCL,用來作各類Log lib統一的接口,若是 Gülcü 要搞一個更好的 Log 實現的話,直接寫一個實現就行了,爲啥還要搞一個和SLF4J呢?

 

緣由是Gülcü 認爲 JCL 的 API 設計得很差,容易讓使用者寫出性能有問題的代碼。

好比在用 JCL 輸出一個 debug 級別的 log:

logger.debug("start process request, url:" + url);

 

這個有什麼問題呢?通常生產環境 log 級別都會設到 info 或者以上,那這條 log 是不會被輸出的。然而無論會不會輸出,這其中都會作一個字符串鏈接操做,而後生產一個新的字符串。若是這條語句在循環或者被調用不少次的函數中,就會多作不少無用的字符串鏈接,影響性能。

因此 JCL 的最佳實踐推薦這麼寫:

if (logger.isDebugEnabled()) {
    logger.debug("start process request, url:" + url);
}

 

然而開發者經常忽略這個問題或是以爲麻煩而不肯意這麼寫。因此SLF4J提供了新的API,方便開發者使用:

logger.debug("start process request, url:{}", url);

 

這樣的話,在不輸出 log 的時候避免了字符串拼接的開銷;在輸出的時候須要作一個字符串format,代價比手工拼接字符串大一些,可是能夠接受。

而 Logback 則是做爲 Log4j 的繼承者來開發的,提供了性能更好的實現,異步 logger,Filter等更多的特性。

如今事情變複雜了。咱們有了兩個流行的 Log Facade,以及三個流行的 Log Implementation。Gülcü 是個追求完美的人,他決定讓這些Log之間都可以方便的互相替換,因此作了各類 Adapter 和 Bridge 來鏈接:


能夠看到甚至 Log4j 和 JUL 均可以橋接到SLF4J,再經過 SLF4J 適配到到 Logback!

在這裏須要注意不能搞出循環的橋接,好比下面這些依賴就不能同時存在:

  1. jcl-over-slf4j 和 slf4j-jcl
  2. log4j-over-slf4j 和 slf4j-log4j12
  3. 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實現,若是畫出橋接關係的圖來回事什麼樣子呢?

 

是否是感受有點暈呢?一樣,在添加依賴的時候,要當心不要搞成循環依賴。

 看到這裏可能要問了,咱們如今究竟應該怎麼配置和使用 Java 的日誌庫呢?請看下回Java日誌全解析(下) - 最佳實踐

轉載自:https://zhuanlan.zhihu.com/p/24272450

相關文章
相關標籤/搜索