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橋接器。

 

  


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM