學(xué)習(xí)Java日志
本文公眾號(hào)來源:程序猿阿星
作者:程序猿阿星
本文已收錄至我的GitHub
今天來和大家聊聊Java日志體系,Java日志體系可以說是五花八門,眼花繚亂。
導(dǎo)致很多小伙伴因?yàn)槿罩緲?biāo)準(zhǔn)庫之間復(fù)雜的關(guān)系而感到煩惱,不知道統(tǒng)一系統(tǒng)的日志標(biāo)準(zhǔn)庫需要依賴哪些jar包,百度一下所謂的博客,照著人家復(fù)制,卻無法弄懂原理,甚至還有搞了半天項(xiàng)目因jar沖突跑不起來的,心態(tài)直接爆炸。

咳咳,淡定淡定,別慌,我?guī)銈兣渲械脑恚灰沆o下心,跟著本文來,再給個(gè)一鍵三連,你就能隨心所欲的更改日志標(biāo)準(zhǔn)庫,統(tǒng)一日志輸出。
發(fā)展史
我們要正確的配置好日志,讓jar相互生效,就要先理清關(guān)系,理清關(guān)系就得從它的發(fā)展史下手。
System.out和System.err
2001年以前,Java是沒有日志庫的,打印日志全憑System.out和System.err,我人都傻了,十分離譜。

缺點(diǎn)如下:
產(chǎn)生大量的IO操作 同時(shí)在生產(chǎn)環(huán)境中 無法合理的控制是否需要輸出 輸出的內(nèi)容不能保存到文件 只打印在控制臺(tái),打印完就過去了,也就是說除非你一直盯著程序跑 無法定制化,且日志粒度不夠細(xì)
Log4j
此時(shí)名為Ceki的巨佬站出來,說你這個(gè)不好用,我這個(gè)好用,接著在2001年掏出了Log4j,用起來也確實(shí)比System系列香,Log4j一度成為業(yè)內(nèi)日志標(biāo)準(zhǔn)。

后來Log4j成為Apache項(xiàng)目,Ceki也加入Apache組織(據(jù)說Apache還曾經(jīng)建議Sun引入Log4j到Java的標(biāo)準(zhǔn)庫中,但Sun拒絕了)。
JUL(Java Util Logging)
原來Sun也有自己的盤算,不就一個(gè)日志嘛,我自己也搞一個(gè),2002年2月JDK1.4發(fā)布,Sun推出了自己的日志標(biāo)準(zhǔn)庫JUL(Java Util Logging),其實(shí)是照著Log4j抄的,而且還沒抄好,還是在JDK1.5以后性能和可用性才有所提升。
因?yàn)樵?code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 100, 65);">JUL出來以前,Log4j就已經(jīng)成為一項(xiàng)成熟的技術(shù),使得Log4j在選擇上占據(jù)了一定的優(yōu)勢(shì)。
JCL(Jakarta Commons Logging)
現(xiàn)在市面上有兩款Java日志標(biāo)準(zhǔn)庫,分別是Log4j與JUL,此時(shí)Apache組織十分有想法,想統(tǒng)一抽象日志標(biāo)準(zhǔn)接口規(guī)范(就像JDBC統(tǒng)一數(shù)據(jù)庫訪問層),讓其他日志標(biāo)準(zhǔn)庫去實(shí)現(xiàn)它的抽象接口,這樣你的日志操作都是統(tǒng)一的接口。

于是JUL剛出來不久,2002年8月Apache推出了JCL(Jakarta Commons Logging),也就是日志抽象層,支持運(yùn)行時(shí)動(dòng)態(tài)加載日志組件的實(shí)現(xiàn),當(dāng)然也提供一個(gè)默認(rèn)實(shí)現(xiàn)Simple Log(在ClassLoader中進(jìn)行查找,如果能找到Log4j則默認(rèn)使用log4j實(shí)現(xiàn),如果沒有則使用JUL 實(shí)現(xiàn),再?zèng)]有則使用JCL內(nèi)部提供的Simple Log實(shí)現(xiàn))。

