一、前言
在進行 Java 開發(fā)時,通常我們會選擇 Slf4j 作為日志門面坏瞄,但日志實現卻不盡相同。如果系統(tǒng)運行中同時存在多個日志實現甩卓,就會出現類似下圖的 Warning鸠匀。
二、問題原因
我們知道 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);
我們就可以通過這個作為入口,去看看源碼的實現痢法。如下圖所示狱窘,我標注了需要關注的核心代碼。
- (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,并最終返回纺且。
- (6)reportActualBinding() 方法會校驗 SET 的 size盏道,如果大于 1,就會打印出一開始我們看見的 Warning 了隆檀。
三摇天、問題解決
解決思路就是將你不想要的日志實現從依賴包中排除掉即可,通過 IDEA 提供的 Diagrams 能夠非常方便的查看項目中的依賴關系恐仑。
打開項目的 POM 文件泉坐,右鍵選擇 Diagrams -> Show Dependencies點擊窗口左上角的下圖中的這個圖標歧斟,可以只看當前選中的這個依賴的關系纯丸。
選中后效果如下:
如上圖所示,logback 由 spring-boot-starter-logging 引入静袖,最頂層是由 spring-boot-starter-web 和 spring-boot-starter-test 引入觉鼻。
我們嘗試在 spring-boot-starter-web 中排除該依賴,應該就可以了队橙。如果排出后重新搜索仍然存在 logback 依賴坠陈,則重復執(zhí)行排除的操作。
<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>`