JCL、SLF4J、Log4J、Log4J2、LogBack和JUL之間的關係

寫在前面

日誌組件是我們平時開發過程中必然會用到的組件。在系統中正確的打印日誌至少有下面的這些好處:

  • 調試 :在程序的開發過程中,必然需要我們不斷的調試以達到程序能正確執行的狀態 。記錄日誌可以讓開發人員清楚的瞭解程序的運行狀態定位問題;
  • 信息收集 :在 DT 時代,誰掌握了數據誰就掌握了主動權。現在主流的日誌系統可以非常方便的記錄用戶行為數據,格式化成便於進行大數據分析的格式;
  • 記錄運行狀態 :應用程序投產之後,難免會出現生產事故,有了系統日誌工程師可以根據日誌迅速定位問題。

當然,硬幣都具有兩面性。引入日誌組件也並不是沒有缺點。

  • 代碼冗餘 :光從實現業務邏輯的角度來講,在應用程序中插入打印日誌的代碼打印一大堆日誌是完全沒必要的,這在一定程度上降低了代碼的可讀性;
  • 降低系統性能 :這點很容易理解,因為需要進行日誌打印處理,所以系統的運行速度肯定會有所降低。

綜合比較日誌組件優缺點,我們發現引入日誌組件還是非常有必要的。

在我們平時的開發過程中,常用的日誌組件有Log4J、Log4J2和LogBack等。代碼中,我們一般都是像下面這樣使用它們的。

<code>import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;@SpringBootApplication@EnableAdminServerpublic class AppQuickStart {    private static Logger logger = LoggerFactory.getLogger(AppQuickStart.class);    public static void main(String[] args) {        System.out.println(Thread.currentThread().getName());        logger.info("app begin to start...");        SpringApplication.run(AppQuickStart.class, args);        logger.info("app start success...");    }}/<code>

或者是像下面這樣

<code>import org.apache.commons.logging.Log;import org.apache.commons.logging.LogFactory;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;@SpringBootApplication@EnableAdminServerpublic class AppQuickStart {    private static final Log logger = LogFactory.getLog(AppQuickStart.class);    public static void main(String[] args) {        System.out.println(Thread.currentThread().getName());        logger.info("app begin to start...");        SpringApplication.run(AppQuickStart.class, args);        logger.info("app start success...");    }}/<code>

上面的兩段代碼很相似,是我們引入日誌的模板代碼,唯一有區別的地方是第一段代碼引入了 SLF4J 的Jar包,第二段代碼引入了 common-logging 的Jar包(後面簡稱JCL)。

剛開始接觸日誌組件的時候,我對這樣的使用方式感到很疑惑:我們不是要用使用 Log4J 或者是 LogBack 打日誌麼,怎麼完全沒見到 Log4J 和 LogBack 的影子,反而有冒出來兩個新框架 SLF4J 和 common-logging 。

那麼這兩個框架到底有什麼作用?和 Log4J 、 Log4J2 還有 LogBack 又是什麼關係?如果你也有這樣的疑問,說明你還是善於思考的。今天的文章就來介紹下 JCL 、 SLF4J 、 Log4J 、 Log4J2 、 LogBacky 以及 JUL (JUL的存在感很低,哈哈~)之間的關係。

門面模式

學過設計模式的同學都會知道在23種設計模式中有一種模式叫 門面模式

JCL、SLF4J、Log4J、Log4J2、LogBack和JUL之間的關係

JCL、SLF4J、Log4J、Log4J2、LogBack和JUL之間的關係

以上是門面模式的結構圖。

在這個結構圖中,出現了兩個角色:

  • 門面(Facade)角色 : 客戶端可以調用這個角色的方法。此角色知曉相關的(一個或者多個)子系統的功能和責任。在正常情況下,本角色會將所有從客戶端發來的請求委派到相應的子系統去。
  • 子系統(SubSystem)角色 : 可以同時有一個或者多個子系統。每個子系統都不是一個單獨的類,而是一個類的集合(如上面的子系統就是由ModuleA、ModuleB、ModuleC三個類組合而成)。每個子系統都可以被客戶端直接調用,或者被門面角色調用。子系統並不知道門面的存在,對於子系統而言,門面僅僅是另外一個客戶端而已。

使用門面模式具有以下優點

  • 鬆散耦合 : 門面模式鬆散了客戶端與子系統的耦合關係,讓子系統內部的模塊能更容易擴展和維護。
  • 簡單易用 : 門面模式讓子系統更加易用,客戶端不再需要了解子系統內部的實現,也不需要跟眾多子系統內部的模塊進行交互,只需要跟門面類交互就可以了。
  • 更好的劃分訪問層次 : 通過合理使用Facade,可以幫助我們更好地劃分訪問的層次。有些方法是對系統外的,有些方法是系統內部使用的。把需要暴露給外部的功能集中到門面中,這樣既方便客戶端使用,也很好地隱藏了內部的細節。

