slf4j、jcl、jul、log4j一、log4j二、logback大總結

#1 系列目錄html

#2各類jar包總結java

  • log4j1:apache

    • log4j:log4j1的所有內容
  • log4j2:編程

    • log4j-api:log4j2定義的API
    • log4j-core:log4j2上述API的實現
  • logback:api

    • logback-core:logback的核心包
    • logback-classic:logback實現了slf4j的API
  • commons-logging:微信

    • commons-logging:commons-logging的原生所有內容
    • log4j-jcl:commons-logging到log4j2的橋樑
    • jcl-over-slf4j:commons-logging到slf4j的橋樑
  • slf4j轉向某個實際的日誌框架:架構

    場景介紹:如 使用slf4j的API進行編程,底層想使用log4j1來進行實際的日誌輸出,這就是slf4j-log4j12乾的事。框架

    • slf4j-jdk14:slf4j到jdk-logging的橋樑
    • slf4j-log4j12:slf4j到log4j1的橋樑
    • log4j-slf4j-impl:slf4j到log4j2的橋樑
    • logback-classic:slf4j到logback的橋樑
    • slf4j-jcl:slf4j到commons-logging的橋樑
  • 某個實際的日誌框架轉向slf4j:this

    場景介紹:如 使用log4j1的API進行編程,可是想最終經過logback來進行輸出,因此就須要先將log4j1的日誌輸出轉交給slf4j來輸出,slf4j再交給logback來輸出。將log4j1的輸出轉給slf4j,這就是log4j-over-slf4j作的事.net

    這一部分主要用來進行實際的日誌框架之間的切換(下文會詳細講解)

    • jul-to-slf4j:jdk-logging到slf4j的橋樑
    • log4j-over-slf4j:log4j1到slf4j的橋樑
    • jcl-over-slf4j:commons-logging到slf4j的橋樑

#3集成總結

##3.1 commons-logging與其餘日誌框架集成

  • 1 commons-logging與jdk-logging集成:

    須要的jar包:

    • commons-logging
  • 2 commons-logging與log4j1集成:

    須要的jar包:

    • commons-logging
    • log4j
  • 3 commons-logging與log4j2集成:

    須要的jar包:

    • commons-logging
    • log4j-api
    • log4j-core
    • log4j-jcl(集成包)
  • 4 commons-logging與logback集成:

    須要的jar包:

    • logback-core
    • logback-classic
    • slf4j-api、jcl-over-slf4j(2個集成包,能夠再也不須要commons-logging)
  • 5 commons-logging與slf4j集成:

    須要的jar包:

    • jcl-over-slf4j(集成包,再也不須要commons-logging)
    • slf4j-api

##3.2 slf4j與其餘日誌框架集成

  • slf4j與jdk-logging集成:

    須要的jar包:

    • slf4j-api
    • slf4j-jdk14(集成包)
  • slf4j與log4j1集成:

    須要的jar包:

    • slf4j-api
    • log4j
    • slf4j-log4j12(集成包)
  • slf4j與log4j2集成:

    須要的jar包:

    • slf4j-api
    • log4j-api
    • log4j-core
    • log4j-slf4j-impl(集成包)
  • slf4j與logback集成:

    須要的jar包:

    • slf4j-api
    • logback-core
    • logback-classic(集成包)
  • slf4j與commons-logging集成:

    須要的jar包:

    • slf4j-api
    • commons-logging
    • slf4j-jcl(集成包)

#4 日誌系統之間的切換

##4.1 log4j無縫切換到logback

###4.1.1 案例

咱們已經在代碼中使用了log4j1的API來進行日誌的輸出,如今想不更改已有代碼的前提下,使之經過logback來進行實際的日誌輸出。

已使用的jar包:

  • log4j

使用案例:

private static final Logger logger=Logger.getLogger(Log4jTest.class);

public static void main(String[] args){
	if(logger.isInfoEnabled()){
		logger.info("log4j info message");
	}
}

上述的Logger是log4j1本身的org.apache.log4j.Logger,在上述代碼中,咱們在使用log4j1的API進行編程

如今如何能讓上述的日誌輸出經過logback來進行輸出呢?

只須要更換一下jar包就能夠:

  • 第一步:去掉log4j jar包

  • 第二步:加入如下jar包

    • log4j-over-slf4j(實現log4j1切換到slf4j)
    • slf4j-api
    • logback-core
    • logback-classic
  • 第三步:在類路徑下加入logback的配置文件

