0x00 前言
作為后端開發(fā)修肠,日志可能是我們最常用的功能之一了。平時大家也可能經(jīng)常遇見日志沖突煎殷,常見的overflow報錯,今天為大家詳解一下腿箩,這其中的原理以及問題所在豪直。本文涉及 jar 包有:log4j,log4j-over-slf4j,slf4j-api,slf4j-log4j12 等等。
0x01 背景
不知道大家在平時開發(fā)中珠移,是否經(jīng)常遇見以下幾個問題:
- log4j,logback等等日志包沖突弓乙,然后再慢慢排除,不勝其煩钧惧。
-
使用了 slf4j 暇韧,但是又遇到 StackOverflwError 錯誤。image
-
好不容易解決了StackOverflwError浓瞪,可能又遇到了類似如圖所示的錯誤:image
- ......
0x02 概述
什么是 SLF4J 呢懈玻?簡而言之,他就是一個日志的門面乾颁,市場上的日志系統(tǒng)非常多涂乌, SLF4J 想做的一件事情,就是將這多種的日志系統(tǒng)包裝起來英岭,提供統(tǒng)一的 API 供調用湾盒,從而解決日志的列國爭霸的情況。
0x03 SLF4J 調用流程
在解決以上問題之前诅妹,讓我們先對日志系統(tǒng)做一個大概的理解罚勾。到底什么是 log4j,什么是 slf4j-api,為什么有一個 slf4j-log4j12,又怎么有個log4j-over-slf4j,看上去頭暈眼花吭狡,繞來繞去荧库。先祭上一張官網(wǎng)圖:相信大家都見過這張圖,但是未必全都理解圖上說的是什么意思赵刑。所以呢分衫,我們先不講圖,先看看最上面的一排文字般此,SLF4J bound to xxxx,xxxx總共涉及:
- null
- logback-classic
- log4j
- java.util.logging
- simple
- no-operation
第1個顧名思義蚪战,不綁定,就是沒有日志實現(xiàn)铐懊。第5邀桑,6可以看出來,是 SLF4J 自己的實現(xiàn)科乎,這里就忽略不講壁畸。所以著重講一下2、3、4捏萍。他們有個共同的特點太抓,就是他們都是日志的真正實現(xiàn)庫,他們和SLF4J沒有關系令杈,你可以使用 logback 打印日志走敌,也可以使用 log4j 打日志。
第一層逗噩, application 就是應用層掉丽。
看看圖中第二層,統(tǒng)統(tǒng)都是 SLF4J API异雁。那么這一層是干什么用的呢捶障?這一層就是一個門面層,而和門面層息息相關的 jar 包是什么呢纲刀?對残邀,就是圖中的 slf4j-api.jar 。這個jar包中柑蛇,提供了日志調用的所有接口,應用都是直接調用這個 jar 包中的接口進行日志調用驱闷。
第三層有點不同耻台。第一列,無實現(xiàn)空另,第2盆耽,5,6都是原生的一個實現(xiàn)扼菠,而3摄杂,4都有一個適配層。這里說說 logback循榆。logback 他為什么不需要適配層呢析恢,因為他就是按照了 SLF4J 接口去實現(xiàn)的一個日志庫,相當于親兒子秧饮,自然不需要適配層映挂。所以,logback-classic.jar 和 logback-core.jar 就是 logback 的底層實現(xiàn)盗尸。而3柑船,4就不同了,他們的接口或多或少會有差異泼各,調用方式也各不相同鞍时,所以,需要一個適配層。所以 slf4j-log412.jar 和 slf4j-jdk14.jar 包的作用就是一個適配逆巍,這里是一個橋接及塘,應用通過 slf4j-api 的接口調用過來時,橋接類實際會調用其底層的實現(xiàn)蒸苇,達到一個橋接的過程磷蛹。所以,slf4j-log412.jar,slf4j-jdk14.jar,slf4j-simple.jar,slf4j-nop.jar 是同一類溪烤,就是把-
右邊的實現(xiàn)進行一個橋接味咳。
第四層,可以看到第2檬嘀,3對應的實現(xiàn)槽驶,log4j.jar 和 jvm 就是最后的實現(xiàn)。
至此鸳兽,做一個小總結:
- slf4j-api.jar 是上層的門面掂铐,里面提供接口供調用。
- logback-classic.jar, logback-core.jar, log4j.jar, 是同一類別揍异,屬于底層實現(xiàn)庫全陨。
- slf4j-log412.jar,slf4j-jdk14.jar,slf4j-simple.jar,slf4j-nop.jar,可以看成 slf4j-xxxx.jar,屬于同一個類別衷掷,就是對 - 后面的庫做一個橋接辱姨,更簡單的理解,從左到右讀:把 - 左邊的調用用右邊的庫實現(xiàn)戚嗅。
此時再看看上面那張圖雨涛,是否已經(jīng)全部理解了呢?
0x04 SLF4J 轉換流程
如果看完上面懦胞,不覺得 SLF4J 有什么好處替久,就來看看下面這張圖:那這張圖又是什么意思呢?這就要說到 SLF4J 的一個強大之處了躏尉。設想一下以下一種情況:新建了一個工程蚯根,引入的第一個庫使用的是 java.util.logging 日志庫,引入的第二個庫使用的是 log4j 日志庫胀糜,而你自己的工程稼锅,老板規(guī)定,必須要用 logback 僚纷。你怎么辦呢矩距?這個時候, SLF4J 就出場了怖竭。他能幫你把所有日志歸攏到你所指定的一種日志實現(xiàn)锥债。就是說,他可以把 jul 日志實現(xiàn)轉成 logback,還能把 log4j 實現(xiàn)轉成 logback哮肚。那他是怎么做的呢登夫?回過頭來看圖吧。
著重講解左上角這一部分允趟,其他的類似恼策。
先看 application,這個是應用潮剪,可以看到涣楷,他也遇到了我說到的問題。他的依賴里面有使用 log4j 的抗碰,有用 commons logging 的狮斗,有用 java.util.logging 的。所以此時需要做一個替換弧蝇,分別是通過 jcl-over-slf4j.jar 替換掉 commons-logging.jar,log4j-over-slf4j.jar 替換掉 log4j.jar, jul-to-slf4j.jar 包中安裝 SLF4JBrindgeHandler 解決碳褒。替換掉之后,就把所有日志調用轉接到 slf4j-api 上了看疗,然后 api 接口再調用底層實現(xiàn)沙峻,圖上是 logback。文中說的替換是什么意思呢两芳?就是把原日志實現(xiàn)庫排除掉摔寨,引入 xxx-over-slf4j.jar 。
那么盗扇,xxx-over-slf4j.jar 是什么原理呢?先給大家看這張圖:
左邊是 log4j.jar 的包結構沉填,右邊是 log4j-over-slf4j.jar 的包結構疗隶。發(fā)現(xiàn)貓膩了嗎?他們的目錄結構一模一樣翼闹!所以用 log4j-over-slf4j 可以替換掉 log4j斑鼻!且編譯不會出錯。log4j-over-slf4j.jar 實現(xiàn)了基本上所有 log4j 會被調用的 api 接口猎荠。所以替換之后坚弱,不會報錯,編譯也能通過关摇,而底層實現(xiàn)卻全轉到 slf4j 這里去了荒叶。這里就是一個貍貓換太子的把戲。
再看其他兩個圖输虱,底層分別是 log4j, jvm 實現(xiàn)些楣。都是講其他庫 over 一下到 slf4j 。
特別注意一下, logback 不需要轉愁茁,為什么蚕钦?因為他是親兒子。天生就帶這些鹅很。
所以再小總結一下:
- jcl-over-slf4j.jar, log4j-over-slf4j.jar, jul-to-slf4j.jar嘶居,這種形式類似 xxx-over-slf4j.jar 的,就是將
-
前的太子用 slf4j 的貍貓代替促煮。而 xxx-to-slf4j.jar 比較特殊邮屁,這是因為 xxx 這個包無法被替換掉,比如 java.util.logging,系統(tǒng)的庫污茵,無法替換樱报,所以只能采用別的手段。此類別的實現(xiàn)讀者有興趣可以去看看泞当,本文不再分析迹蛤。
0x05 常見問題解決
StackOverflow 錯誤
為什么會出現(xiàn)這個問題呢?控制臺輸出上一般會比較清楚襟士,就是你既使用了橋接庫盗飒,又使用了over庫(貍貓)。比如:試想一下:你先用 over 庫把 log4j 轉成了 slfj4 調用陋桂。緊接著逆趣,你又把 slf4j 適配到 log4j 上。這就構成了一個死循環(huán)嗜历,肯定是會出現(xiàn)堆棧溢出的問題宣渗。所以,一個工程里面梨州,只能保留一個日志實現(xiàn)庫痕囱,還有配套的橋接庫,加上其他日志的 over 庫暴匠,才是正確之道鞍恢。
異常參數(shù)錯誤
上面提到的這個錯誤:
大致可以看出來吧,他缺少一個真正的實現(xiàn)每窖“锏簦看你選擇使用什么,就把什么庫補充上去窒典。加上 logback蟆炊,去掉 slf4j-log4j 或者加上 log4j 都可以解決這個問題。
其他問題都比較類似瀑志,如果看懂了上文的介紹盅称,應該可以著手解決此類問題了肩祥。
0x06 不算總結的總結
了解了日志的原理,以后媽媽再也不擔心日志沖突了缩膝!