1 基本日志架構之間關系

接口層:SELF4J,COMMONS-LOGGING
實作層:LOG4J,LOGBACK,JDK-LOOGING,LOG4J2
以上為通用的日志架構實作(即實作)和門面(即接口)。日志門面的出現很大程度緩解了日志系統的混亂,很多庫的作者不在使用具體的日志架構實作了,而是去使用接口層,即面向接口程式設計。此處,貼一段話,方面更能了解。
應用程式直接使用這些具體日志架構的API來滿足日志輸出需求當然是可以的,但是由于各個日志架構之間的API通常是不相容的,這樣做就使得應用程式喪失了更換日志架構的靈活性。比直接使用具體日志架構API更合理的選擇是使用日志門面接口。日志門面接口提供了一套獨立于具體日志架構實作的API,應用程式通過使用這些獨立的API就能夠實作與具體日志架構的解耦,這跟JDBC是類似的。最早的日志門面接口是commons-logging,但目前最受歡迎的是slf4j。日志門面接口本身通常并沒有實際的日志輸出能力,它底層還是需要去調用具體的日志架構API的,也就是實際上它需要跟具體的日志架構結合使用。由于具體日志架構比較多,而且互相也大都不相容,日志門面接口要想實作與任意日志架構結合可能需要對應的橋接器,就好像JDBC與各種不同的資料庫之間的結合需要對應的JDBC驅動一樣。
2 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橋接器。