原理是什麼呢?

###4.1.2 切換原理

看下log4j-over-slf4j就一目瞭然了:

log4j轉slf4j

咱們能夠看到,這裏面實際上是簡化更改版的log4j。去掉log4j1的原生jar包,換成該簡化更改版的jar包(能夠實現無縫遷移)。

可是簡化更改版中的Logger和原生版中的實現就不一樣了,簡化版中的Logger實現以下(繼承了Category):

public class Category {
    private String name;
    protected org.slf4j.Logger slf4jLogger;
    private org.slf4j.spi.LocationAwareLogger locationAwareLogger;

    Category(String name) {
        this.name = name;
        slf4jLogger = LoggerFactory.getLogger(name);
        if (slf4jLogger instanceof LocationAwareLogger) {
            locationAwareLogger = (LocationAwareLogger) slf4jLogger;
        }
    }
}

從上面能夠看到簡化版中的Logger內部是使用slf4j的API來生成的,因此咱們使用的簡化版的Logger會委託給slf4j來進行輸出,因爲當前類路徑下有logback-classic,因此slf4j會選擇logback進行輸出。從而實現了log4j到logback的日誌切換。

下面的內容就只講解日誌系統到slf4j的切換,再也不講解slf4j選擇何種日誌來輸出

##4.2 jdk-logging無縫切換到logback

###4.2.1 案例

private static final Logger logger=Logger.getLogger(JulSlf4jLog4jTest.class.getName());

public static void main(String[] args){
	logger.log(Level.INFO,"jul info a msg");
	logger.log(Level.WARNING,"jul waring a msg");
}

能夠看到上述是使用jdk-logging自帶的API來進行編程的,如今咱們想這些日誌交給logback來輸出

解決辦法以下:

  • 第一步:加入如下jar包:

    • jul-to-slf4j (實現jdk-logging切換到slf4j)
    • slf4j-api
    • logback-core
    • logback-classic
  • 第二步:在類路徑下加入logback的配置文件

  • 第三步:在代碼中加入以下代碼:

    static{
    	SLF4JBridgeHandler.install();
    }

###4.2.2 切換原理

先來看下jul-to-slf4j jar包中的內容:

jul切換到slf4j

咱們看到只有一個類:SLF4JBridgeHandler

它繼承了jdk-logging中定義的java.util.logging.Handler,Handler是jdk-logging處理日誌過程當中的一個處理器(具體我也沒仔細研究過),在使用以前,必需要提早註冊這個處理器,即上述的SLF4JBridgeHandler.install()操做,install後咱們就能夠經過這個handler實現日誌的切換工做,以下:

protected Logger getSLF4JLogger(LogRecord record) {
    String name = record.getLoggerName();
    if (name == null) {
        name = UNKNOWN_LOGGER_NAME;
    }
    return LoggerFactory.getLogger(name);
}

在處理日誌的過程當中,使用了slf4j的原生方式LoggerFactory來獲取一個slf4j定義的Logger來進行日誌的輸出

而slf4j則又會選擇logback來進行實際的日誌輸出

##4.3 commons-logging切換到logback

###4.3.1 使用案例

使用的jar包

  • commons-logging

案例以下:

private static Log logger=LogFactory.getLog(JulJclTest.class);

public static void main(String[] args){
	if(logger.isTraceEnabled()){
		logger.trace("commons-logging-jcl trace message");
	}
}

能夠看到咱們使用commons-logging的API來進行日誌的編程操做,如今想切換成logback來進行日誌的輸出(這其實就是commons-logging與logback的集成)

解決辦法以下:

  • 第一步:去掉commons-logging jar包(其實去不去都無所謂)

  • 第二步:加入如下jar包:

    • jcl-over-slf4j(實現commons-logging切換到slf4j)
    • slf4j-api
    • logback-core
    • logback-classic
  • 第三步:在類路徑下加入logback的配置文件

###4.3.2 切換原理

這個原理以前都已經說過了,能夠看下commons-logging與logback的集成

就是commons-logging經過jcl-over-slf4j 來選擇slf4j做爲底層的日誌輸出對象,而slf4j又選擇logback來做爲底層的日誌輸出對象。

##4.4 經常使用的日誌場景切換解釋

上面把日誌的切換原理說清楚了,下面就針對具體的例子來進行應用

先來看下slf4j官方的一張圖:

日誌系統之間的切換

下面分別詳細說明這三個案例

###4.4.1 左上圖

  • 現狀:

    目前的應用程序中已經使用了以下混雜方式的API來進行日誌的編程:

    • commons-logging
    • log4j1
    • jdk-logging

    如今想統一將日誌的輸出交給logback

  • 解決辦法:

    • 第一步:將上述日誌系統所有無縫先切換到slf4j

      • 去掉commons-logging(其實去不去均可以),使用jcl-over-slf4j將commons-logging的底層日誌輸出切換到slf4j
      • 去掉log4j1(必須去掉),使用log4j-over-slf4j,將log4j1的日誌輸出切換到slf4j
      • 使用jul-to-slf4j,將jul的日誌輸出切換到slf4j
    • 第二步:使slf4j選擇logback來做爲底層日誌輸出

    加入如下jar包:

    • slf4j-api
    • logback-core
    • logback-classic

下面的2張圖和上面就很相似

###4.4.2 右上圖

  • 現狀:

    目前的應用程序中已經使用了以下混雜方式的API來進行日誌的編程:

    • commons-logging
    • jdk-logging

    如今想統一將日誌的輸出交給log4j1

  • 解決辦法:

    • 第一步:將上述日誌系統所有無縫先切換到slf4j

      • 去掉commons-logging(其實去不去均可以),使用jcl-over-slf4j將commons-logging的底層日誌輸出切換到slf4j
      • 使用jul-to-slf4j,將jul的日誌輸出切換到slf4j
    • 第二步:使slf4j選擇log4j1來做爲底層日誌輸出

    加入如下jar包:

    • slf4j-api
    • log4j
    • slf4j-log4j12(集成包)

###4.4.3 左下圖

  • 現狀:

    目前的應用程序中已經使用了以下混雜方式的API來進行日誌的編程:

    • commons-logging
    • log4j

    如今想統一將日誌的輸出交給jdk-logging

  • 解決辦法:

    • 第一步:將上述日誌系統所有無縫先切換到slf4j

      • 去掉commons-logging(其實去不去均可以),使用jcl-over-slf4j將commons-logging的底層日誌輸出切換到slf4j
      • 去掉log4j1(必須去掉),使用log4j-over-slf4j,將log4j1的日誌輸出切換到slf4j
    • 第二步:使slf4j選擇jdk-logging來做爲底層日誌輸出

    加入如下jar包:

    • slf4j-api
    • slf4j-jdk14(集成包)

#5 衝突說明

仍然是這裏的內容slf4j官網的衝突說明

其實明白上面介紹的各jar包的做用,就很容易理解

##5.1 jcl-over-slf4j 與 slf4j-jcl 衝突

  • jcl-over-slf4j: commons-logging切換到slf4j

  • slf4j-jcl : slf4j切換到commons-logging

若是這二者共存的話,必然形成相互委託,形成內存溢出

##5.2 log4j-over-slf4j 與 slf4j-log4j12 衝突

  • log4j-over-slf4j : log4j1切換到slf4j
  • slf4j-log4j12 : slf4j切換到log4j1

若是這二者共存的話,必然形成相互委託,形成內存溢出。可是log4j-over-slf4內部作了一個判斷,能夠防止形成內存溢出:

即判斷slf4j-log4j12 jar包中的org.slf4j.impl.Log4jLoggerFactory是否存在,若是存在則表示衝突了,拋出異常提示用戶要去掉對應的jar包,代碼以下,在slf4j-log4j12 jar包的org.apache.log4j.Log4jLoggerFactory中:

slf4j-log4j12防止衝突的策略

##5.3 jul-to-slf4j 與 slf4j-jdk14 衝突

  • jul-to-slf4j : jdk-logging切換到slf4j
  • slf4j-jdk14 : slf4j切換到jdk-logging

若是這二者共存的話,必然形成相互委託,形成內存溢出

#6 結束語

至此,這個日誌系列就算終於完成了。它注重於日誌系統之間的交互與集成,因此想深刻研究單個日誌系統的架構的話,就須要各位自行去深刻研究了。

歡迎關注微信公衆號:乒乓狂魔

微信公衆號

相關文章
相關標籤/搜索