常見java日志系統的搭配詳解:關於slf4j log4j log4j2 logback jul jcl commons-logging jdk-logging


先看一張圖:

 

是不是有點暈, 暈就對了。這個僅僅是 slf4j 的情況,實際上, 我們不僅要接觸到 slf4j ,有時候還會接觸其他的日志系統。且看下文分解。

 

1 直接使用各個日志系統

1.1 直接使用log4j

最開始的時候, 我們都是使用log4j, 怎么使用呢? 先引入jar,log4j-1.x.x  jar

maven是這樣的:

        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.17</version>
        </dependency>

然后配置: 配置文件主要是 log4j.properties, 具體略

然后代碼中使用

static org.apache.log4j.Logger logger = org.apache.log4j.LogManager.getLogger(TestLog.class);

1.2 直接使用JCL,即commons-logging

有時候我們也使用Apache的 commons-logging ,也就是 Jakarta Commons Logging,簡稱 JCL。commons-logging其實也是一個日志 接口,不是一個日志控件,沒有日志功能,它只是統一了JDK Logging與Log4j的API,並把日志功能交給JDK Loggings或者是log4j 。

commons-logging能夠選擇使用Log4j還是JDK Logging,但是他不依賴Log4j,JDK Logging的API。如果項目的classpath中包含了log4j的類庫,就會使用log4j,否則就使用JDK Logging。使用commons-logging能夠靈活的選擇使用那些日志方式,而且不需要修改源代碼。

怎么使用呢? 首先需要一個 commons-logging-1.2.jar, 然后引入 log4j的類庫,或者不引入直接JDK Logging(這里的 log4j的類庫 應該是 指log4j-1.x , 對於 log4j2 不知道是否支持 )

參考 :https://blog.csdn.net/u011794238/article/details/50747953 

maven是這樣的:

        <dependency>
            <groupId>commons-logging</groupId>
            <artifactId>commons-logging</artifactId>
            <version>1.2</version>
        </dependency>

配置:主要是 commons-logging.properties,具體略

關於 commons-logging ,其實也是一個 slf4j 類似的日志接口系統,至於其原理,請參考其他的博客,

 

1.3 直接使用log4j2

后面出了個log4j2,log4j2是怎么使用的? 還是先引入jar : log4j-api-2.x.x.jar和log4j-core-2.x.x.jar

maven是這樣的:

<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.8.2</version>
</dependency>

沒錯,引入一個 log4j-core 即可, log4j-core  會自動引入 log4j-api,另外我發現這么一個jar:

        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>2.8.2</version>
        </dependency>

仔細看一下,這個僅僅是一個pom 不是jar,也就是 log4j-core 的parent 。

然后配置: 配置文件主要是 log4j2.xml ( 有時候也可以是 log4j.xml, 或其他名字 ), 具體略

然后代碼中使用

static org.apache.logging.log4j.Logger logger = org.apache.logging.log4j.LogManager.getLogger(TestLog.class);

注意看到 log4j 、log4j2 的差別還是比較大的, log4j需要一個 jar,log4j-1.x.x  jar ,而且是 1.x.x 的格式, 最高好像也就是1.7.25 , 我是沒有看到 log4j-2.x的 jar 的。 但是到了 log4j2,jar 的命名發生了很大的變化: log4j-api-2.x.x.jar和log4j-core-2.x.x.jar ,沒有 log4j-2.x .jar , 而且也不是一個jar ,是分開了2個。

 

1.4 直接使用logback

后面又來了一個 logback, logback 是天生就和slf 緊密結合在一起的,無法分開。(觀察發現logback也沒有提供任何的 Logger 實現,logback-core 的功能主要就是實現了很大的 appender ,pattern 等)一般我們需要引入logback-classic , 它直接依賴了 logback-core、slf4j-api , 所以,我們需要3個jar 。

maven是這樣的:

<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.3</version>
</dependency>

然后配置:  配置文件主要是 logback.xml , 具體略

然后代碼中使用,(只能通過slf4j 的api )

static Logger logger = LoggerFactory.getLogger(TestLog.class);

 

1.5 直接使用JUL ,也就是jdk-logging

直接用 jul 的人應該很少了吧。不過這里還是要說明一下。具體 怎么使用呢?首先,它是jdk 自帶的,不需要引入jar:

然后配置: 默認配置文件是logging.properties,logging.properties 一般位於 jdk或者jre 的lib 目錄, 一個 jdk或者jre 只有一個配置。具體略

然后代碼中使用

static java.util.logging.Logger logger = java.util.logging.Logger.getLogger(TestLog.class.getName());

logger.info("in");

logger.severe("severe");

logger.fine("fine");

 

可以看到它的 Logger  是  java.util.logging 包(簡稱 JUL)下面的, 是jdk 自帶的。 不過需要注意的是 JUL的Logger 提供的方法和其他的框架的還不一樣。

它的日志是這樣的:

四月 11, 2019 1:52:46 下午 com.lk.TestLog main
信息: in
四月 11, 2019 1:52:46 下午 com.lk.TestLog main
嚴重: severe

可以看到其中的月份和 日志級別都使用了 漢字, 非常的明顯的 特征。

 

2 使用slf4j 日志接口框架

前面都是直接使用的方式(除了logback), 如果我們統一到 slf4j 呢?這里說的是統一到 slf4j是指 代碼中使用 slf4j的API。 首先我們要明白,slf4j 是什么? 它是一個統一的日志接口框架,這樣說可能還是有些懵逼。 怎么說呢, 可以簡單理解為它提供了幾個關鍵的接口,但是沒有實現他們。 所以說它只是一個 日志接口。通常我們需要 slf4j 結合其他日志框架來打印我們的日志。 老實說,這樣做TM有些奇特, 這樣做的主要目的是 方便的替換 我們日志框架的實現, 而不用修改代碼—— 其實這樣的需求也是非常少的。

不管怎么說,slf4j 已然成為了主流。

 

幾乎所有的其他日志框架都可以作為 slf4j 日志接口的實現。怎么使用呢? 通常我們需要首先引入 slf4j-api-1.x.x.jar ( 目前來看, 主要有 1.5,1.6,1.7  三個實現,1.5 有些特別, 兼容性不是很好), 然后引入具體的實現的 jar。 

 

有哪些實現?

2.1 slf4j-api 的NOP實現

NOP, 也就是No Operation,也就是不做任何操作。不需要引入任何其他jar,配置文件也是無。

 

2.2 slf4j-api 的logback實現

logback,前文已述

 

2.3 slf4j-api 的simple實現

simple,意味着簡單。它非常簡單的實現, 需要引入slf4j-simple-1.7.5.jar,

maven是這樣的:

        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-simple</artifactId>
            <version>1.7.12</version>
        </dependency>

slf4j-simple 依賴了 slf4j-api 。slf4j-simple 通常只是把日志直接打印到 控制台。

配置是:simplelogger.properties 或者通過D系統參數。

 

2.4 slf4j-api 的JUL實現

其實就是把日志操作 轉接給了 jdk , 也就是 jul , 需要 slf4j-jdk14-1.x.x.jar ,

maven是這樣的:

<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-jdk14</artifactId>
<version>1.7.25</version>
</dependency>

slf4j-jdk14 依賴了 slf4j-api 。

配置同直接使用JUL的場景

 

2.5 slf4j-api 的log4j 實現

具體日志工作交給了 log4j , 需要 slf4j-log4j12-1.x.x.jar (主要是 slf4j-log4j12-1.6.x.jar, slf4j-log4j12-1.7.x.jar), log4j-1.2.x.jar ,

maven是這樣的:

<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.6.1</version>
</dependency>

slf4j-log4j12 依賴了slf4j-api 和 log4j12 。

配置同直接使用log4j的場景

 

2.6 slf4j-api 的log4j2 實現

依賴 log4j2 , 需要 log4j-slf4j-impl-2.x.x.jar , log4j-api ,  log4j-core ,

maven是這樣的:


<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.8.2</version>
</dependency>

<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-slf4j-impl</artifactId>
<version>2.8.2</version>
</dependency>

log4j-slf4j-impl 依賴了slf4j-api 和 log4j-api,  但是並沒有依賴   log4j-core , 因此, 我們需要手動引入 它 。

