天天看點

Slf4j 包老沖突, 到底怎麼解決?

一、前言

在進行 Java 開發時,通常我們會選擇 Slf4j 作為日志門面,但日志實作卻不盡相同。如果系統運作中同時存在多個日志實作,就會出現類似下圖的 Warning。

Slf4j 包老沖突, 到底怎麼解決?

二、問題原因

我們知道 SpringBoot 預設使用的日志實作是 Logback,是以我們嘗試在項目中引入 Log4j 的依賴時,就複現了上圖的報錯。

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-log4j2</artifactId>
</dependency>      

上圖報錯告知我們存在多個 SLF4J bingdings,分别位于 logback 和 log4j 包中,有兩個 StaticLoggerBinder。

我們知道使用 Slf4j ,需要 LoggerFactory.getLogger() 方法擷取執行個體。

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;      

private final Logger logs = LoggerFactory.getLogger(xxx.class); 我們就可以通過這個作為入口,去看看源碼的實作。如下圖所示,我标注了需要關注的核心代碼。

Slf4j 包老沖突, 到底怎麼解決?

(1)調用 getILoggerFactory() 方法得到 LoggerFactory。

(2)對于首次調用,INITIALIZATION_STATE 應該是 UNINITIALIZED,是以進入初始化的邏輯,調用方法 performInitialization()。

(3)調用 bind() 方法。

(4)如果不是 isAndroid(),調用

findPossibleStaticLoggerBinderPathSet() 方法,故名思意,查找可能的 staticLoggerBinder,注意這裡傳回的類型是 SET,即可能是多個。

(5)在findPossibleStaticLoggerBinderPathSet() 這個方法内,首先通過 classLoader 加載了 org/slf4j/impl/StaticLoggerBinder.class 這個類的 path,它可能存在多個,是以使用了 while 擷取了所有的 path,并最終傳回。

Slf4j 包老沖突, 到底怎麼解決?

(6)reportActualBinding() 方法會校驗 SET 的 size,如果大于 1,就會列印出一開始我們看見的 Warning 了。

Slf4j 包老沖突, 到底怎麼解決?

三、問題解決

解決思路就是将你不想要的日志實作從依賴包中排除掉即可,通過 IDEA 提供的 Diagrams 能夠非常友善的檢視項目中的依賴關系。

打開項目的 POM 檔案,右鍵選擇 Diagrams -> Show Dependencies

Slf4j 包老沖突, 到底怎麼解決?

假設我們想要排除 logback 依賴,使用 log4j。Ctrl + F 搜尋 logback,可以找到引用該依賴的樹形結構。

Slf4j 包老沖突, 到底怎麼解決?

點選視窗左上角的下圖中的這個圖示,可以隻看目前選中的這個依賴的關系。

Slf4j 包老沖突, 到底怎麼解決?

選中後效果如下:

Slf4j 包老沖突, 到底怎麼解決?

如上圖所示,logback 由 spring-boot-starter-logging 引入,最頂層是由 spring-boot-starter-web 和 spring-boot-starter-test 引入。

我們嘗試在 spring-boot-starter-web 中排除該依賴,應該就可以了。如果排出後重新搜尋仍然存在 logback 依賴,則重複執行排除的操作。

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
    <exclusions>
        <exclusion>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-logging</artifactId>
        </exclusion>
    </exclusions>
</dependency>      

四、總結

日志架構沖突特别對于新手來說處理起來比較頭疼,因為涉及到了日志接口和日志實作。

我們推崇的應該是面向接口程式設計,是以我們大到開源項目,小到公司的公共 jar 包,應當合理利用 Maven 的傳遞機制。具體的日志實作不應該傳遞出去,避免影響到調用的下遊方。

<optional>true</optional>