1. 程式人生 > >JCL、SLF4J、Log4J、Log4J2、LogBack和JUL之間的關係,你搞清楚了嗎?

JCL、SLF4J、Log4J、Log4J2、LogBack和JUL之間的關係,你搞清楚了嗎?

寫在前面

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

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

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

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

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

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

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
@EnableAdminServer
public 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...");

    }
}

或者是像下面這樣

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
@EnableAdminServer
public 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...");

    }
}

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

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

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

門面模式

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

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

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

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

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

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

JCLSLF4J

為什麼要介紹上面的門面模式呢?因為現今主流的日誌元件都是使用門面模式實現的。而JCLSLF4J就是門面模式中的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充當不同日誌框架門面的角色,讓使用者可以自由切換底層的日誌實現。

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

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

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

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

Log4J、Log4J2和LogBack的有趣歷史

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

其實這個兩個框架的作者都是一個人,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自帶的日誌框架。由於在使用便利性和效能上都欠佳,所以存在感一直不高。

簡單總結

  • JCLSLF4J功能一樣,都是日誌門面,使用它們引入日誌元件的目的是將系統和具體的日誌實現之間解耦;
  • Log4JLog4J2LogBackJUL都是具體的日誌實現。使用時要和門面日誌搭配使用。

參考

  • SLF4J官網
  • JCL官網
  • JAVA設計模式之門面模式(外觀模式)