今天看到了一篇對(duì)Java日志系統(tǒng)講解很不錯(cuò)的文章惯雳,所以做個(gè)學(xué)習(xí)記錄,如有侵權(quán)請(qǐng)聯(lián)系刪除
概述
???????Java的日志系統(tǒng)非常豐富坠陈,常用的有l(wèi)og4j、JUL捐康、logback等等仇矾,隨著日志系統(tǒng)的發(fā)展出現(xiàn)了日志框架commons-logging、slf4j
發(fā)展史
???????日志最早出現(xiàn)的是apache開源社區(qū)的log4j解总,是應(yīng)用最為廣泛的日志工具贮匕,然而sun公司在JDK1.4中增加了JUL日志實(shí)現(xiàn)企圖對(duì)抗log4j,同時(shí)斷斷續(xù)續(xù)也出現(xiàn)了其他的日志工具花枫,這就造成了混亂刻盐,因?yàn)檫@些日志系統(tǒng)互相沒有關(guān)聯(lián),替換和統(tǒng)一變得棘手劳翰,想象一下你的應(yīng)用使用了log4j敦锌,然后使用了其他團(tuán)隊(duì)的庫,而他們使用了JUL佳簸,那么你的應(yīng)用就需要使用兩個(gè)日志系統(tǒng)了乙墙,然后又有第二個(gè)庫使用了simplelog,這個(gè)時(shí)候估計(jì)你就會(huì)崩潰了...那么如何解決呢生均?進(jìn)行抽象听想,抽象出一個(gè)接口層對(duì)每個(gè)日志實(shí)現(xiàn)都適配或者轉(zhuǎn)接,提供給別人的庫都直接使用抽象層而具體的實(shí)現(xiàn)由使用者決定马胧。不錯(cuò)汉买,開源社區(qū)提供了commons-logging抽象,被稱為JCL佩脊,JCL確實(shí)出色的完成了兼容主流的日志實(shí)現(xiàn)(log4j蛙粘、JUL朽色、simplelog),基本一統(tǒng)江湖组题,就連大名鼎鼎的spring也是依賴了JCL葫男。然而好景不長,另一個(gè)優(yōu)秀的日志框架slf4j的出現(xiàn)使場面更加混亂崔列,而slf4j的作者(Ceki Gülcü)正是log4j的作者梢褐,他覺得JCL不夠優(yōu)秀所以要搞一套更優(yōu)雅的出來,于是slf4j誕生了赵讯,同時(shí)為slf4j實(shí)現(xiàn)了一個(gè)親兒子——logback盈咳。
???????slf4j確實(shí)更加優(yōu)雅,但是之前已有很多代碼庫已經(jīng)使用了JCL边翼,雖然出現(xiàn)了slf4j與JCL之間的橋接轉(zhuǎn)換鱼响,但是集成的時(shí)候依然問題多多,到此本來應(yīng)該完了组底,但是Ceki Gülcü覺得還是得回頭拯救下自己的“大阿哥”——log4j丈积,于是log4j2誕生了,同樣log4j2也參與到了slf4j日志體系中债鸡,想必將來會(huì)更加混亂......
JCL
???????使用JCL一般需要配置一個(gè)commons-logging.properties在classpath上江滨,這個(gè)文件有一行代碼:
org.apache.commons.logging.LogFactory= org.apache.commons.logging.impl.LogFactoryImpl
???????這個(gè)是告訴JCL我們要使用哪個(gè)日志實(shí)現(xiàn),JCL會(huì)在classpath下去加載對(duì)應(yīng)的日志工廠實(shí)現(xiàn)類厌均,具體的日志工廠實(shí)現(xiàn)類可以是log4j唬滑,也可以是jul等等。用戶主需要依賴JCL的api即可棺弊,對(duì)日志系統(tǒng)的替換主需要修改一下commons-logging.properties文件切換到對(duì)應(yīng)的日志工廠實(shí)現(xiàn)即可晶密,但是我們也可以看到因?yàn)镴CL是在運(yùn)行時(shí)去加載classpath下的實(shí)現(xiàn)類,會(huì)有classloader的問題模她。
slf4j
???????slf4j的設(shè)計(jì)確實(shí)比較優(yōu)雅稻艰,它采用了我們比較熟悉的方式——接口和實(shí)現(xiàn)分離,有個(gè)純粹的接口層slf4j-api工程缝驳,這個(gè)里面基本完全定義了日志的接口连锯,所以對(duì)于開發(fā)者來說只需要是這個(gè)即可归苍。
???????有接口就要有實(shí)現(xiàn)用狱,比較推崇的實(shí)現(xiàn)是logback,logback完全實(shí)現(xiàn)了slf4j-api的接口拼弃,并且性能是那個(gè)也比log4j更好夏伊,我們知道log4j的使用比較普遍,所以為了支持這部分用戶是必須的吻氧,slf4j-log4j12也實(shí)現(xiàn)了slf4j-api溺忧,這個(gè)算是對(duì)log4j的適配器咏连。同樣的道理,對(duì)JUL的是配置為slf4j-jdk14鲁森。
???????為了讓使用JCL等等其他其他日志系統(tǒng)的用戶可以很簡單的切換到slf4j上來祟滴,給出了各種橋接工程,例如:jcl-over-slf4j會(huì)把JCL的調(diào)用都橋接到slf4j上來(可以看出jcl-over-slf4j的api和JCL是相同的歌溉,所以這兩個(gè)jar是不能共存的)垄懂,jul-to-slf4j是把jul的調(diào)用橋接到slf4j上,log4j-over-slf4j是把log4j的調(diào)用橋接到slf4j痛垛,下面用一張圖來表示下這個(gè)家族的大致成員(紅線表示沖突)
???????如上圖所示草慧,最上層表示橋接層,中間是接口層匙头,最下層表示具體的實(shí)現(xiàn)漫谷,可以看出這個(gè)圖中所有的jar都是圍繞著slf4j活動(dòng)的,其中slf4j-jul的jar包名稱是slf4j-jdk14
???????slf4j-api和具體的實(shí)現(xiàn)層是怎么綁定的呢蹂析?這個(gè)其實(shí)是在編譯時(shí)綁定的舔示,它可以不需要像使用JCL那樣需要配置一下,只需要把slf4j-api和slf4j-log4j放到classpath上电抚,即實(shí)現(xiàn)綁定斩郎。原理可以下載slf4j-api的源碼查看,這個(gè)設(shè)計(jì)還是很巧妙的喻频,slf4j-api中會(huì)去調(diào)用StaticLoggerBinder這個(gè)類獲取綁定的工廠類缩宜,而每個(gè)日志實(shí)現(xiàn)會(huì)在自己的jar中提供這樣一個(gè)類,這樣slf4j-api就實(shí)現(xiàn)了編譯時(shí)綁定實(shí)現(xiàn)甥温。但是這樣接口的源碼編譯需要依賴具體的實(shí)現(xiàn)了锻煌,不太合理吧?這里容易讓人迷惑姻蚓,因?yàn)榇蜷_slf4j-api的jar宋梧,看不到StaticLoggerBinder,當(dāng)我們?nèi)ゲ榭磗lf4j-api的源碼狰挡,在源碼中看到了StaticLoggerBinder這個(gè)類捂龄,猜想應(yīng)該是slf4j-api在打包過程中有動(dòng)作,刪除了自己包中的那個(gè)類加叁,結(jié)果不出所料倦沧,確實(shí)是pom中的ant-task給處理了,pom中處理方式如下:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-antrun-plugin</artifactId>
<executions>
<execution>
<phase>process-classes</phase>
<goals>
<goal>run</goal>
</goals>
</execution>
</executions>
<configuration>
<tasks>
<echo>Removing slf4j-api's dummy StaticLoggerBinder and StaticMarkerBinder</echo>
<delete dir="target/classes/org/slf4j/impl"/>
</tasks>
</configuration>
</plugin>
???????打出來的slf4j-api的包是"不完整"的它匕,只有找到包含StaticLoggerBinder這個(gè)類的包才可以展融,于是slf4j-log4j和logback-classic都提供了這個(gè)類。另外豫柬,slf4j-log4j和logback以及slf4j-jdk14是不能同時(shí)和slf4j共存的告希,也就是說只能有一個(gè)實(shí)現(xiàn)存在扑浸,不然啟動(dòng)會(huì)提示有多個(gè)綁定。
???????同時(shí)這個(gè)圖中橋階層和對(duì)應(yīng)的實(shí)現(xiàn)jar是不能共存的燕偶,比如log4j-over-slf4j和slf4j-log4j喝噪,jul-to-slf4j和slf4j-jdk14,這個(gè)很好理解指么,會(huì)有死循環(huán)仙逻,啟動(dòng)也會(huì)報(bào)錯(cuò)。也就是說jar之前有互斥性涧尿。
???????當(dāng)然slf4j也提供了可以把對(duì)slf4j的調(diào)用橋接到JCL上的工程包——slf4j-jcl系奉,可以看出slf4j的設(shè)計(jì)者考慮非常周到,想想這樣的情況:遺留系統(tǒng)使用的是JCL+log4j姑廉,因?yàn)橄到y(tǒng)功能演進(jìn)缺亮,依賴了其他業(yè)務(wù)線的庫,恰好那個(gè)庫依賴了slf4j-api桥言,并且應(yīng)用需要關(guān)心這個(gè)庫的日志萌踱,那么就需要轉(zhuǎn)接日志到JCL上即可。細(xì)心的你可能一經(jīng)發(fā)現(xiàn)号阿,slf4j-jcl和jcl-over-slf4j也是互斥的并鸵,太多互斥的了.......
???????對(duì)于log4j2的加入,也很簡單扔涧,和logback是很相似的园担,如下圖:
紅線依然表示依賴的互斥,當(dāng)然log4j-slf4j-impl也會(huì)和logback-classic枯夜、slf4j-log4j弯汰、slf4j-jdk14互斥。
常見的問題:
slf4j-api和實(shí)現(xiàn)版本不對(duì)應(yīng)湖雹,尤其是1.6.x和1.5.x不兼容咏闪,如果沒有特殊需求,直接升級(jí)到最新版本摔吏。
slf4j的多個(gè)實(shí)現(xiàn)同時(shí)存在鸽嫂,比如slf4j-log4j和logback-classic,排除其中一個(gè)即可征讲。
log4j和logback不能同時(shí)使用据某?可以同時(shí)使用,這兩個(gè)并不矛盾稳诚,遺留系統(tǒng)可能直接使用了log4j的代碼哗脖,并且不能通過log4j-over-slf4j橋接,那么可以讓他繼續(xù)使用log4j扳还,這里(http://www.slf4j.org/legacy.html)有詳細(xì)的介紹才避。
該如何選用這些呢?建議在非特殊情況下氨距,都使用slf4j-api+logback桑逝,不要直接使用日志實(shí)現(xiàn),性能沒什么影響俏让。對(duì)于要提供給別人的類庫楞遏,建議使用slf4j-api,使用方可以自由選擇具體的實(shí)現(xiàn)首昔,并且建議類庫不要依賴具體的日志實(shí)現(xiàn)寡喝。對(duì)于自己的桌面小應(yīng)用,可以直接使用log4j勒奇,畢竟只是隨便做做预鬓。
logback因?yàn)槟居衧pring提供的啟動(dòng)listener,所以要自己寫赊颠?可以看看這里(https://github.com/qos-ch/logback-extensions)格二,開源社區(qū)已經(jīng)做好了。
日志系統(tǒng)一般不會(huì)影響到系統(tǒng)性能竣蹦,除非你的系統(tǒng)對(duì)性能非扯ゲ拢苛刻,如果這樣你可以考慮使用Blitz4j(https://github.com/Netflix/blitz4j)痘括,這個(gè)是Netflix(http://netflix.github.io/)社區(qū)對(duì)log4j的性能改進(jìn)版长窄,不過他們依然建議去使用log4j或者logback。