Java的那些日誌框架們

日誌在排查線上問題、跟蹤線上系統運行狀況中發揮着重要做用。在Java應用的開發中,常見的日誌框架有JCL(commons-logging),slf4jJUL(java.util.logging),log4jlog4j2logback等。這些日誌框架大體能夠分爲兩類,一類是日誌門面(JCL、slf4j),定義日誌的抽象接口;另外一類是日誌實現(JUL,log4j,log4j2,logback),負責真正地處理日誌。爲何會有這麼多的日誌框架呢?從Java日誌框架的發展史裏大概能夠一探究竟。html

Java日誌框架的發展歷史java

  • log4j是Java社區最先的日誌框架,推出後一度成爲Java的事實日誌標準,聽說Apache曾建議Sun把log4j加入到Java標準庫中,可是被Sun拒絕
  • 在Java1.4中,Sun在標準庫中推出了本身的日誌框架java.util.logging,功能相對簡陋
  • 雖然JUL相對簡陋,但仍是有類庫採用了它,這就出現了同一個項目中同時使用log4j和JUL要維護兩套配置的問題,Apache試圖解決這個問題,推出了JCL日誌門面(接口),定義了一套日誌接口,底層實現支持log4j和JUL,可是並無解決多套配置的問題
  • log4j的主力開發Ceki Gülcü因爲某些緣由離開了Apache,建立了slf4j日誌門面(接口),並實現了性能比log4j性能更好的logback(若是Ceki Gülcü沒有離開Apache,這應該就是log4j2的codebase了)
  • Apache不甘示弱,成立了不兼容log4j 1.x的log4j2項目,引入了logback的特性(還酸酸地說解決了logback架構上存在的問題),但目前採用率不是很高

日誌框架選擇

那麼面對這些日誌框架,該如何選擇呢?若是你是在開發一個新的項目(類庫)而不是維護一個上古的遺留代碼,那麼在打印日誌時推薦使用日誌門面,秉承面向接口編程的思想,與具體的日誌實現框架解耦,這樣往後能夠很容易地切換到其餘的日誌實現框架。apache

特別是當你的代碼以SDK的方式提供給別人使用時,使用日誌門面能避免使用方可能出現的日誌框架衝突問題。若是你的SDK裏使用了log4j,而使用方的應用裏使用的logback,這時使用方就不得不分別針對log4j和logback維護兩套日誌配置文件,來確保全部日誌正常的輸出(slf4j提供了衝突解決方案,稍後在下文介紹)。編程

在目前已有的兩個日誌門面框架中,slf4j規避了JCL在部分場景下由於ClassLoader致使綁定日誌實現框架失敗的問題;能支持以上提到的全部日誌實現框架;且slf4j支持佔位符功能,在須要拼接日誌的狀況在接口層面就比JCL有更好的性能,因此推薦使用slf4j,下面簡單多介紹下slf4j。api

// slf4j的佔位符功能
LOGGER.info("hello {}", name);

slf4j如何實現對log4j和JUL的支持

logback由於自己就實現了slf4j-api,因此自然就能很好地支持slf4j,可是log4j和JCL不一樣,早在slf4j以前就已經存在,他們可不是爲了實現slf4j而設計的,那麼如何實現slf4j和他們的綁定呢?答案就是適配器模式。以下圖,slf4j分別爲log4j和JCL實現了適配層slf4-log4j12.jarslf4j-jdk14.jar,經過適配層把日誌的處理轉發給底層日誌實現框架。架構

concrete-bindings

下面是使用log4j 1.x作爲日誌實現框架的maven依賴配置:oracle

<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-api</artifactId>
    <version>1.7.26</version>
</dependency>
<!-- slf4j-log4j12 依賴了log4j,不須要再顯示地依賴log4j -->
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-log4j12</artifactId>
    <version>1.7.26</version>
</dependency>

slf4j解決日誌實現框架衝突

因爲歷史緣由,總會遇到依賴的多個類庫使用不一樣日誌實現框架的狀況,以前也提到了,爲了確保日誌正常輸出,須要針對多個的日誌實現框架維護多個配置文件。爲了解決這個問題,slf4j再次基於適配器模式提供瞭解決方案,針對不一樣的日誌實現框架實現了xxx-over-slf4j適配層,把對日誌實現框架的調用轉發到slf4j-api,再由slf4j把日誌處理轉發給日誌實現框架。框架

日誌依賴衝突

上圖分別展現了把log4j,logback,JUL,JCL的調用分別轉換成其中一種日誌實現框架的示意圖。假設項目依賴的SDK分別使用了log4j、JUL和JCL,打算把日誌實現框架統一成log4j,maven依賴配置以下:maven

<dependency>
    <groupId>me.tunzao</groupId>
    <artifactId>classloader-common</artifactId>
    <version>1.0-SNAPSHOT</version>
    <exclusions>
        <!-- 排除對jcl的依賴 -->
        <exclusion>
            <artifactId>commons-logging</artifactId>
            <groupId>commons-logging</groupId>
        </exclusion>
    </exclusions>
</dependency>

<!-- 把對jcl的請求轉發給slf4j -->
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>jcl-over-slf4j</artifactId>
    <version>1.7.26</version>
</dependency>

<!-- 把對jul的請求轉發給slf4j -->
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>jul-to-slf4j</artifactId>
    <version>1.7.26</version>
</dependency>

<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-api</artifactId>
    <version>1.7.26</version>
</dependency>

<!-- slf4j-log4j12 依賴了log4j,不須要再顯示地依賴log4j -->
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-log4j12</artifactId>
    <version>1.7.26</version>
</dependency>

須要注意的是log4j-over-slf4j.jar 和 slf4j-log4j12.jar 不能同時出如今classpath下,不然就會由於循環調用而堆棧溢出,同理jul-to-slf4j.jar和slf4j-jdk14.jar、jcl-over-slf4j.jar和slf4j-jcl.jar亦不能同時出現。性能

原文

相關文章
相關標籤/搜索