但是JUL有三個(gè)缺點(diǎn)
效率較低 容易引發(fā)混亂 使用了自定義ClassLoader的程序中,使用JCL會(huì)引發(fā)內(nèi)存泄露
總之就是問題也挺多~
Slf4j(Simple Logging Facade for Java)
2006年巨佬Ceki(Log4j的作者)因?yàn)橐恍┰螂x開了Apache組織,之后Ceki覺得JCL不好用,自己擼了一套新的日志標(biāo)準(zhǔn)接口規(guī)范Slf4j(Simple Logging Facade for Java),也可以稱為日志門面,很明顯Slf4j是對(duì)標(biāo)JCL,后面也證明了Slf4j比JCL更優(yōu)秀。

由于Slf4j出來的較晚,光有一個(gè)接口,沒有實(shí)現(xiàn)的日志庫也很蛋疼,如JUL和Log4j都是沒有實(shí)現(xiàn)Slf4j,就算開發(fā)者想用Slf4j也用不了。

這時(shí)候巨佬Ceki發(fā)話了,Sum和Apache這兩個(gè)組織不來實(shí)現(xiàn)我的接口沒關(guān)系,我來實(shí)現(xiàn)就好了,只有魔法才能打敗魔法。
后面巨佬Ceki提供了一系列的橋接包來幫助Slf4j接口與其他日志庫建立關(guān)系,這種方式稱為橋接設(shè)計(jì)模式。

有了橋接包配合,其他的問題都迎刃而解,我們先看看有那些問題吧~

從上圖可以看出,不同時(shí)期的項(xiàng)目使用的日志標(biāo)準(zhǔn)庫不一樣,我們以Slf4j接口作為劃分線,考慮兩個(gè)問題,一個(gè)是Slf4j之前的項(xiàng)目怎么統(tǒng)一日志標(biāo)準(zhǔn),另一個(gè)是Slf4j之后的項(xiàng)目怎么統(tǒng)一日志標(biāo)準(zhǔn)。
先來看Slf4j之后的項(xiàng)目怎么統(tǒng)一日志標(biāo)準(zhǔn),項(xiàng)目D、E都使用Slf4j接口,首先在代碼層已經(jīng)統(tǒng)一了,如果要做到日志標(biāo)準(zhǔn)統(tǒng)一也十分簡(jiǎn)單,直接替換日志標(biāo)準(zhǔn)庫與對(duì)應(yīng)的橋接包即可,就如下圖所示

好家伙,Slf4j接口配合橋接包簡(jiǎn)直無敵了,靈活配置。。
再來看Slf4j之前的項(xiàng)目怎么統(tǒng)一日志標(biāo)準(zhǔn),項(xiàng)目A、B、C都使用了不同的日志標(biāo)準(zhǔn),所以它們的API不一樣,如果要統(tǒng)一標(biāo)準(zhǔn),首先就要改動(dòng)代碼,這樣侵入太強(qiáng)了,難道就沒有辦法在不改代碼的情況下,讓A、B、C項(xiàng)目統(tǒng)一日志標(biāo)準(zhǔn)嗎?
辦法當(dāng)然有,Slf4j接口能通過橋接包勾搭上具體的日志標(biāo)準(zhǔn)庫,為什么日志標(biāo)準(zhǔn)庫不能通過橋接包勾搭Slf4j接口呢?

想把A、B、C項(xiàng)目都統(tǒng)一成Log4j日志輸出,只需要做如下調(diào)整

是不是很簡(jiǎn)單,引入Slf4j與相關(guān)的橋接包,再引入具體的日志標(biāo)準(zhǔn)庫,比如Log4j,就完成了3個(gè)項(xiàng)目的統(tǒng)一日志標(biāo)準(zhǔn),對(duì)代碼層是零入侵。
Logback
Ceki巨佬覺得市場(chǎng)上的日志標(biāo)準(zhǔn)庫都是間接實(shí)現(xiàn)Slf4j接口,也就是說每次都需要配合橋接包,因此在2006年,Ceki巨佬基于Slf4j接口擼出了Logback日志標(biāo)準(zhǔn)庫,做為Slf4j接口的默認(rèn)實(shí)現(xiàn),Logback也十分給力,在功能完整度和性能上超越了所有已有的日志標(biāo)準(zhǔn)庫。
目前Java日志體系關(guān)系圖如下

