【可能是全網(wǎng)最全的】JAVA日志框架適配/沖突解決方案

前言

你是否遇到過配置了日志土童,但打印不出來的情況工坊?

你是否遇到過配置了logback栅组,啟動時卻提示log4j錯誤的情況?像下面這樣:

log4j:WARN No appenders could be found for logger (org.example.App).
log4j:WARN Please initialize the log4j system properly.
log4j:WARN See http://logging.apache.org/log4j/1.2/faq.html#noconfig for more info.

你是否遇到過SLF4J的這種報錯刃麸?

SLF4J: Class path contains multiple SLF4J bindings.
SLF4J: Found binding in [jar:file:/C:/Users/jiang/.m2/repository/ch/qos/logback/logback-classic/1.2.3/logback-classic-1.2.3.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: Found binding in [jar:file:/C:/Users/jiang/.m2/repository/org/slf4j/slf4j-log4j12/1.7.30/slf4j-log4j12-1.7.30.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: See http://www.slf4j.org/codes.html#multiple_bindings for an explanation.
SLF4J: Actual binding is of type [ch.qos.logback.classic.util.ContextSelectorStaticBinder]

你是否遇到過DUBBO日志打印不正常的情況泊业?

你是否遇到過Mybatis SQL日志打印不出來的情況吁伺?

你是否遇到過JPA/Hibernate SQL日志無法打印的情況租谈?

你是否遇到過復雜項目中割去,很多框架內(nèi)部日志無法打印的情況?

你是否遇到過Tomcat工程夸赫,日志文件打印了多份咖城,catalina.out和其他文件?

你是否遇到過SpringBoot項目握础,日志文件打印了多份的問題揭绑?

你是否遇到過各種日志配置問題……


日志框架的沖突

上面的這些問題他匪,基本都是由于多套日志框架共存或配置錯誤導致的夸研。那么為什么會出現(xiàn)共存或者沖突呢亥至?

一般是以下幾種原因:

  1. 項目手動引用了各種日志框架的包 - 比如同時引用了log4j/log4j2/logback/jboss-logging/jcl等
  2. 包管理工具的傳遞依賴(Transitive Dependencies)導致絮供,比如依賴了dubbo,但是dubbo依賴了zkclient,可zkclient又依賴了log4j,此時如果你的項目中還有其他日志框架存在并有使用,那么就會導致多套共存
  3. 同一個日志框架多版本共存

JAVA里的各種日志框架

在正式介紹沖突和解決之前浓恳,需要先簡單的說一下Java中的各種日志框架:

Java 中的日志框架分為兩種颈将,分別為日志抽象/門面讹堤,日志實現(xiàn)

日志抽象/門面

日志抽象/門面洲守,他們不負責具體的日志打印沾凄,如輸出到文件撒蟀、配置日志內(nèi)容格式等温鸽。他們只是一套日志抽象,定義了一套統(tǒng)一的日志打印標準姑尺,如Logger對象蝠猬,Level對象榆芦。

slf4j(Simple Logging Facade for Java)和jcl(Apache Commons Logging)這兩個日志框架就是JAVA中最主流的日志抽象了匆绣。還有一個jboss-logging,主要用于jboss系列軟件堪夭,比如hibernate之類凯力。像 jcl已經(jīng)多年不更新了(上一次更新時間還是14年)咐鹤,目前最推薦的是使用 slf4j

日志實現(xiàn)

Java 中的日志實現(xiàn)框架祈惶,主流的有以下幾種:

  1. log4j - Apache(老牌日志框架,不過多年不更新了凡涩,新版本為log4j2)
  2. log4j2 - Apache(log4j 的新版本活箕,目前異步IO性能最強可款,配置也較簡單)
  3. logback - QOS(slf4j就是這家公司的產(chǎn)品)
  4. jul(java.util.logging) - jdk內(nèi)置

在程序中,可以直接使用日志框架埃叭,也可以使用日志抽象+日志實現(xiàn)搭配的方案悉罕。不過一般都是用日志抽象+日志實現(xiàn)壁袄,這樣更靈活,適配起來更簡單莺奔。

目前最主流的方案是slf4j+logback/log4j2,不過如果是jboss系列的產(chǎn)品恼琼,可能用的更多的還是jboss-logging晴竞,畢竟親兒子嘛噩死。像JPA/Hibernate這種框架里,內(nèi)置的就是jboss-logging

SpringBoot + Dubbo 日志框架沖突的例子

舉個例子來說個最常見的傳遞依賴導致的共存沖突

比如我有一個“干凈的”spring-boot項目行嗤,干凈到只有一個spring-boot-starter依賴栅屏,此時我想集成dubbo,使用zookeeper作為注冊中心堂鲜,此時我的依賴配置是這樣:

<dependencies>
  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter</artifactId>
  </dependency>
  <dependency>
    <groupId>org.apache.dubbo</groupId>
    <artifactId>dubbo-spring-boot-starter</artifactId>
    <version>2.7.9</version>
  </dependency>
  <dependency>
    <groupId>org.apache.dubbo</groupId>
    <artifactId>dubbo-registry-zookeeper</artifactId>
    <version>2.7.9</version>
  </dependency>
</dependencies>

現(xiàn)在啟動這個spring-boot項目栈雳,會發(fā)現(xiàn)一堆紅色錯誤:

SLF4J: Class path contains multiple SLF4J bindings.
SLF4J: Found binding in [jar:file:/C:/Users/jiang/.m2/repository/ch/qos/logback/logback-classic/1.2.3/logback-classic-1.2.3.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: Found binding in [jar:file:/C:/Users/jiang/.m2/repository/org/slf4j/slf4j-log4j12/1.7.30/slf4j-log4j12-1.7.30.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: See http://www.slf4j.org/codes.html#multiple_bindings for an explanation.
SLF4J: Actual binding is of type [ch.qos.logback.classic.util.ContextSelectorStaticBinder]
----------------------------------人肉分割線----------------------------------------
log4j:WARN No appenders could be found for logger (org.apache.dubbo.common.logger.LoggerFactory).
log4j:WARN Please initialize the log4j system properly.
log4j:WARN See http://logging.apache.org/log4j/1.2/faq.html#noconfig for more info.

從錯誤提示上看,錯誤內(nèi)容分為兩個部分:

  1. slf4j報錯缔莲,提示找到多個slf4j的日志綁定
  2. log4j報錯哥纫,提示log4j沒有appender配置

出現(xiàn)這個錯誤,就是因為dubbo的傳遞依賴中含有l(wèi)og4j痴奏,但是spring-boot的默認配置是slf4j+logback蛀骇。在依賴了dubbo相關包之后厌秒,現(xiàn)在項目中同時存在logback/jcl(apache commons-logging)/log4j/jul-to-slf4j/slf4j-log4j/log4j-to-slf4j

來看一下依賴圖:


springboot-sample.png

這個時候就亂套了松靡,slf4j-log4j是log4j的slf4j實現(xiàn)简僧,作用是調(diào)用slf4j api的時候使用log4j輸出;而log4j-to-slf4j的作用是將log4j的實現(xiàn)替換為log4j雕欺,這樣一來不是死循環(huán)了

而且還有l(wèi)ogback的存在岛马,logback默認實現(xiàn)了slf4j的抽象,而slf4j-log4j也是一樣實現(xiàn)了slf4j的抽象屠列,logback啦逆,項目里共存了兩套slf4j的實現(xiàn),那么在使用slf4j接口打印的時候會使用哪個實現(xiàn)呢笛洛?

答案是“第一個”夏志,也就是第一個被加載的Slf4j的實現(xiàn)類,但這種依靠ClassLoader加載順序來保證的日志配置順序是非常不靠譜的

如果想正常使用日志苛让,讓這個項目里所有的框架都正常打印日志沟蔑,必須將日志框架統(tǒng)一。不過這里的統(tǒng)一并不是至強行修改狱杰,而是用“適配/中轉(zhuǎn)”的方式瘦材。

現(xiàn)在項目里雖然有slf4j-log4j的配置,但這個配置是適配log4j2用的仿畸,而我們的依賴了只有l(wèi)og4j1食棕,實際上這個中轉(zhuǎn)是無效的。但logback是有效的错沽,而且是spring-boot項目的默認配置簿晓,這次就選擇logback作為項目的統(tǒng)一日志框架吧。

現(xiàn)在項目里存在log4j(1)的包千埃,而且啟動時又報log4j的錯誤憔儿,說明某些代碼調(diào)用了log4j的api。但我們又不想用log4j镰禾,所以需要先解決log4j的問題皿曲。

由于有l(wèi)og4j代碼的引用,所以直接刪除log4j一定是不可行的吴侦。slf4j提供了一個log4j-over-slf4j的包屋休,這個包復制了一份log4j1的接口類(Logger等),同時將實現(xiàn)類修改為slf4j了备韧。

所以將log4j的(傳遞)依賴排除劫樟,同時引用log4j-over-slf4j,就解決了這個log4j的問題。現(xiàn)在來修改下pom中的依賴(查看依賴圖可以使用maven的命令叠艳,或者是IDEA自帶的Maven Dependencies Diagram奶陈,再或者Maven Helper之類的插件)

<dependency>
  <groupId>org.apache.dubbo</groupId>
  <artifactId>dubbo-registry-zookeeper</artifactId>
  <version>2.7.9</version>
  <scope>compile</scope>
  <!--排除log4j-->
  <exclusions>
    <exclusion>
      <artifactId>log4j</artifactId>
      <groupId>log4j</groupId>
    </exclusion>
  </exclusions>
</dependency>
<!--增加log4j-slf4j -->
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>log4j-over-slf4j</artifactId>
    <version>1.7.30</version>
</dependency>

解決了log4j的問題之后,現(xiàn)在還有slf4j有兩個實現(xiàn)的問題附较,這個問題處理就更簡單了吃粒。由于我們計劃使用logback,那么只需要排除/刪除slf4j-log4j這個實現(xiàn)的依賴即可

<dependency>
  <groupId>org.apache.dubbo</groupId>
  <artifactId>dubbo-registry-zookeeper</artifactId>
  <version>2.7.9</version>
  <scope>compile</scope>
  <exclusions>
    <exclusion>
      <artifactId>log4j</artifactId>
      <groupId>log4j</groupId>
    </exclusion>
    <exclusion>
      <artifactId>slf4j-log4j12</artifactId>
      <groupId>org.slf4j</groupId>
    </exclusion>
  </exclusions>
</dependency>

修改完成拒课,再次啟動就沒有錯誤了徐勃,輕松解決問題

日志適配大全

上面只是介紹了一種轉(zhuǎn)換的方式,但這么多日志框架早像,他們之間是可以互相轉(zhuǎn)換的僻肖。不過最終目的都是統(tǒng)一一套日志框架,讓最終的日志實現(xiàn)只有一套
**
這么多的日志適配/轉(zhuǎn)換方式卢鹦,全記住肯定是有點難臀脏。為此我畫了一張可能是全網(wǎng)最全的日志框架適配圖(原圖尺寸較大,請點擊放大查看)冀自,如果再遇到?jīng)_突揉稚,需要將一個日志框架轉(zhuǎn)換到另一款的時候,只需要按照圖上的路徑熬粗,引入相關的依賴包即可窃植。
**

image.png

比如想把slf4j,適配/轉(zhuǎn)換到log4j2荐糜。按照圖上的路徑,只需要引用log4j-slf4j-impl即可葛超。

如果想把jcl暴氏,適配/轉(zhuǎn)換到slf4j,只需要刪除jcl包绣张,然后引用jcl-over-slf4j即可答渔。

圖上的箭頭,有些標了文字的侥涵,是需要額外包進行轉(zhuǎn)換的沼撕,有些沒有標文字的,是內(nèi)置了適配的實現(xiàn)芜飘。其實內(nèi)置實現(xiàn)的這種會更麻煩务豺,因為如果遇到共存基本都需要通過配置環(huán)境變量/配置額外屬性的方式來指定一款日志實現(xiàn)。

目前slf4j是適配方案中嗦明,最核心的那個框架笼沥,算是這個圖的中心樞紐。只要圍繞slf4j做適配/轉(zhuǎn)化,就沒有處理不了的沖突

總結(jié)

解決日志框架共存/沖突問題其實很簡單奔浅,只要遵循幾個原則:

  1. 統(tǒng)一使用一套日志實現(xiàn)
  2. 刪除多余的無用日志依賴
  3. 如果有引用必須共存的話馆纳,那么就移除原始包,使用“over”類型的包(over類型的包復制了一份原始接口汹桦,重新實現(xiàn))
  4. 不能over的鲁驶,使用日志抽象提供的指定方式,例如jboss-logging中钥弯,可以通過org.jboss.logging.provider環(huán)境變量指定一個具體的日志框架實現(xiàn)

項目里統(tǒng)一了日志框架之后寿羞,無論用那種日志框架打印,最終還是走向我們中轉(zhuǎn)/適配后的唯一一個日志框架玖院。

解決了共存/沖突之后,項目里就只剩一款日志框架郊酒。再也不會出現(xiàn)“日志打不出”,“日志配置不生效”之類的各種惡心問題褐健,下班都能早點了!

原創(chuàng)不易谁不,轉(zhuǎn)載請在開頭著名文章來源和作者烛缔。如果我的文章對您有幫助,請點贊/收藏/關注鼓勵支持晕翠。

最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
禁止轉(zhuǎn)載爸邢,如需轉(zhuǎn)載請通過簡信或評論聯(lián)系作者碌尔。
  • 序言:七十年代末待诅,一起剝皮案震驚了整個濱河市卑雁,隨后出現(xiàn)的幾起案子锐膜,更是在濱河造成了極大的恐慌而柑,老刑警劉巖媒咳,帶你破解...
    沈念sama閱讀 221,695評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件射富,死亡現(xiàn)場離奇詭異,居然都是意外死亡胰耗,警方通過查閱死者的電腦和手機限次,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,569評論 3 399
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來柴灯,“玉大人卖漫,你說我怎么就攤上這事≡海” “怎么了羊始?”我有些...
    開封第一講書人閱讀 168,130評論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長查描。 經(jīng)常有香客問我突委,道長,這世上最難降的妖魔是什么叹誉? 我笑而不...
    開封第一講書人閱讀 59,648評論 1 297
  • 正文 為了忘掉前任鸯两,我火速辦了婚禮,結(jié)果婚禮上钧唐,老公的妹妹穿的比我還像新娘。我一直安慰自己匠襟,他們只是感情好钝侠,可當我...
    茶點故事閱讀 68,655評論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著酸舍,像睡著了一般帅韧。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上啃勉,一...
    開封第一講書人閱讀 52,268評論 1 309
  • 那天忽舟,我揣著相機與錄音,去河邊找鬼淮阐。 笑死叮阅,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的泣特。 我是一名探鬼主播浩姥,決...
    沈念sama閱讀 40,835評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼状您!你這毒婦竟也來了勒叠?” 一聲冷哼從身側(cè)響起兜挨,我...
    開封第一講書人閱讀 39,740評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎眯分,沒想到半個月后拌汇,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,286評論 1 318
  • 正文 獨居荒郊野嶺守林人離奇死亡颗搂,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,375評論 3 340
  • 正文 我和宋清朗相戀三年担猛,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片丢氢。...
    茶點故事閱讀 40,505評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡傅联,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出疚察,到底是詐尸還是另有隱情蒸走,我是刑警寧澤,帶...
    沈念sama閱讀 36,185評論 5 350
  • 正文 年R本政府宣布貌嫡,位于F島的核電站比驻,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏岛抄。R本人自食惡果不足惜别惦,卻給世界環(huán)境...
    茶點故事閱讀 41,873評論 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望夫椭。 院中可真熱鬧掸掸,春花似錦、人聲如沸蹭秋。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,357評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽仁讨。三九已至羽莺,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間洞豁,已是汗流浹背盐固。 一陣腳步聲響...
    開封第一講書人閱讀 33,466評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留丈挟,地道東北人刁卜。 一個月前我還...
    沈念sama閱讀 48,921評論 3 376
  • 正文 我出身青樓,卻偏偏與公主長得像礁哄,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子溪北,可洞房花燭夜當晚...
    茶點故事閱讀 45,515評論 2 359