天天看點

10分鐘搞定--混亂的 Java 日志體系

常用的日志元件
10分鐘搞定--混亂的 Java 日志體系

提問

:如果自己開發一個開源架構(如Spring),你将采用上述哪個日志元件?

發現哪個都不能采用,隻能基于應用程式實際使用的日志元件來,不然就會日志列印多份。那具體怎麼找到應用程式實際使用的日志元件呢?JCL(Apache Commons Logging)解決了這個問題。

在 sun 開發 logger 前,apache 項目已經開發了功能強大的 log4j 日志工具,并向 sun 推薦将其納入到 jdk 的一部分,可是 sun 拒絕了 apache 的提議,sun 後來自己開發了一套記錄日志的工具,即JUL。可是現在的開源項目都使用的是 log4j,log4j 已經成了事實上的标準,但由于又有一部分開發者在使用 sun logger,是以 apache 才推出 Apache Commons Logging,使得我們不必關注我們正在使用何種日志工具。

Apache Commons Logging(JCL)

之前叫Jakarta Commons Logging,簡稱JCL,是Apache提供的一個通用日志API,可以讓應用程式不再依賴于具體的日志實作工具。Apache commons-logging是JCL的标準實作。

commons-logging包中對其它一些日志工具,包括Log4J、Avalon LogKit、JUL等,進行了簡單的包裝,可以讓應用程式在運作時,直接将JCL API打點的日志适配到對應的日志實作工具中。

JCL通過動态查找的機制,在程式運作時自動找出實際使用的日志庫。

JCL動态查找(綁定)日志元件原理

10分鐘搞定--混亂的 Java 日志體系

JCL為每一種日志實作采用了一個擴充卡,具體采用哪個、是動态的根據指定順序查找classPath是否存在相應日志的實作。如果JCL運作時沒找到任何一種第三方的日志實作,則就用jdk14自帶的java.util.logging(JUL)。假如你的maven工程pom.xml裡加入了log4j的依賴,運作時JCL找到即可動态綁定。

Spring 的日志就是采用JCL,解決了應用程式和架構日志不統一的問題。動态去尋找(應用程式配置的)日志體系的實作。

但JCL方式也有不足,不能把所有日志的實作都包含。具體可以看commons-logging架構的LoggerFactory和LoggerFactoryImpl,裡面寫死了數組,不包含log4j2和logback的最新實作。

org.apache.commons.logging.impl.LogFactoryImpl見下圖:

10分鐘搞定--混亂的 Java 日志體系

通過看JCL的源碼,jcl為每一種日志實作采用了一個擴充卡,具體采用哪個、是動态的根 據指定順序查找classPath是否存在相應的實作,查找順序就是數組元素的順序。如果一個應用當中有多個classLoader。比如OSGI規定了每個子產品都有其獨立的ClassLoader。這種機制保證了插件互相獨立,同時也限制了JCL在OSGi中的正常使用。這時出現了slf4j基于靜态綁定的方式來解決了這個問題。

SLF4J

SLF4J 全稱 Simple Logging Facade for Java(簡單日志門面)。與JCL類似,本身不替供日志具體實作,隻對外提供接口或門面。是以它不是具體的日志解決方案,而是通過Facade Pattern門面模式對外提供一些Java Logging API。這些對外提供的核心API其實就是一些接口以及一個LoggerFactory的工廠類。

在使用SLF4J的時候,不需要在代碼中或配置檔案中指定你打算使用哪個具體的日志系統,可以在部署的時候不修改任何配置即可接入一種日志實作方案,在編譯時靜态綁定想用的Log庫。

按照官方的說法,SLF4J是一個用于日志系統的簡單Facade,允許最終使用者在部署其應用時使用其所希望的日志系統。作者建立SLF4J的目的是為了替代Apache Commons Logging。即使以後又出現更新的其他日志元件,也能完全适應。

SLF4J 靜态綁定日志元件原理

10分鐘搞定--混亂的 Java 日志體系

使用SLF4J時,如果你需要使用某一種日志實作,那麼你選擇相對應的SLF4J的橋接包即可。比如使用log4j日志元件,就選slf4j-log4j12橋接包,業務中就可以使用log4j進行底層日志輸出。

SLF4J提供的橋接包:

• slfj-log4j12.jar (表示橋接 log4j)

• slf4j-jdk14.jar(表示橋接jdk Looging)

• sIf4j-jcl.jar(表示橋接 jcl)

• log4j-slf4j-impl(表示橋接log4j2)

• logback-classic(表示橋接 logback)

SLF4J提供了統一的記錄日志的接口(LoggerFactory),隻要按照其提供的方法記錄即可,最終日志的格式、記錄級别、輸出方式等通過具體日志系統的配置來實作,是以可以在應用中靈活切換日志系統。

logback是slf4j-api的天然實作,不需要橋接包就可以使用。

與commons loging(JCL)不同的是其采用在classPath加入橋接jar包來表示具體采用哪種實作(靜态綁定)

示範

:slf4j方式,使用log4j日志元件

隻需要在pom.xml中加入:

<!-- 加入slf4j的核心API -->
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>1.7.25</version>
        </dependency>

        <!-- slf4j靜态綁定log4j12的橋接包 -->
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-log4j12</artifactId>
            <version>1.7.2</version>
        </dependency>

        <!-- 實際的日志實作 log4j的依賴-->
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.16</version>
        </dependency>
           

好了,到這裡是不是覺得,Java日志體系目前有JCL和SLF4J的方式,是不是就萬事大吉了?(其實并不是)

JCL方式的commons-logging 是動态查找綁定。

SLF4J是靜态綁定,需要加橋接包。如slf4j-log4j2。

下面給出一個場景

需求

:我的應用程式想使用log4j2列印日志;而Spring采用的JCL中不包含log4j2(LoggerFactoryImpl源碼中定義的數組中不包含log4j2),運作時,JCL從數組順序尋找日志的實作,如果沒有引入其他實作,最終則會用JUL列印日志,如下圖。

10分鐘搞定--混亂的 Java 日志體系

這時會出現什麼問題呢?Spring 列印日志方式和應用程式列印日志不統一,錯誤排除時比較困難。而且應用程式和Spring架構,日志不統一,太亂了,還得搞2份日志配置檔案。

為了讓Spring和我們的應用程式,采用統一的log4j2 日志體系,需要加入擴充卡。

改善上面應用程式和架構日志不統一的問題(加入擴充卡後):

10分鐘搞定--混亂的 Java 日志體系

中間這個擴充卡:其實就是jcl-over-slfj.jar(jcl适配slf4j),加入下面的依賴即可,再去除commons-logging。

10分鐘搞定--混亂的 Java 日志體系

最後SLF4J的總結:無論你選用哪個日志元件,在你的應用中,都應當使用slf4j作為統一的API,好處是可以很友善的切換底層實作。

Java常用日志體系

10分鐘搞定--混亂的 Java 日志體系

實戰:

SpringMVC配置log4j