深入理解Logger日志——Slf4j框架綁定原理


深入理解Logger日志——Slf4j框架綁定原理

   說到Logger日志的動態綁定,主要歸功與Slf4j,在之前的文章也說過,Slf4j是類似於Apache Common-Logging,英文為Simple Logging Facade,是一個簡單的日志門面適配器,所有的日志代碼都可以用slf4j方式,它會根據項目具體依賴的日志實現包進行日志操作,只需修改pom.xml文件中的日志實現依賴,對於Log4j、Log4j2和Logback等都有相應的橋接包,對相應的slf4j-api接口實現。

  那么slf4j是如何自動綁定上的呢。

  多個框架是如何選擇的呢。


Slf4j框架架構原理

  日志的綁定原理主要是依賴Slf4j的靜態加載,從相應的實現框架中獲取Logger。需要自己動手一步步跟蹤下去才會感觸良多。

  Slf4j的實現流程

  Logger日志打印調用流程:

  在slf4j中主要是實現 LoggerFactoryBinder 的 StaticLoggerBinder 采用單例模式,編譯時期靜態加載方式,得到不同的ILoggerFactory工廠的實現類,最終拿到相應框架所匹配的Logger。

  對於Slf4j的源碼分析我這邊就不再過多的贅余,關鍵得自己去理解的看看,推薦一個:Java日志體系(slf4j)

  介紹一下slf4j中主要的幾個類的作用:

  • Logger:slf4j日志接口類,提供了trace < debug < info < warn < error這5個級別對應的方法,主要提供了占位符{}的日志打印方式;
  • Log4jLoggerAdapter:Logger適配器,主要對org.apache.log4j.Logger對象的封裝,占位符{}日志打印的方式在此類中實現;
  • LoggerFactory:日志工廠類,獲取實際的日志工廠類,獲取相應的日志實現對象;
  • lLoggerFactory:底層日志框架中日志工廠的中介,再其實現類中,通過底層日志框架中的日志工廠獲取對應的日志對象;
  • StaticLoggerBinder:靜態日志對象綁定,在編譯期確定底層日志框架,獲取實際的日志工廠,也就是lLoggerFactory的實現類;

  項目中有多種日志框架時會存在多個StaticLoggerBinder時候獲取第一個,主要是采用類的順序加載機制,Pom文件中依賴的填寫順序有關(個人實踐得到)

  這里需要講一下Maven中對於jar包的處理方式

  Maven的Jar包默認處理策略

  • 最短路徑優先

  Maven 對於兩個依賴有沖突時,誰的路徑深度短誰優先:如對 D1 和 D2 時,會默認選擇最短路徑的那個 jar 包,即 D2。E->F->D2 比 A->B->C->D1 路徑較短 。

  • 最先聲明優先

  對與相同路徑長度的,選擇最先聲明的依賴,如 A->B->C1, E->F->C2 ,兩個依賴路徑長度相同,選擇最先聲明。


 框架橋接過程

  首先先看Slf4j-api包結構:主要定義了一些接口做相應的適配,其包結構主要是 org.slf4j  其他實現框架都是根據其進行相應的橋接

   Log4j的橋接包形式:相同包名,StaticLoggerBinder采用構造器形式獲取 Log4jLoggerFactory 工廠

  Logback的橋接包形式:相同包名,在StaticLoggerBinder調用時就已經靜態初始化LoggerContext 工廠
   Logback的詳細配置:https://cloud.tencent.com/developer/article/1445599

   Log4j2的橋接包形式:相同包名,與Log4j類似采用構造器形式獲取 Log4jLoggerFactory 工廠
  Log4j2的詳細配置及原理介紹:

 

  在官方文檔中slf4j與其他日志框架如何綁定的示意圖(大家都放我也放一下~)

 

  以下是個人收集一些比較簡單好理解的綁定關系圖,體現了StaticLoggerBinder綁定的重要性

 

 

 

 

  


 綁定過程中所遇到的問題

  1、Log4j和Log4j2需要配置好log4j.xml和log4j2.xml文件,不然無法正常使用,Logback可以正常使用

  2、與SpringBoot的spring-boot-starter中的依賴spring-boot-starter-logging內部的依賴Logback產生沖突(可以自己點擊查看一番)

Caused by: java.lang.IllegalArgumentException: LoggerFactory is not a Logback LoggerContext but Logback is on the classpath. Either remove Logback or the competing implementation (class org.slf4j.impl.Log4jLoggerFactory loaded from file:/Users/zhouguanglin/.m2/repository/org/slf4j/slf4j-log4j12/1.7.29/slf4j-log4j12-1.7.29.jar). If you are using WebLogic you will need to add 'org.slf4j' to prefer-application-packages in WEB-INF/weblogic.xml: org.slf4j.impl.Log4jLoggerFactory
    at org.springframework.util.Assert.instanceCheckFailed(Assert.java:699)
    at org.springframework.util.Assert.isInstanceOf(Assert.java:599)
    at org.springframework.boot.logging.logback.LogbackLoggingSystem.getLoggerContext(LogbackLoggingSystem.java:284)
    at org.springframework.boot.logging.logback.LogbackLoggingSystem.beforeInitialize(LogbackLoggingSystem.java:104)
    at org.springframework.boot.context.logging.LoggingApplicationListener.onApplicationStartingEvent(LoggingApplicationListener.java:232)
    at org.springframework.boot.context.logging.LoggingApplicationListener.onApplicationEvent(LoggingApplicationListener.java:213)

    主要原因是:SpringBoot所使用的搭配日志框架是Logback

    若是項目使用SpringBoot但搭配的是其他日志框架A,由StaticLoggerBinder.getSingleton().getLoggerFactory()獲取的是搭配框架A的實現,導致與SpringBoot內部自身搭配的Logback產生沖突,可以進入報錯地方查看。

 

   從代碼邏輯中可以發現,Assert.isInstanceOf 判斷獲取得到的ILoggerFactory的實現類必須是LoggerContext 才OK,硬性規定不然就會報錯。

  解決方案:只要在SpringBoot中排除Logback的依賴就OK,如下:

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
            <exclusions>
                <exclusion>
                    <artifactId>logback-classic</artifactId>
                    <groupId>ch.qos.logback</groupId>
                </exclusion>
            </exclusions>
        </dependency>

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM