2017年12月19日 星期二

SLF4J 與其他 logging framework 實作綁定與橋接的關聯

使用 SLF4J 的時候,同時使用很多開源函式庫,會很容易導致各種 log 跑不出來或者 dependency 衝突的問題
本質上是因為不同開源函式庫,各自使用了不同的 logging framework
因而導致了 log 被輸出到沒有被設定的實作,或者相同的實作被重複綁定等等。

綁定一個 LOG 框架

綁定的意思是,要讓寫到 SLF4J 的 log,被輸出到某個 logging framework 去,例如輸出到 logback 等等。
這裡可以參考官方文件 [1] 中,關於綁定的架構圖如下:

以 logback 來說,就是如果要讓 SLF4J 輸出到 logback,需要引用 slf4j-api、logback-classic 和 logback-core 這三個 JAR
而實際上,因為 logback 就是預設的 SLF4J 的實作,因此也不需要什麼特別的 dependency。
如果是其他 logging framework,就有可能需要額外的東西了,像是綁定 log4j 就需要額外加入 slf4j-log412 這個 JAR。

橋接一個 LOG 框架

這裡講的橋接的意思,是說已經有既有的程式碼將 log 寫到某個 logging framework
但是其他程式碼要整合既有程式碼時,希望把它寫的 log 重新整合回 SLF4J,一起輸出到另外一個 logging framework。

這裡一樣也是先參考官方文件 [2] 的橋接架構圖:

以 log4j 來說,如果要將既有的 log4j 的 log 橋接到 SLF4J 上,需要引用 log4j-over-slf4j 這個 dependency。

不過另外需要注意的是,這上面的圖其實沒有談到 Log4j2。
Log4j(org.apache.log4j)和 Log4j2(org.apache.logging.log4j)要橋接到 SLF4J 時,必須個別處理。
可以參考下面的範例。

範例

以我自己常用的狀況來說,我習慣用 logback 作為實際輸出 Log 的框架,而程式碼使用 SLF4J 來寫 Log。
但因為常常引用很多開源套件,各個套件用的框架不同,所以我會把所有框架的 Log 全導到 SLF4J
然後再讓 SLF4J 將所有 Log 都輸出給 logback。

以下就是以這個狀況來說,我會寫在 Maven 上的 dependency 設定:

<properties>
	<slf4j.version>1.7.25</slf4j.version>
	<logback.version>1.2.3</logback.version>
</properties>

<dependencies>
	<!-- Logging -->
	<dependency>
		<groupId>org.slf4j</groupId>
		<artifactId>slf4j-api</artifactId>
		<version>${slf4j.version}</version>
	</dependency>
	<dependency>
		<groupId>ch.qos.logback</groupId>
		<artifactId>logback-classic</artifactId>
		<version>${logback.version}</version>
	</dependency>
	<dependency>
		<groupId>ch.qos.logback</groupId>
		<artifactId>logback-core</artifactId>
		<version>${logback.version}</version>
	</dependency>
	<!-- Bridge java.util.logging to SLF4J. -->
	<dependency>
		<groupId>org.slf4j</groupId>
		<artifactId>jul-to-slf4j</artifactId>
		<version>${slf4j.version}</version>
	</dependency>
	<!-- Bridge commons logging to SLF4J. -->
	<dependency>
		<groupId>org.slf4j</groupId>
		<artifactId>jcl-over-slf4j</artifactId>
		<version>${slf4j.version}</version>
	</dependency>
	<!-- Bridge Log4j to SLF4J. -->
	<dependency>
		<groupId>org.slf4j</groupId>
		<artifactId>log4j-over-slf4j</artifactId>
		<version>${slf4j.version}</version>
	</dependency>
	<!-- Bridge Log4j2 to SLF4J. -->
	<dependency>
		<groupId>org.apache.logging.log4j</groupId>
		<artifactId>log4j-to-slf4j</artifactId>
		<version>2.9.1</version>
	</dependency>
	<!-- Globally exclusion -->
	<!-- Reference: https://stackoverflow.com/questions/4716310 -->
	<!-- Ignore logging frameworks other than slf4j -->
	<dependency>
		<groupId>log4j</groupId>
		<artifactId>log4j</artifactId>
		<version>1.2.17</version>
		<scope>provided</scope>
	</dependency>
	<dependency>
		<groupId>commons-logging</groupId>
		<artifactId>commons-logging</artifactId>
		<version>1.2</version>
		<scope>provided</scope>
	</dependency>
</dependencies>

參考資料
  1. SLF4J user manual
  2. Bridging legacy APIs
  3. log4j-over-slf4j与slf4j-log4j12共存stack overflow异常分析

沒有留言: