1. 程式人生 > >Java日誌框架總結

Java日誌框架總結

1. 前言

  從寫程式碼開始,就陸陸續續接觸到了許多日誌框架,較常用的屬於LOG4J,LogBack等。每次自己寫專案時,就copy前人的程式碼或網上的demo。配置log4j.properties或者logback.properties就能搞定。這種思想一直持續到最近,前幾天寫了一個小demo,放在liunx上跑的時候竟然報stackOverFlow異常,仔細看異常資訊,log4j-over-slf4j與slf4j-log4j12共存導致stack overflow異常,這一刻終於來了,我不得不去正式日誌框架了。

2. 解決方案

  先貼一下我的解決方案。既然報衝突了,不管三七二十一,先刪除一個jar包就OK!第一種解決方案直接在專案的WEB-INF\lib下刪除slf4j-log4j12.jar包。

cd /WEB-INF/lib
rm -rf slf4j-log4j12-1.7.25.jar

  這種方案只是臨時方案,再次打包部署時還會出現此問題。第二種方案是在pom.xml找到引用此JAR包的地方,我是因為引入了zookeeper,自帶了此JAR包。使用<exclusion>標籤排除此JAR包。或者使用<packagingExcludes>標籤在打包時排除此JAR包。個人推薦使用<packagingExcludes>,因為我的<exclusion>標籤不管用,此處不做詳解,直接貼程式碼!

<dependency>
    <groupId>org.apache.zookeeper</groupId>
    <artifactId>zookeeper</artifactId>
    <version>3.4.6</version>
    <exclusions>
        <exclusion>
            <artifactId>slf4j-log4j12</artifactId>
            <groupId>org.slf4j</groupId>
        </exclusion>
     </exclusions>
</dependency>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<configuration>
    <packagingExcludes>WEB-INF/lib/slf4j-log4j12-1.7.25.jar</packagingExcludes>
</configuration>
</plugin>

3. 基本日誌框架之間關係

  類比java的面向介面程式設計思想,JAVA日誌框架分為介面層,實現層,還多了個橋接層,橋接層聯絡介面層和實現層。

  

  介面層:SELF4J,COMMONS-LOGGING

  實現層:LOG4J,LOGBACK,JDK-LOOGING,LOG4J2

  以上為通用的日誌框架實現(即實現)和門面(即介面)。日誌門面的出現很大程度緩解了日誌系統的混亂,很多庫的作者不在使用具體的日誌框架實現了,而是去使用介面層,即面向介面程式設計。此處,貼一段話,方面更能理解。

  應用程式直接使用這些具體日誌框架的API來滿足日誌輸出需求當然是可以的,但是由於各個日誌框架之間的API通常是不相容的,這樣做就使得應用程式喪失了更換日誌框架的靈活性。比直接使用具體日誌框架API更合理的選擇是使用日誌門面介面。日誌門面介面提供了一套獨立於具體日誌框架實現的API,應用程式通過使用這些獨立的API就能夠實現與具體日誌框架的解耦,這跟JDBC是類似的。最早的日誌門面介面是commons-logging,但目前最受歡迎的是slf4j。日誌門面介面本身通常並沒有實際的日誌輸出能力,它底層還是需要去呼叫具體的日誌框架API的,也就是實際上它需要跟具體的日誌框架結合使用。由於具體日誌框架比較多,而且互相也大都不相容,日誌門面介面要想實現與任意日誌框架結合可能需要對應的橋接器,就好像JDBC與各種不同的資料庫之間的結合需要對應的JDBC驅動一樣。

4. StackOverFlow異常分析

  

  上圖來自SLF4J官網。如圖,上層都是使用SLF4JAPI對外暴露介面。SLF4JAPI使用的是slf4j-api.jar包。接著下層各個日誌框架的實現就不一樣了,最左邊的是slf4j的一個空實現。第二列和第五列是logback和slf4j的一個簡單實現,這2個框架沒有使用所謂的橋接器,直接繼承slf4j,實現slf4j的介面。log4j和jul的實現是要依靠橋接器,如上,slf4j-log412.jar和slf4j-jdk14.jar就是橋接器,分別連線slf4j,log4j和slf4j,jul。下面的log4j.jar和JVM runtime便是具體實現。

  其他日誌系統轉掉回slf4j,如果只存在slf-4j轉到日誌系統實現類,便不會存在StackOverFlow的異常。如果我們使用log4j日誌系統,但又想使用別的日誌系統,此時就要使用從日誌系統到slf4j的橋接類 log4j-over-slf4j,這個庫定義了與log4j一致的介面(包名、類名、方法簽名均一致),但是介面的實現卻是對slf4j日誌介面的包裝,即間接呼叫了slf4j日誌介面,實現了對日誌的轉發。

  既然存在這麼多橋接器,萬一我的系統中存在slf4j -> log4j 和 log4j -> slf4j的橋接器。。。。就會出現互相委託,無限迴圈,堆疊溢位的狀況。所有就會有出現異常。slf4j關於橋接器的詳細介紹參考slf4j官方網站:https://www.slf4j.org/legacy.html

  比如,我現在想使用slf4j的實現類logback日誌框架

  1. 引入slf4j & logback日誌包和slf4j -> logback橋接器;

  2. 排除common-logging、log4j、log4j2日誌包;

  3. 引入jdk-logging -> slf4j、common-logging -> slf4j、log4j -> slf4j、log4j2 -> slf4j橋接器;

  4. 排除slf4j -> jdk-logging、slf4j -> common-logging、slf4j -> log4j、slf4j -> log4j2橋接器。