JCL 和 SLF4J

為什麼要介紹上面的門面模式呢?因為現今主流的日誌組件都是使用門面模式實現的。而 JCL 和 SLF4J 就是門面模式中的 Facade 角色。

JCL 官網對 JCL 的介紹:

The Logging package is an ultra-thin bridge between different logging implementations. A library that uses the commons-logging API can be used with any logging implementation at runtime. Commons-logging comes with support for a number of popular logging implementations, and writing adapters for others is a reasonably simple task. ——JCL官網

上面英文的大致意思是: JCL 是不同日誌實現之間的一座“橋樑”, JCL 支持許多主流的日誌實現。而且自己編寫 JCL 的適配代碼也很簡單。

SLF4J 的介紹:

The Simple Logging Facade for Java (SLF4J) serves as a simple facade or abstraction for various logging frameworks (e.g. java.util.logging, logback, log4j) allowing the end user to plug in the desired logging framework at deployment time. —— SLF4J官網

上面英文的大致意思是: SLF4J 充當不同日誌框架門面的角色,讓用戶可以自由切換底層的日誌實現。

通過上面的介紹,我們可以知道 JCL 和 SLF4J 都是日誌門面(Facade),而 Log4J 、 Log4J2 和 LogBack 都是子系統角色(SunSystem),也就是具體的日誌實現框架。他們的關係如下。

JCL、SLF4J、Log4J、Log4J2、LogBack和JUL之間的關係

使用日誌門面引入日誌組件的最大優勢是: 將系統和具體的日誌實現框架解耦合

假如說我們不使用日誌門面,直接使用特定的日誌框架(比如說 Log4J )的API進行編程,那麼我們勢必會在每個類中都耦合 Log4J 的API,如果你的系統一直使用Log4j作為日誌實現,那OK。一旦有一天你老闆心血來潮覺得 Log4J 不能滿足系統的需求了(這邊只是舉個栗子, Log4J 還是很強大^_^),指派你將Log4J更換成其他的日誌實現。我想此刻的你一定會有點懵逼。因為你需要修改每個類中耦合的 Log4J API。

如果使用JCL或者SLF4J等日誌門面很好的幫我們解決了這種問題,我們不需要修改代碼,只需要更換日誌實現框架即可。

Log4J、Log4J2和LogBack的有趣歷史

使用過 Log4J 和 LogBack 的同學肯定能發現,這兩個框架的設計理念極為相似,使用方法也如出一轍。

其實這個兩個框架的作者都是一個人,Ceki Gülcü,俄羅斯程序員。

Log4J 最初是基於Java開發的日誌框架,發展一段時間後,作者Ceki Gülcü將 Log4j 捐獻給了Apache軟件基金會,使之成為了Apache日誌服務的一個子項目。 又由於 Log4J 出色的表現,後續又被孵化出了支持C, C++, C#, Perl, Python, Ruby等語言的子框架。

然而,偉大的程序員好像都比較有個性。Ceki Gülcü由於不滿Apache對 Log4J 的管理,決定不再參加 Log4J 的開發維護。“出走”後的Ceki Gülcü另起爐灶,開發出了 LogBack 這個框架( SLF4J 是和 LogBack 一起開發出來的)。

LogBack 改進了很多 Log4J 的缺點,在性能上有了很大的提升,同時使用方式幾乎和 Log4J 一樣,許多用戶開始慢慢開始使用 LogBack 。

由於受到 LogBack 的衝擊, Log4J 開始式微。終於,2015年9月,Apache軟件基金業宣佈, Log4j 不在維護,建議所有相關項目升級到 Log4j2 。

Log4J2 是Apache開發的一個新的日誌框架,改進了很多 Log4J 的缺點,同時也借鑑了 LogBack ,號稱在性能上也是完勝 LogBack 。有興趣的朋友可以測試下兩者的性能。

這邊順帶提下 JUL 這個日誌組件。這個日誌組件是JDK自帶的日誌框架。由於在使用便利性和性能上都欠佳,所以存在感一直不高。

簡單總結

  • JCL 和 SLF4J 功能一樣,都是日誌門面,使用它們引入日誌組件的目的是將系統和具體的日誌實現之間解耦;
  • Log4J 、 Log4J2 、 LogBack 和 JUL 都是具體的日誌實現。使用時要和門面日誌搭配使用。


分享到:


相關文章: