<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          教你全方位解決Java 日志框架沖突!

          共 5847字,需瀏覽 12分鐘

           ·

          2022-05-28 12:35

          不點(diǎn)藍(lán)字關(guān)注,我們哪來(lái)故事?


          一個(gè)指導(dǎo)程序員進(jìn)入大公司/獨(dú)角獸?的精品社群,致力于分享職場(chǎng)達(dá)人的專業(yè)打法,包括「學(xué)習(xí)路線+簡(jiǎn)歷模板+實(shí)習(xí)避坑+筆試面試+試用轉(zhuǎn)正+升職加薪+跳槽技巧」。

          點(diǎn)這里去了解,劍指大廠吧!








          你是否遇到過(guò)配置了日志,但打印不出來(lái)的情況?你是否遇到過(guò)配置了 logback,啟動(dòng)時(shí)卻提示 log4j 錯(cuò)誤的情況?像下面這樣:
          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.

          你是否遇到過(guò) SLF4J 的這種報(bào)錯(cuò)?

          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]

          你是否遇到過(guò) DUBBO 日志打印不正常的情況?

          你是否遇到過(guò) Mybatis SQL 日志打印不出來(lái)的情況?

          你是否遇到過(guò) JPA/Hibernate SQL 日志無(wú)法打印的情況?

          你是否遇到過(guò)復(fù)雜項(xiàng)目中,很多框架內(nèi)部日志無(wú)法打印的情況?

          你是否遇到過(guò) Tomcat 工程,日志文件打印了多份,catalina.out 和其他文件?

          你是否遇到過(guò) SpringBoot 項(xiàng)目,日志文件打印了多份的問(wèn)題?

          你是否遇到過(guò)各種日志配置問(wèn)題……

          日志框架的沖突

          上面的這些問(wèn)題,基本都是由于多套日志框架共存或配置錯(cuò)誤導(dǎo)致的。那么為什么會(huì)出現(xiàn)共存或者沖突呢?

          一般是以下幾種原因:

          1. 項(xiàng)目手動(dòng)引用了各種日志框架的包 - 比如同時(shí)引用了 log4j/log4j2/logback/jboss-logging/jcl 等

          2. 包管理工具的傳遞依賴(Transitive Dependencies)導(dǎo)致,比如依賴了 dubbo,但是 dubbo 依賴了 zkclient,可 zkclient 又依賴了 log4j,此時(shí)如果你的項(xiàng)目中還有其他日志框架存在并有使用,那么就會(huì)導(dǎo)致多套共存

          3. 同一個(gè)日志框架多版本共存

          JAVA 里的各種日志框架

          在正式介紹沖突和解決之前,需要先簡(jiǎn)單的說(shuō)一下 Java 中的各種日志框架:

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

          日志抽象/門面

          日志抽象/門面,他們不負(fù)責(zé)具體的日志打印,如輸出到文件、配置日志內(nèi)容格式等。他們只是一套日志抽象,定義了一套統(tǒng)一的日志打印標(biāo)準(zhǔn),如 Logger 對(duì)象,Level 對(duì)象。

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

          日志實(shí)現(xiàn)

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

          1. log4j - Apache(老牌日志框架,不過(guò)多年不更新了,新版本為 log4j2)

          2. log4j2 - Apache(log4j 的新版本,目前異步 IO 性能最強(qiáng),配置也較簡(jiǎn)單)

          3. logback - QOS(slf4j 就是這家公司的產(chǎn)品)

          4. jul(java.util.logging) - jdk 內(nèi)置

          在程序中,可以直接使用日志框架,也可以使用日志抽象+日志實(shí)現(xiàn)搭配的方案。不過(guò)一般都是用日志抽象+日志實(shí)現(xiàn),這樣更靈活,適配起來(lái)更簡(jiǎn)單。

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

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

          舉個(gè)例子來(lái)說(shuō)個(gè)最常見(jiàn)的傳遞依賴導(dǎo)致的共存沖突

          比如我有一個(gè)“干凈的”spring-boot 項(xiàng)目,干凈到只有一個(gè)spring-boot-starter依賴,此時(shí)我想集成 dubbo,使用 zookeeper 作為注冊(cè)中心,此時(shí)我的依賴配置是這樣:

          <dependencies>
          ??<dependency>
          ????<groupId>org.springframework.bootgroupId>

          ????<artifactId>spring-boot-starterartifactId>
          ??dependency>
          ??<dependency>
          ????<groupId>org.apache.dubbogroupId>
          ????<artifactId>dubbo-spring-boot-starterartifactId>
          ????<version>2.7.9version>
          ??dependency>
          ??<dependency>
          ????<groupId>org.apache.dubbogroupId>
          ????<artifactId>dubbo-registry-zookeeperartifactId>
          ????<version>2.7.9version>
          ??dependency>
          dependencies>

          現(xiàn)在啟動(dòng)這個(gè) spring-boot 項(xiàng)目,會(huì)發(fā)現(xiàn)一堆紅色錯(cuò)誤:

          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.

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

          1. slf4j 報(bào)錯(cuò),提示找到多個(gè) slf4j 的日志綁定

          2. log4j 報(bào)錯(cuò),提示 log4j 沒(méi)有 appender 配置

          出現(xiàn)這個(gè)錯(cuò)誤,就是因?yàn)?dubbo 的傳遞依賴中含有 log4j,但是 spring-boot 的默認(rèn)配置是 slf4j+logback。在依賴了 dubbo 相關(guān)包之后,現(xiàn)在項(xiàng)目中同時(shí)存在logback/jcl(apache commons-logging)/log4j/jul-to-slf4j/slf4j-log4j/log4j-to-slf4j

          來(lái)看一下依賴圖:


          這個(gè)時(shí)候就亂套了,slf4j-log4j是 log4j 的 slf4j 實(shí)現(xiàn),作用是調(diào)用 slf4j api 的時(shí)候使用 log4j 輸出;而log4j-to-slf4j的作用是將log4j的實(shí)現(xiàn)替換為log4j,這樣一來(lái)不是死循環(huán)了

          而且還有 logback 的存在,logback 默認(rèn)實(shí)現(xiàn)了 slf4j 的抽象,而slf4j-log4j也是一樣實(shí)現(xiàn)了 slf4j 的抽象,logback,項(xiàng)目里共存了兩套slf4j的實(shí)現(xiàn),那么在使用slf4j接口打印的時(shí)候會(huì)使用哪個(gè)實(shí)現(xiàn)呢?

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

          如果想正常使用日志,讓這個(gè)項(xiàng)目里所有的框架都正常打印日志,必須將日志框架統(tǒng)一。不過(guò)這里的統(tǒng)一并不是至強(qiáng)行修改,而是用“適配/中轉(zhuǎn)”的方式。

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

          現(xiàn)在項(xiàng)目里存在 log4j(1)的包,而且啟動(dòng)時(shí)又報(bào) log4j 的錯(cuò)誤,說(shuō)明某些代碼調(diào)用了 log4j 的 api。但我們又不想用 log4j,所以需要先解決 log4j 的問(wèn)題。

          由于有 log4j 代碼的引用,所以直接刪除 log4j 一定是不可行的。slf4j 提供了一個(gè)log4j-over-slf4j的包,這個(gè)包復(fù)制了一份 log4j1 的接口類(Logger 等),同時(shí)將實(shí)現(xiàn)類修改為 slf4j 了。

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

          <dependency>
          ??<groupId>org.apache.dubbogroupId>

          ??<artifactId>dubbo-registry-zookeeperartifactId>
          ??<version>2.7.9version>
          ??<scope>compilescope>
          ??
          ??<exclusions>
          ????<exclusion>
          ??????<artifactId>log4jartifactId>
          ??????<groupId>log4jgroupId>
          ????exclusion>
          ??exclusions>
          dependency>

          <dependency>
          ????<groupId>org.slf4jgroupId>
          ????<artifactId>log4j-over-slf4jartifactId>
          ????<version>1.7.30version>
          dependency>

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

          <dependency>
          ??<groupId>org.apache.dubbogroupId>

          ??<artifactId>dubbo-registry-zookeeperartifactId>
          ??<version>2.7.9version>
          ??<scope>compilescope>
          ??<exclusions>
          ????<exclusion>
          ??????<artifactId>log4jartifactId>
          ??????<groupId>log4jgroupId>
          ????exclusion>
          ????<exclusion>
          ??????<artifactId>slf4j-log4j12artifactId>
          ??????<groupId>org.slf4jgroupId>
          ????exclusion>
          ??exclusions>
          dependency>

          修改完成,再次啟動(dòng)就沒(méi)有錯(cuò)誤了,輕松解決問(wèn)題

          日志適配大全

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



          比如想把 slf4j,適配/轉(zhuǎn)換到 log4j2。按照?qǐng)D上的路徑,只需要引用 log4j-slf4j-impl 即可。

          如果想把 jcl,適配/轉(zhuǎn)換到 slf4j,只需要?jiǎng)h除 jcl 包,然后引用 jcl-over-slf4j 即可。

          圖上的箭頭,有些標(biāo)了文字的,是需要額外包進(jìn)行轉(zhuǎn)換的,有些沒(méi)有標(biāo)文字的,是內(nèi)置了適配的實(shí)現(xiàn)。其實(shí)內(nèi)置實(shí)現(xiàn)的這種會(huì)更麻煩,因?yàn)槿绻龅焦泊婊径夹枰ㄟ^(guò)配置環(huán)境變量/配置額外屬性的方式來(lái)指定一款日志實(shí)現(xiàn)。

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

          總結(jié)

          解決日志框架共存/沖突問(wèn)題其實(shí)很簡(jiǎn)單,只要遵循幾個(gè)原則:

          1. 統(tǒng)一使用一套日志實(shí)現(xiàn)

          2. 刪除多余的無(wú)用日志依賴

          3. 如果有引用必須共存的話,那么就移除原始包,使用“over”類型的包(over 類型的包復(fù)制了一份原始接口,重新實(shí)現(xiàn))

          4. 不能 over 的,使用日志抽象提供的指定方式,例如jboss-logging中,可以通過(guò)org.jboss.logging.provider環(huán)境變量指定一個(gè)具體的日志框架實(shí)現(xiàn)

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

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

          推薦


          歡迎加入我的知識(shí)星球,一起劍指大廠,不斷晉升加薪。

          劍指大廠不僅是一個(gè)獲取信息的圈子,還是一個(gè)規(guī)劃職業(yè)的導(dǎo)師。已在知識(shí)星球,更新如下點(diǎn)這里去了解,劍指大廠吧!或點(diǎn)擊下圖了解):


          //////?END?//////
          ↓ 點(diǎn)擊下方關(guān)注,看更多架構(gòu)分享?↓
          瀏覽 56
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  91大鸡巴操 | 正在播放:【性感女侍】疯狂做爱 | 一区二区福利视频 | 自拍偷拍五月婷婷 | 欧美尻屄视频 |