Log4j2
自從Logback出來后,可以說Slf4j+Logback組合如日中天,很沖擊JCL+Log4j組合,Apache眼看有被Logback反超的勢(shì)頭。

于2012年,Apache直接推出新項(xiàng)目Log4j2(不兼容Log4j),Log4j2全面借鑒Slf4j+Logback(十分明顯的抄襲嫌疑)。
因?yàn)?code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 100, 65);">Log4j2不僅僅具有Logback的所有特性,還做了分離設(shè)計(jì),分為log4j-api和log4j-core,log4j-api是日志接口,log4j-core是日志標(biāo)準(zhǔn)庫,并且Apache也為Log4j2提供了各種橋接包。。。

到目前為止Java日志體系被劃分為兩大陣營(yíng),分別是Apache陣營(yíng)和Cekij陣營(yíng),如下圖所示

至于系統(tǒng)中用那套體系,各位自行選擇,個(gè)人偏向用Ceki提供的Slf4j那套
Slf4j的橋接包介紹
相信大家都對(duì)橋接包都有了基本概念,這里羅列下與Slf4j配合使用的橋接包
Slf4j轉(zhuǎn)向某個(gè)日志標(biāo)準(zhǔn)庫
slf4j-jdk14.jarSlf4j到JUL的橋梁slf4j-log4j12.jarSlf4j到Log4j的橋梁log4j-slf4j-impl.jarSlf4j到Log4j2的橋梁slf4j-jcl.jarSlf4j到JCL的橋梁
某個(gè)實(shí)際日志框架轉(zhuǎn)向Slf4j
jul-to-slf4j.jarJUL到Slf4j的橋梁log4j-over-slf4j.jarLog4j到Slf4j的橋梁jcl-over-slf4j.jarJCL到Slf4j的橋梁
小小實(shí)踐
從事Java開發(fā)的伙伴們都清楚,Spring框架內(nèi)部使用JCL做日志輸出標(biāo)準(zhǔn),可是項(xiàng)目使用Slf4j + Logback做日志輸出標(biāo)準(zhǔn),問題來了,怎樣才能讓項(xiàng)目?jī)?nèi)的Spring保持統(tǒng)一日志輸出標(biāo)準(zhǔn)呢?
其實(shí)非常簡(jiǎn)單,只需要引入正確的Slf4j橋接包,去除無用的日志組件即可。

我沒騙你們吧,引入jcl-over-slf4j.jar或jul-to-slf4j.jar問題就解決了,十分簡(jiǎn)單~
小結(jié)
本文到此就結(jié)束了,以上,通過Java日志發(fā)展史一步一步的帶大家理清Java日志間的關(guān)系,并拋出問題以及解決問題,相信看完后,大家不會(huì)再因?yàn)槿罩緲?biāo)準(zhǔn)庫之間復(fù)雜的關(guān)系感到煩惱,同時(shí)也能知其所以然的統(tǒng)一日志標(biāo)準(zhǔn)。
《對(duì)線面試官》系列目前也已經(jīng)連載21篇啦!有深度風(fēng)趣的系列!
【對(duì)線面試官】Java注解 【對(duì)線面試官】Java泛型 【對(duì)線面試官】 Java NIO 【對(duì)線面試官】Java反射 && 動(dòng)態(tài)代理 【對(duì)線面試官】多線程基礎(chǔ) 【對(duì)線面試官】 CAS 【對(duì)線面試官】synchronized 【對(duì)線面試官】AQS&&ReentrantLock 【對(duì)線面試官】線程池 【對(duì)線面試官】ThreadLocal 【對(duì)線面試官】CountDownLatch和CyclicBarrier 【對(duì)線面試官】List 【對(duì)線面試官】Map 【對(duì)線面試官】SpringMVC 【對(duì)線面試官】Spring基礎(chǔ) 【對(duì)線面試官】SpringBean生命周期 【對(duì)線面試官】Redis基礎(chǔ) 【對(duì)線面試官】Redis持久化 【對(duì)線面試官】Kafka基礎(chǔ) 【對(duì)線面試官】使用Kafka會(huì)考慮什么問題?

