日誌SLF4J解惑

0x00 前言

做爲後端開發,日誌多是咱們最經常使用的功能之一了。平時你們也可能常常碰見日誌衝突,常見的overflow報錯,今天爲你們詳解一下,這其中的原理以及問題所在。本文涉及 jar 包有:log4j,log4j-over-slf4j,slf4j-api,slf4j-log4j12 等等。java

0x01 背景

不知道你們在平時開發中,是否常常碰見如下幾個問題:後端

  • log4j,logback等等日誌包衝突,而後再慢慢排除,不勝其煩。
  • 使用了 slf4j ,可是又遇到 StackOverflwError 錯誤。
  • 好不容易解決了StackOverflwError,可能又遇到了相似如圖所示的錯誤:
  • ......

0x02 概述

什麼是 SLF4J 呢?簡而言之,他就是一個日誌的門面,市場上的日誌系統很是多, SLF4J 想作的一件事情,就是將這多種的日誌系統包裝起來,提供統一的 API 供調用,從而解決日誌的列國爭霸的狀況。api

0x03 SLF4J 調用流程

在解決以上問題以前,讓咱們先對日誌系統作一個大概的理解。到底什麼是 log4j,什麼是 slf4j-api,爲何有一個 slf4j-log4j12,又怎麼有個log4j-over-slf4j,看上去頭暈眼花,繞來繞去。先祭上一張官網圖:app

相信你們都見過這張圖,可是未必全都理解圖上說的是什麼意思。因此呢,咱們先不講圖,先看看最上面的一排文字,SLF4J bound to xxxx,xxxx總共涉及:

  1. null
  2. logback-classic
  3. log4j
  4. java.util.logging
  5. simple
  6. no-operation

第1個顧名思義,不綁定,就是沒有日誌實現。第5,6能夠看出來,是 SLF4J 本身的實現,這裏就忽略不講。因此着重講一下二、三、4。他們有個共同的特色,就是他們都是日誌的真正實現庫,他們和SLF4J沒有關係,你可使用 logback 打印日誌,也可使用 log4j 打日誌。jvm

第一層, application 就是應用層。日誌

看看圖中第二層,通通都是 SLF4J API。那麼這一層是幹什麼用的呢?這一層就是一個門面層,而和門面層息息相關的 jar 包是什麼呢?對,就是圖中的 slf4j-api.jar 。這個jar包中,提供了日誌調用的全部接口,應用都是直接調用這個 jar 包中的接口進行日誌調用。code

第三層有點不一樣。第一列,無實現,第2,5,6都是原生的一個實現,而3,4都有一個適配層。這裏說說 logback。logback 他爲何不須要適配層呢,由於他就是按照了 SLF4J 接口去實現的一個日誌庫,至關於親兒子,天然不須要適配層。因此,logback-classic.jar 和 logback-core.jar 就是 logback 的底層實現。而3,4就不一樣了,他們的接口或多或少會有差別,調用方式也各不相同,因此,須要一個適配層。因此 slf4j-log412.jar 和 slf4j-jdk14.jar 包的做用就是一個適配,這裏是一個橋接,應用經過 slf4j-api 的接口調用過來時,橋接類實際會調用其底層的實現,達到一個橋接的過程。因此,slf4j-log412.jar,slf4j-jdk14.jar,slf4j-simple.jar,slf4j-nop.jar 是同一類,就是把-右邊的實現進行一個橋接。cdn

第四層,能夠看到第2,3對應的實現,log4j.jar 和 jvm 就是最後的實現。blog

至此,作一個小總結:接口

  1. slf4j-api.jar 是上層的門面,裏面提供接口供調用。
  2. logback-classic.jar, logback-core.jar, log4j.jar, 是同一類別,屬於底層實現庫。
  3. slf4j-log412.jar,slf4j-jdk14.jar,slf4j-simple.jar,slf4j-nop.jar,能夠當作 slf4j-xxxx.jar,屬於同一個類別,就是對 - 後面的庫作一個橋接,更簡單的理解,從左到右讀:把 - 左邊的調用用右邊的庫實現。

此時再看看上面那張圖,是否已經所有理解了呢?

0x04 SLF4J 轉換流程

若是看完上面,不以爲 SLF4J 有什麼好處,就來看看下面這張圖:

那這張圖又是什麼意思呢?這就要說到 SLF4J 的一個強大之處了。設想一下如下一種狀況:新建了一個工程,引入的第一個庫使用的是 java.util.logging 日誌庫,引入的第二個庫使用的是 log4j 日誌庫,而你本身的工程,老闆規定,必需要用 logback 。你怎麼辦呢?這個時候, SLF4J 就出場了。他能幫你把全部日誌歸攏到你所指定的一種日誌實現。就是說,他能夠把 jul 日誌實現轉成 logback,還能把 log4j 實現轉成 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 接口再調用底層實現,圖上是 logback。文中說的替換是什麼意思呢?就是把原日誌實現庫排除掉,引入 xxx-over-slf4j.jar 。

那麼,xxx-over-slf4j.jar 是什麼原理呢?先給你們看這張圖:

左邊是 log4j.jar 的包結構,右邊是 log4j-over-slf4j.jar 的包結構。發現貓膩了嗎?他們的目錄結構如出一轍!因此用 log4j-over-slf4j 能夠替換掉 log4j!且編譯不會出錯。log4j-over-slf4j.jar 實現了基本上全部 log4j 會被調用的 api 接口。因此替換以後,不會報錯,編譯也能經過,而底層實現卻全轉到 slf4j 這裏去了。這裏就是一個狸貓換太子的把戲。

再看其餘兩個圖,底層分別是 log4j, jvm 實現。都是講其餘庫 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,系統的庫,沒法替換,因此只能採用別的手段。此類別的實現讀者有興趣能夠去看看,本文再也不分析。

0x05 常見問題解決

StackOverflow 錯誤

爲何會出現這個問題呢?控制檯輸出上通常會比較清楚,就是你既使用了橋接庫,又使用了over庫(狸貓)。好比:

試想一下:你先用 over 庫把 log4j 轉成了 slfj4 調用。緊接着,你又把 slf4j 適配到 log4j 上。這就構成了一個死循環,確定是會出現堆棧溢出的問題。因此,一個工程裏面,只能保留一個日誌實現庫,還有配套的橋接庫,加上其餘日誌的 over 庫,纔是正確之道。

異常參數錯誤

上面提到的這個錯誤:

大體能夠看出來吧,他缺乏一個真正的實現。看你選擇使用什麼,就把什麼庫補充上去。加上 logback,去掉 slf4j-log4j 或者加上 log4j 均可以解決這個問題。

其餘問題都比較相似,若是看懂了上文的介紹,應該能夠着手解決此類問題了。

0x06 不算總結的總結

瞭解了日誌的原理,之後媽媽不再擔憂日誌衝突了!

相關文章
相關標籤/搜索