配置同直接使用log4j2的場景

 

2.7 slf4j-api 的JCL實現

依賴 commons-logging , 需要 slf4j-jcl ,

maven :用得少,略

配置:略

 

3 使用其他日志框架,然后橋接給 slf4j 日志接口框架

變態的還不僅僅是這個, 有時候,我們還需要 反向的 日志操作。比如 我們有代碼已經直接使用的 log4j 的api, 但是想使用 slf4j 的實現,怎么辦呢? 可以 先引入  log4j 的api的jar, 然后引入 slf4j-api , 然后引入 slf4j 的具體實現(如上), 比如 log4j  的接口,slf4j 做橋接, logback 的實現, 這個是完全可以的。 (這可以達到一種奇特的效果, 整個系統中, 雖然有各種第二方、三方框架, 他們直接使用各種各樣的 日志api, 但是我都可以讓他們 統一使用 logback的實現, 也就是 只使用一個 配置即可: logback.xml , 可以減少配置量)

但是, 我們肯定不會說, log4j  的接口,slf4j 做橋接, 然后又采用 log4j  作為slf4j  的實現。看起來行得通, 實際上會出現 循環引用的問題, 

這種做法,其實有點繞,容易讓人迷糊。也許是我見識短淺,我其實沒有直接使用過這樣做法。 不過, 現在中 確實也有這樣的需求。

 

具體來說 也有很多種, log4j, log4j2, jcl,jul,等等。但是 我們似乎找不到 logback 橋接給 slf4j  的情況,我感覺是因為logback 沒有直接使用的接口,它天生就是和slf4j 緊密聯系的。

3.1 使用log4j的 api,然后橋接給slf4j 

對於 log4j, 我們首先需要log4j的 api接口: log4j-over-slf4j-1.7.x.jar, 其他的就交給了 slf4j  ————  你可能有疑問, log4j 的接口在哪里呢? 沒錯,就是  log4j-over-slf4j-1.7.x.jar ,這里我們沒有使用 log4j-1.x.x  jar,因為 log4j-1.x.x  jar 包含了 log4j 的實現,我們不需要它,我們需要排除它。 這種做法簡直不可思議, 不過它就是這么發生了!

maven是這樣的:

        <dependency>
            <groupId>org.slf4j</groupId>
            <version>1.7.21</version>
            <artifactId>log4j-over-slf4j</artifactId>
        </dependency>

log4j-over-slf4j 依賴了slf4j-api。

至於橋接到slf4j 之后的工作,比如配置文件啊,還需要的其他jar 啊,請參考前文。總之, 不要出現了 循環引用即可。

 

循環引用

另外,我注意到, 如果 log4j-over-slf4j  、  log4j-1.x.x  jar 如果同時存在於 maven 的pom ,或者classpath, 那么 到底是啟用哪一個呢?  對應 maven, 答案是, 哪個先出現在pom中 就啟用哪一個!

需要重要的是: log4j-over-slf4j.jar 不能和 slf4j-log4j12.jar 同時存在於 classpath 中, 否則

對於 slf4j-log4j12-1.7.25.jar :出現

Detected both log4j-over-slf4j.jar AND bound slf4j-log4j12.jar on the class path, preempting StackOverflowError.

slf4j-log4j12-1.7.25.jar  比較溫和,有檢測機制, slf4j-log4j12-1.7.25.jar 之前的 slf4j-log4j12-1.x.x jar 則是直接拋出 異常:

java.lang.StackOverflowError
    at java.util.HashMap.hash(HashMap.java:338)
    at java.util.HashMap.get(HashMap.java:556)
    at org.slf4j.impl.Log4jLoggerFactory.getLogger(Log4jLoggerFactory.java:67)
    at org.slf4j.LoggerFactory.getLogger(LoggerFactory.java:358)
    at org.apache.log4j.Category.<init>(Category.java:57)
    at org.apache.log4j.Logger.<init>(Logger.java:37)
    at org.apache.log4j.Log4jLoggerFactory.getLogger(Log4jLoggerFactory.java:43)
    at org.apache.log4j.LogManager.getLogger(LogManager.java:45)

除了log4j, 我想其他日志框架也是如此,不能出現over-slf4j 之后出現循環委托的情況。

 

