面試題:應用中很多jar包,比如spring、mybatis、redis等等,各自用的日誌系統各異,怎麼用slf4j統一輸出?
一、問題概述
如題所說,後端應用(非spring boot專案)通常用到了很多jar包,比如spring系列、mybatis、hibernate、各類連線資料庫的客戶端的jar包。可能這個jar包用的是logback、那個用的是log4j、那個又是log4j2,
這時候,怎麼才能保證各jar包的日誌都能輸出,且能以統一的格式輸出呢?
為什麼要強調非spring boot專案,可參考第四節。
二、幾種日誌框架的簡單介紹
來源:https://juejin.im/post/5a7c5d575188254e76179c0f
Java中的日誌框架分如下幾種:
1.Log4j
Apache Log4j是一個基於Java的日誌記錄工具。它是由Ceki Gülcü首創的,現在則是Apache軟體基金會的一個專案。
2.Log4j 2
Apache Log4j 2是apache開發的一款Log4j的升級產品。
3.Commons Logging
Apache基金會所屬的專案,是一套Java日誌介面,之前叫Jakarta Commons Logging,後更名為Commons Logging。
4.Slf4j
Slf4j
類似於Commons Logging,是一套簡易Java日誌門面,本身並無日誌的實現。(Simple Logging Facade for Java,縮寫Slf4j)。
5.Logback
一套日誌元件的實現(slf4j陣營)。
6.Jul
(Java Util Logging)
自Java1.4以來的官方日誌實現。
上面這幾個框架,又分為了兩類:一類是介面,包括了slf4j、commons logging;剩下的幾個是具體實現,包括了jul、logback、log4j、log4j2。
現在,講究針對介面程式設計,而不是針對具體實現,方便移植。而現在最通用的就是slf4j。就我待過的幾家公司來說,全都都用的是slf4j作為日誌門面,具體的實現一般直接採用預設的logback。
下面就具體講講,怎麼講其他框架五花八門的日誌一統為slf4j。
三、統一的思路
我們先分析下。
假設有個jar採用了JUL,即java自帶的日誌系統。我們要怎麼才能將其適配到slf4j去呢?
在JUL的logger的方法被呼叫時,因為Logger所在的包位於java.util,預設就被載入了,不太方便以假亂真。
想要覆蓋其實現,我能想到的是,通過aop切面,直接將引數傳給slf4j的api,不呼叫原來的日誌實現。
再來,假設有個jar採用了log4j,即寫日誌用的是org.apache.log4j.Logger,該類是來源於log4j的相關jar包,那麼,
我可以排除掉log4j相關的jar包,程式碼裡報錯了,對吧?那我再建一個相同package名、相同類名的class。在這個假的
class裡,我去呼叫slf4j的api。
那麼,這些功能需要我自己去寫嗎?不需要!slf4j已經給我們提供了從各類日誌框架九九歸一到slf4j的轉換包。
源日誌框架 | 目標日誌框架 | 所需轉換用jar包 | maven(這裡缺了version欄位,請參考下一節) | 備註 |
jul | slf4j | jul-to-slf4j |
<dependency> |
需要排除掉原有依賴。 |
Commons Logging | slf4j | jcl-over-slf4j |
<dependency> |
需要排除掉原有依賴。 |
log4j | slf4j | log4j-over-slf4j |
<dependency> |
需要排除掉原有依賴。 |
log4j2 | slf4j | log4j-to-slf4j |
<dependency> |
需要排除掉原有依賴。 注意這裡使用的為 log4j-to-slf4j |
這裡給一張slf4j官方手冊裡的,也是漏了log4j2的。
四、如何選擇以上轉換jar包的版本
在spring boot中,其實根本不需要以上的麻煩工作,只需要引用以下元件:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
<version>1.5.9.RELEASE</version>
</dependency>
我們看看這個元件裡,包含了什麼:
可以看到,log4j、jul、jcl(即commons-logging)的轉換包都有了。
那麼,在非spring boot專案中,我們只要去mvnrepository.com查詢:spring-boot-starter-logging.
然後,選擇對應的版本,就可以在下圖看到該元件依賴的包:
怎麼樣,應該是個比較簡單的辦法吧?
五、未完成的統一之路
這一篇裡面,我們還只講瞭如何將各日誌框架統一到slf4j來。
但是slf4j只是個門面,具體的實現呢(比如log4j等)又被我們排除掉了。
那麼slf4j沒有實現,也是不能完成輸出日誌的任務的。
所以我們還需要一個實現。這份留到下篇仔細講。
六、github程式碼(已實現上述的統一問題,預設實現採用了logback)
程式碼路徑: