超贊!20年以來Java日志Log4j2發(fā)展史!
你知道的越多,不知道的就越多,業(yè)余的像一棵小草!
你來,我們一起精進!你不來,我和你的競爭對手一起精進!
編輯:業(yè)余草
segmentfault.com/u/imango
推薦:https://www.xttblog.com/?p=5300
這兩天Log4j經(jīng)常霸占頭條,繼前兩天的漏洞修復(fù)和浮現(xiàn)的文章后,今天我們再來聊一聊 Log4j 的發(fā)展史!
前言
最早開始擼碼當時就遇到幾次日志jar包沖突的問題,當時也是很煩躁,畢竟了解的也不多,什么那里4j,這里4j,還有什么橋接包,而且在我感覺他們的包名都還差不多??!我當時是比較懵逼的,上網(wǎng)搜了下,隨便看到一個類似這種橋接過去橋接過來的圖我就懵,都是些啥玩意兒。

最近項目中突然發(fā)現(xiàn)我們自己的工程對于日志的記錄也不是很好,很多各種日志的注解大家用的也不一樣,有@Log4j,也有@Slf4j的,也有@Log4j2的,索性干脆徹底去了解一下日志系統(tǒng)。
由于個人一直覺得現(xiàn)在學(xué)習(xí)技術(shù)是很好的年代,畢竟我們在前人已經(jīng)踩過的坑,總結(jié)過的經(jīng)驗之上來學(xué)習(xí),相當于站在了巨人的肩膀上,但同時也是最壞的時代,我們對于新技術(shù)可能還是很了解,但是這些新技術(shù)到底為什么出現(xiàn),它們到底解決了什么問題,時代選擇了這些技術(shù)一定是有原因的,我們不清楚技術(shù)發(fā)展的歷史,就像國外不清楚我們中華民族的歷史,所以會曲解我們一樣。
言歸正傳,雖然看著日志系統(tǒng)很亂,很難梳理清楚,可能名字都不是很好記,但我們要是從日志系統(tǒng)本身的發(fā)展歷史去了解一次,可能就會更加明白為什么會出現(xiàn)現(xiàn)在這種情況了。
發(fā)展歷史
System.out和System.err
這應(yīng)該是最早的日志記錄方式吧,但是不靈活也不可配置,要么就是全部打印,要么就是全部不打印,沒有一個統(tǒng)一的日志級別。另外性能也不高,還讓程序變成串行化,具體可以參考:都 9102 了,你還不知道 System.out.println 的危害!
Log4j
在1996年初,E.U.SEMPER(歐洲安全電子市場)項目決定編寫自己的跟蹤API,最后該API演變?yōu)?code style="margin-right: 2px;margin-left: 2px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(53, 148, 247);background: rgba(59, 170, 250, 0.1);padding-right: 2px;padding-left: 2px;border-radius: 2px;height: 21px;line-height: 22px;">Log4j,Log4j日志軟件包一經(jīng)推出就備受歡迎,當然這里必須要提到一個人,就是Log4j的主要貢獻者,這個大佬:Ceki Gülcü。
Ceki Gülcü
可能應(yīng)該叫巨佬了。。。后面你就明白了,后來Log4j成為了Apache基金會項目中的一員,同時Log4j的火爆,讓Log4j一度成為業(yè)內(nèi)日志標桿。(據(jù)說Apache基金會還曾經(jīng)建議Sun引入Log4j到java的標準庫中,但是sun拒絕了)。
JUL(Java Util Logging)
果然Sun有自己的考慮,2002年2月Java1.4發(fā)布,Sun竟然推出了自己的日志庫Java Util Logging,其實很多日志的實現(xiàn)思想也都是仿照Log4j,畢竟Log4j先出來很多年了,已經(jīng)很成熟了此時,這兩個日志工具打架,顯然Log4j是更勝一籌。
它們打架感覺就是互相競爭,Sun心里可能在想,不就是做個日志工具嘛,誰不會!當然好景不長。
JCL(Jakarta Commons Logging)
Apache: 玩編程,誰玩的過我!你不讓我成為JDK標準,我就自己成為日志標準,哼?。ㄍ耆珎€人意淫。。。)
于是JUL剛出來不久,2002年8月Apache又推出了日志接口Jakarta Commons Logging,也就是日志抽象層,當然也提供了一個默認實現(xiàn)Simple Log,這野心很大,想一統(tǒng)日志抽象(就像以前的JDBC一統(tǒng)數(shù)據(jù)庫訪問層),讓日志產(chǎn)品去實現(xiàn)它的抽象,這樣只要你的日志代碼依賴的是JCL的接口,你就可以很方便的在Log4j和JUL之間做切換,當時日志領(lǐng)域大概是這樣的結(jié)構(gòu),當然也還是方便理解的,也很優(yōu)雅。

但是好景不長,在使用過程中,雖然現(xiàn)在日志系統(tǒng)在JCL的統(tǒng)一下很優(yōu)雅,很美好,但大家發(fā)現(xiàn)了JCL還不夠好,有些人甚至認為JCL造成的問題比解決的問題還多...emmm
?http://tapestryjava.blogspot.com/2007/08/so-long-commons-logging-hello-slf4j.html
?

Slf4j(Simple Logging Facade for Java)
所以大佬粗線,「Ceki Gülcü」(也就是Log4j的作者)由于一些原因離開了Apache,之后覺得JCL不好,于是于2005年自己擼出一個新工程,也就是一套新日志接口(有得也叫日志門面):Slf4j(Simple Logging Facade for Java),感覺粗來了么。。。這戰(zhàn)爭的硝煙,明顯這個Slf4j是直指JCL啊,但是后面確實也證明了Slf4j是要比JCL在很多地方更優(yōu)秀。
「Ceki Gülcü」:玩接口,我一個人就是一支軍隊!
但是由于Slf4j出來的較晚,而且還只是一個日志接口,所以之前已經(jīng)出現(xiàn)的日志產(chǎn)品,如JUL和Log4j都是沒有實現(xiàn)這個接口的,所以尷尬的是光有一個接口,沒有實現(xiàn)的產(chǎn)品也是很憋屈啊,就算開發(fā)者想用Slf4j也是用不了,這時候,大佬發(fā)話了。
「Ceki Gülcü」:別急,我早幫你們想好了,要讓Sun或者Apache這兩個龐然大物來實現(xiàn)我的接口,太南啦,老鐵,但。。。我?guī)湍銈儗崿F(xiàn),不就完了么。。。
于是大佬「Ceki Gülcü」擼出了之前提到的橋接包,也就是這種類似適配器模式。

好了,大佬提供了橋接包,于是日志系統(tǒng)現(xiàn)在有了這樣的結(jié)構(gòu)。

但是其實之前很多Java應(yīng)用應(yīng)該依賴的JCL,所以光有日志產(chǎn)品橋接包,好像還不夠
「Ceki Gülcü」:沒問題,不就是不夠橋接包么,我寫,我來證明Slf4j是最完美的
于是有了JCL的橋接包。

JCL的橋接包相當于此時的橋接包就是分了兩種場景
之前 Java應(yīng)用用的日志接口(如JCL)之前 Java應(yīng)用用的日志產(chǎn)品(如Log4j)
那好,那我們?nèi)绻倏紤]一下這種場景呢?
假設(shè)哈,你的Java應(yīng)用使用了Spring的第三方的框架,但是假設(shè)Spring默認用JCL,并且最終用的JUL打印的日志,但是你的系統(tǒng)使用了Slf4j作為日志接口,日志產(chǎn)品使用了Log4j,那。。。不出意外的話。。。你將有兩種日志輸出,兩種日志的打印方式不統(tǒng)一,到時候解決bug的時候就很惱火,而且配置日志的配置文件還需要兩份。

所以為了方便統(tǒng)一應(yīng)用中的所有日志,大佬發(fā)話了。
「Ceki Gülcü」:沒事,大家都選擇用Slf4j統(tǒng)一吧,我來幫大家統(tǒng)一,沒有事是橋接包解決不了的,有的話,那就再來個。

當然此時這種場景也是符合之前說的兩種情況的,因此現(xiàn)在日志系統(tǒng)大體應(yīng)該是這樣的。

總結(jié)一句話就是:大佬擼完。。。
但好景又不長,大佬畢竟是大佬,Log4j不就是自己寫的么,所以最清楚Log4j缺點的人也正是他。
Logback
由于使用Slf4j,需要一次橋接包,也就是之前的日志產(chǎn)品都不是正統(tǒng)的Slf4j的實現(xiàn),因此,2006年,出自「Ceki Gülcü」之手的日志產(chǎn)品Logback應(yīng)運而生,而且大佬還專門寫了一篇:
?http://logback.qos.ch/reasonsToSwitch.html
?

是不是這太針對了。。。哈哈哈哈,就是這么無情,當然都是他寫的,他肯定是最清楚這兩者實現(xiàn)的區(qū)別。
肯定的,Logback是完美實現(xiàn)了Slf4j,于是現(xiàn)在日志系統(tǒng)變成了。

ok了,現(xiàn)在咱們有了2個日志接口,3個日志產(chǎn)品,大家也都看起來相安無事。。。但。。。Slf4j+Logback的模式,顯然很沖擊JCL+Log4j,并且本身Logback確實比Log4j性能更優(yōu),設(shè)計更為合理,所以。。。老東家Apache可就坐不住了。
Log4j2
在2012年,Apache直接推出新項目,不是Log4j1.x升級,而是新項目Log4j2,因為Log4j2是完全不兼容Log4j1.x的。
并且很微妙的,Log4j2幾乎涵蓋Logback所有的特性(這不是對著干是啥~而且還有抄襲的嫌疑。。。哈哈哈),更甚者的Log4j2也搞了分離的設(shè)計,分化成log4j-api和log4j-core,這個log4j-api也是日志接口,log4j-core才是日志產(chǎn)品。。。
emmm,我看到這,我都有點崩潰了。
現(xiàn)在我們可有了3個日志接口,以及4個日志產(chǎn)品。。。當然Apache也知道該做啥,為了讓大家可以接入自己的Log4j2,那不就是橋嘛,不就是橋嘛,Apache也麻溜的推出了它的橋接包,所以。。。
唉,所以我都不敢畫圖了。。。
前方高能?。?!
前方高能?。?!
前方高能!?。?/span>

總結(jié)到這,我們可以吸取什么經(jīng)驗么?
不寫接口的壞處 沒有什么問題是加一個層適配器(接口)解決不了的,如果有,那就再加一層
最佳實踐
了解了日志的發(fā)展歷史,那現(xiàn)在我們再回過頭來看看如果,你的系統(tǒng)在選擇日志方案的時候,如何抉擇呢?畢竟我們3個日志接口,以及4個日志產(chǎn)品。
顯然第一點是使用日志接口的
API而不是直接使用日志產(chǎn)品的API這一條也是必須的,也是符合依賴倒置原則的,我們應(yīng)該依賴日志的抽象,而不是日志的實現(xiàn)
日志產(chǎn)品的依賴只添加一個
當然也這個也是必須的,依賴多個日志產(chǎn)品,只會讓自己的應(yīng)用處理日志顯得更復(fù)雜,不可統(tǒng)一控制
把日志產(chǎn)品的依賴設(shè)置為
Optional和runtime scope其中
Optional是為了依賴不會被傳遞,比如別的人引用你這個jar,就會被迫使用不想用的日志依賴
??<dependency>
??????<groupId>org.apache.logging.log4jgroupId>
??????<artifactId>log4j-coreartifactId>
??????<version>${log4j.version}version>
??????<optional>trueoptional>
??dependency>
而scope設(shè)置為runtime,是可以保證日志的產(chǎn)品的依賴只有在運行時需要,編譯時不需要,這樣,開發(fā)人員就不會在編寫代碼的過程中使用到日志產(chǎn)品的API了
<dependency>
????<groupId>org.apache.logging.log4jgroupId>
????<artifactId>log4j-slf4j-implartifactId>
????<version>${log4j.version}version>
????<scope>runtimescope>
dependency>
謝謝觀看o( ̄▽ ̄)d
最后留一個懸念,我們的大佬「Ceki Gülcü」為啥離開apache?