3.2 使用log4j2的 api,然后橋接給slf4j 

 log4j 是 1.x 時代了, 如果是 log4j2 呢? 我們首先需要log4j2的 api接口:log4j-to-slf4j-2.x.x.jar, 

maven是這樣的:

<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-to-slf4j</artifactId>
<version>2.11.1</version>
</dependency>

log4j-to-slf4j-2.x.x.jar 依賴了slf4j-api 和log4j-api ( log4j-api 是log4j2 的api 接口 ), log4j-to-slf4j-2.x.x.jar 沒有依賴 log4j-core。 那么 log4j2 是怎么反向橋接到 slf4j 上去的呢? 其實這個是 log4j-to-slf4j 的功勞,log4j-to-slf4j-2.x.x.jar 橋接了 log4j-api ,然后具體實現交給了 slf4j。 

特別注意到:log4j-1.x 的橋接jar 名是這樣的:  log4j-over-slf4j  而log4j-2.x 的橋接jar 名是這樣的: log4j-to-slf4j,  名字非常相近。

至於橋接到slf4j 之后的工作,請參考前文。

 

3.3 使用JCL 的 api,然后橋接給slf4j 

前面說過使用 SLF4J的API, 然后 使用JCL的實現, 反過來也是可以的。

jcl 並不是jul, 不是 java.util.logging 而是 org.apache.commons.logging , 也就是 commons-logging 的那一套。 為此, 我們需要一個 jcl-over-slf4j-1.x.x.jar

maven是這樣的:

<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
<version>1.7.25</version>
</dependency>

至於橋接到slf4j 之后的工作,請參考前文。

 

3.4 使用JUL 的 api,然后橋接給slf4j 

JUL 和 JCL 是僅僅一字之別,但是千萬不能搞混啊!它是將 jdk logging API 打印的日志交給了 slf4j,我們 需要一個 jul-to-slf4j-1.7.x.jar 

maven是這樣的:

<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jul-to-slf4j</artifactId>
<version>1.7.25</version>
</dependency>

 代碼中還需要做一些修改:

需要先執行下面的:

    static{
        SLF4JBridgeHandler.install();
    }

不過從打印的結果, 我看到 日志打印了兩次, 似乎哪里出了問題,並沒有達到想要的效果。

四月 11, 2019 6:40:12 下午 com.lk.TestLog main
信息: in
18:40:12,318 [com.lk.TestLog] - severe           --- log4j的日志 
四月
11, 2019 6:40:12 下午 com.lk.TestLog main 嚴重: severe

需要注意的是 我們並沒有 jul-over-slf4j 的jar, 這個可能是命名的歷史原因, 暫時 jul-to-slf4j 可以理解為就是  jul-over-slf4j ... 

至於橋接到slf4j 之后的工作,請參考前文。

 

4 使用A日志框架接口,然后橋接給 B日志框架實現

這樣的需求也是有的,做法肯定也是有的,也肯定有人實現了的。比如,貌似 feign 也有一個實現,gossip 也有:

這些jar我沒有用過,具體,就不多說了。

 

關於SLF4J 其實也可以寫很多,還有JCL( 也就是commons-logging)具體的原理這里不多講了,請參考其他的博客。總之,感覺這整個java 的日志系統 還是挺麻煩的,特別是其中的jar的命名,不太規律,容易混淆,不容易記住。搞來搞去, 容易把人搞暈呢,這個時候 就需要看其源碼! 其實其中很多jar 的實現是很簡單的,也就幾個類,特別是那些橋接的jar, 一打開看一下就明白了,哦,說得也是呢!

 

 看我的文章后是不是有所收獲呢?是不是應該點32個贊鼓勵一下呢?

 

 

參考:

https://my.oschina.net/pingpangkuangmo/blog/410224
https://www.slf4j.org/legacy.html
https://blog.csdn.net/u011794238/article/details/50747953
https://blog.csdn.net/john1337/article/details/76152906
http://www.cnblogs.com/chen310/p/4216316.html
https://blog.csdn.net/u011794238/article/details/50783188
https://blog.csdn.net/u011794238/article/details/50771488

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM