'實用!細說 Java 主流日誌工具庫'

Java Apache 數據庫 BSD UNIX 設計 用飄柔的程序猿 2019-08-24
"

在項目開發中,為了跟蹤代碼的運行情況,常常要使用日誌來記錄信息。在 Java 世界,有很多的日誌工具庫來實現日誌功能,避免了我們重複造輪子。

我們先來逐一瞭解一下主流日誌工具。

日誌框架

java.util.logging (JUL)

JDK1.4 開始,通過 java.util.logging 提供日誌功能。

它能滿足基本的日誌需要,但是功能沒有 Log4j 強大,而且使用範圍也沒有 Log4j 廣泛。

Log4j

Log4j 是 apache 的一個開源項目,創始人 Ceki Gulcu。

Log4j 應該說是 Java 領域資格最老,應用最廣的日誌工具。從誕生之日到現在一直廣受業界歡迎。

Log4j 是高度可配置的,並可通過在運行時的外部文件配置。它根據記錄的優先級別,並提供機制,以指示記錄信息到許多的目的地,諸如:數據庫,文件,控制檯,UNIX 系統日誌等。

Log4j 中有三個主要組成部分:

  • loggers - 負責捕獲記錄信息。
  • appenders - 負責發佈日誌信息,以不同的首選目的地。
  • layouts - 負責格式化不同風格的日誌信息。

官網地址:

http://logging.apache.org/log4j/2.x/

Logback

Logback 是由 log4j 創始人 Ceki Gulcu 設計的又一個開源日記組件,目標是替代 log4j。

logback 當前分成三個模塊:logback-core、logback-classic 和 logback-access。

  • logback-core - 是其它兩個模塊的基礎模塊。
  • logback-classic - 是 log4j 的一個 改良版本。此外 logback-classic 完整實現 SLF4J API 使你可以很方便地更換成其它日記系統如 log4j 或 JDK14 Logging。
  • logback-access - 訪問模塊與 Servlet 容器集成提供通過 Http 來訪問日記的功能。

官網地址:

http://logback.qos.ch/

Log4j2

官網地址:

http://logging.apache.org/log4j/2.x/

按照官方的說法,Log4j2 是 Log4j 和 Logback 的替代。

Log4j2 架構:


"

在項目開發中,為了跟蹤代碼的運行情況,常常要使用日誌來記錄信息。在 Java 世界,有很多的日誌工具庫來實現日誌功能,避免了我們重複造輪子。

我們先來逐一瞭解一下主流日誌工具。

日誌框架

java.util.logging (JUL)

JDK1.4 開始,通過 java.util.logging 提供日誌功能。

它能滿足基本的日誌需要,但是功能沒有 Log4j 強大,而且使用範圍也沒有 Log4j 廣泛。

Log4j

Log4j 是 apache 的一個開源項目,創始人 Ceki Gulcu。

Log4j 應該說是 Java 領域資格最老,應用最廣的日誌工具。從誕生之日到現在一直廣受業界歡迎。

Log4j 是高度可配置的,並可通過在運行時的外部文件配置。它根據記錄的優先級別,並提供機制,以指示記錄信息到許多的目的地,諸如:數據庫,文件,控制檯,UNIX 系統日誌等。

Log4j 中有三個主要組成部分:

  • loggers - 負責捕獲記錄信息。
  • appenders - 負責發佈日誌信息,以不同的首選目的地。
  • layouts - 負責格式化不同風格的日誌信息。

官網地址:

http://logging.apache.org/log4j/2.x/

Logback

Logback 是由 log4j 創始人 Ceki Gulcu 設計的又一個開源日記組件,目標是替代 log4j。

logback 當前分成三個模塊:logback-core、logback-classic 和 logback-access。

  • logback-core - 是其它兩個模塊的基礎模塊。
  • logback-classic - 是 log4j 的一個 改良版本。此外 logback-classic 完整實現 SLF4J API 使你可以很方便地更換成其它日記系統如 log4j 或 JDK14 Logging。
  • logback-access - 訪問模塊與 Servlet 容器集成提供通過 Http 來訪問日記的功能。

官網地址:

http://logback.qos.ch/

Log4j2

官網地址:

http://logging.apache.org/log4j/2.x/

按照官方的說法,Log4j2 是 Log4j 和 Logback 的替代。

Log4j2 架構:


實用!細說 Java 主流日誌工具庫


Log4j vs Logback vs Log4j2

按照官方的說法,Log4j2 大大優於 Log4j 和 Logback。

那麼,Log4j2 相比於先問世的 Log4j 和 Logback,它具有哪些優勢呢?

  • Log4j2 旨在用作審計日誌記錄框架。Log4j 1.x 和 Logback 都會在重新配置時丟失事件。Log4j 2 不會。在 Logback 中,Appender 中的異常永遠不會對應用程序可見。在 Log4j 中,可以將 Appender 配置為允許異常滲透到應用程序。
  • Log4j2 在多線程場景中,異步 Loggers 的吞吐量比 Log4j 1.x 和 Logback 高 10 倍,延遲低幾個數量級。
  • Log4j2 對於獨立應用程序是無垃圾的,對於穩定狀態日誌記錄期間的 Web 應用程序來說是低垃圾。這減少了垃圾收集器的壓力,並且可以提供更好的響應時間性能。
  • Log4j2 使用插件系統,通過添加新的 Appender、Filter、Layout、Lookup 和 Pattern Converter,可以非常輕鬆地擴展框架,而無需對 Log4j 進行任何更改。
  • 由於插件系統配置更簡單。配置中的條目不需要指定類名。
  • 支持自定義日誌等級。
  • 支持 lambda 表達式。
  • 支持消息對象。
  • Log4j 和 Logback 的 Layout 返回的是字符串,而 Log4j2 返回的是二進制數組,這使得它能被各種 Appender 使用。
  • Syslog Appender 支持 TCP 和 UDP 並且支持 BSD 系統日誌。
  • Log4j2 利用 Java5 併發特性,儘量小粒度的使用鎖,減少鎖的開銷。

日誌門面

何謂日誌門面?

日誌門面是對不同日誌框架提供的一個 API 封裝,可以在部署的時候不修改任何配置即可接入一種日誌實現方案。

common-logging

common-logging 是 apache 的一個開源項目。也稱Jakarta Commons Logging,縮寫 JCL。

common-logging 的功能是提供日誌功能的 API 接口,本身並不提供日誌的具體實現(當然,common-logging 內部有一個 Simple logger 的簡單實現,但是功能很弱,直接忽略),而是在運行時動態的綁定日誌實現組件來工作(如 log4j、java.util.loggin)。

官網地址:

http://commons.apache.org/proper/commons-logging/

slf4j

全稱為 Simple Logging Facade for Java,即 java 簡單日誌門面。

什麼,作者又是 Ceki Gulcu!這位大神寫了 Log4j、Logback 和 slf4j,專注日誌組件開發五百年,一直只能超越自己。

類似於 Common-Logging,slf4j 是對不同日誌框架提供的一個 API 封裝,可以在部署的時候不修改任何配置即可接入一種日誌實現方案。但是,slf4j 在編譯時靜態綁定真正的 Log 庫。使用 SLF4J 時,如果你需要使用某一種日誌實現,那麼你必須選擇正確的 SLF4J 的 jar 包的集合(各種橋接包)。

官網地址:

http://www.slf4j.org/


"

在項目開發中,為了跟蹤代碼的運行情況,常常要使用日誌來記錄信息。在 Java 世界,有很多的日誌工具庫來實現日誌功能,避免了我們重複造輪子。

我們先來逐一瞭解一下主流日誌工具。

日誌框架

java.util.logging (JUL)

JDK1.4 開始,通過 java.util.logging 提供日誌功能。

它能滿足基本的日誌需要,但是功能沒有 Log4j 強大,而且使用範圍也沒有 Log4j 廣泛。

Log4j

Log4j 是 apache 的一個開源項目,創始人 Ceki Gulcu。

Log4j 應該說是 Java 領域資格最老,應用最廣的日誌工具。從誕生之日到現在一直廣受業界歡迎。

Log4j 是高度可配置的,並可通過在運行時的外部文件配置。它根據記錄的優先級別,並提供機制,以指示記錄信息到許多的目的地,諸如:數據庫,文件,控制檯,UNIX 系統日誌等。

Log4j 中有三個主要組成部分:

  • loggers - 負責捕獲記錄信息。
  • appenders - 負責發佈日誌信息,以不同的首選目的地。
  • layouts - 負責格式化不同風格的日誌信息。

官網地址:

http://logging.apache.org/log4j/2.x/

Logback

Logback 是由 log4j 創始人 Ceki Gulcu 設計的又一個開源日記組件,目標是替代 log4j。

logback 當前分成三個模塊:logback-core、logback-classic 和 logback-access。

  • logback-core - 是其它兩個模塊的基礎模塊。
  • logback-classic - 是 log4j 的一個 改良版本。此外 logback-classic 完整實現 SLF4J API 使你可以很方便地更換成其它日記系統如 log4j 或 JDK14 Logging。
  • logback-access - 訪問模塊與 Servlet 容器集成提供通過 Http 來訪問日記的功能。

官網地址:

http://logback.qos.ch/

Log4j2

官網地址:

http://logging.apache.org/log4j/2.x/

按照官方的說法,Log4j2 是 Log4j 和 Logback 的替代。

Log4j2 架構:


實用!細說 Java 主流日誌工具庫


Log4j vs Logback vs Log4j2

按照官方的說法,Log4j2 大大優於 Log4j 和 Logback。

那麼,Log4j2 相比於先問世的 Log4j 和 Logback,它具有哪些優勢呢?

  • Log4j2 旨在用作審計日誌記錄框架。Log4j 1.x 和 Logback 都會在重新配置時丟失事件。Log4j 2 不會。在 Logback 中,Appender 中的異常永遠不會對應用程序可見。在 Log4j 中,可以將 Appender 配置為允許異常滲透到應用程序。
  • Log4j2 在多線程場景中,異步 Loggers 的吞吐量比 Log4j 1.x 和 Logback 高 10 倍,延遲低幾個數量級。
  • Log4j2 對於獨立應用程序是無垃圾的,對於穩定狀態日誌記錄期間的 Web 應用程序來說是低垃圾。這減少了垃圾收集器的壓力,並且可以提供更好的響應時間性能。
  • Log4j2 使用插件系統,通過添加新的 Appender、Filter、Layout、Lookup 和 Pattern Converter,可以非常輕鬆地擴展框架,而無需對 Log4j 進行任何更改。
  • 由於插件系統配置更簡單。配置中的條目不需要指定類名。
  • 支持自定義日誌等級。
  • 支持 lambda 表達式。
  • 支持消息對象。
  • Log4j 和 Logback 的 Layout 返回的是字符串,而 Log4j2 返回的是二進制數組,這使得它能被各種 Appender 使用。
  • Syslog Appender 支持 TCP 和 UDP 並且支持 BSD 系統日誌。
  • Log4j2 利用 Java5 併發特性,儘量小粒度的使用鎖,減少鎖的開銷。

日誌門面

何謂日誌門面?

日誌門面是對不同日誌框架提供的一個 API 封裝,可以在部署的時候不修改任何配置即可接入一種日誌實現方案。

common-logging

common-logging 是 apache 的一個開源項目。也稱Jakarta Commons Logging,縮寫 JCL。

common-logging 的功能是提供日誌功能的 API 接口,本身並不提供日誌的具體實現(當然,common-logging 內部有一個 Simple logger 的簡單實現,但是功能很弱,直接忽略),而是在運行時動態的綁定日誌實現組件來工作(如 log4j、java.util.loggin)。

官網地址:

http://commons.apache.org/proper/commons-logging/

slf4j

全稱為 Simple Logging Facade for Java,即 java 簡單日誌門面。

什麼,作者又是 Ceki Gulcu!這位大神寫了 Log4j、Logback 和 slf4j,專注日誌組件開發五百年,一直只能超越自己。

類似於 Common-Logging,slf4j 是對不同日誌框架提供的一個 API 封裝,可以在部署的時候不修改任何配置即可接入一種日誌實現方案。但是,slf4j 在編譯時靜態綁定真正的 Log 庫。使用 SLF4J 時,如果你需要使用某一種日誌實現,那麼你必須選擇正確的 SLF4J 的 jar 包的集合(各種橋接包)。

官網地址:

http://www.slf4j.org/


實用!細說 Java 主流日誌工具庫


common-logging vs slf4j

slf4j 庫類似於 Apache Common-Logging。但是,他在編譯時靜態綁定真正的日誌庫。這點似乎很麻煩,其實也不過是導入橋接 jar 包而已。

slf4j 一大亮點是提供了更方便的日誌記錄方式:

不需要使用logger.isDebugEnabled()來解決日誌因為字符拼接產生的性能問題。slf4j 的方式是使用{}作為字符串替換符,形式如下:

logger.debug("id: {}, name: {} ", id, name);

總結

綜上所述,使用 slf4j + Logback 可謂是目前最理想的日誌解決方案了。

接下來,就是如何在項目中實施了。

實施日誌解決方案

使用日誌解決方案基本可分為三步:

  • 引入 jar 包
  • 配置
  • 使用 API

常見的各種日誌解決方案的第 2 步和第 3 步基本一樣,實施上的差別主要在第 1 步,也就是使用不同的庫。

引入 jar 包

這裡首選推薦使用 slf4j + logback 的組合。

如果你習慣了 common-logging,可以選擇 common-logging+log4j。

強烈建議不要直接使用日誌實現組件(logback、log4j、java.util.logging),理由前面也說過,就是無法靈活替換日誌庫。

還有一種情況:你的老項目使用了 common-logging,或是直接使用日誌實現組件。如果修改老的代碼,工作量太大,需要兼容處理。在下文,都將看到各種應對方法。

注:據我所知,當前仍沒有方法可以將 slf4j 橋接到 common-logging。如果我孤陋寡聞了,請不吝賜教。

slf4j 直接綁定日誌組件

slf4j + logback

添加依賴到 pom.xml 中即可。

logback-classic-1.0.13.jar 會自動將 slf4j-api-1.7.21.jar 和 logback-core-1.0.13.jar 也添加到你的項目中。

<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.0.13</version>
</dependency>

slf4j + log4j

添加依賴到 pom.xml 中即可。

slf4j-log4j12-1.7.21.jar 會自動將 slf4j-api-1.7.21.jar 和 log4j-1.2.17.jar 也添加到你的項目中。

<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.21</version>
</dependency>

slf4j + java.util.logging

添加依賴到 pom.xml 中即可。

slf4j-jdk14-1.7.21.jar 會自動將 slf4j-api-1.7.21.jar 也添加到你的項目中。

<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-jdk14</artifactId>
<version>1.7.21</version>
</dependency>

slf4j 兼容非 slf4j 日誌組件

在介紹解決方案前,先提一個概念——橋接

什麼是橋接呢

假如你正在開發應用程序所調用的組件當中已經使用了 common-logging,這時你需要 jcl-over-slf4j.jar 把日誌信息輸出重定向到 slf4j-api,slf4j-api 再去調用 slf4j 實際依賴的日誌組件。這個過程稱為橋接。下圖是官方的 slf4j 橋接策略圖:


"

在項目開發中,為了跟蹤代碼的運行情況,常常要使用日誌來記錄信息。在 Java 世界,有很多的日誌工具庫來實現日誌功能,避免了我們重複造輪子。

我們先來逐一瞭解一下主流日誌工具。

日誌框架

java.util.logging (JUL)

JDK1.4 開始,通過 java.util.logging 提供日誌功能。

它能滿足基本的日誌需要,但是功能沒有 Log4j 強大,而且使用範圍也沒有 Log4j 廣泛。

Log4j

Log4j 是 apache 的一個開源項目,創始人 Ceki Gulcu。

Log4j 應該說是 Java 領域資格最老,應用最廣的日誌工具。從誕生之日到現在一直廣受業界歡迎。

Log4j 是高度可配置的,並可通過在運行時的外部文件配置。它根據記錄的優先級別,並提供機制,以指示記錄信息到許多的目的地,諸如:數據庫,文件,控制檯,UNIX 系統日誌等。

Log4j 中有三個主要組成部分:

  • loggers - 負責捕獲記錄信息。
  • appenders - 負責發佈日誌信息,以不同的首選目的地。
  • layouts - 負責格式化不同風格的日誌信息。

官網地址:

http://logging.apache.org/log4j/2.x/

Logback

Logback 是由 log4j 創始人 Ceki Gulcu 設計的又一個開源日記組件,目標是替代 log4j。

logback 當前分成三個模塊:logback-core、logback-classic 和 logback-access。

  • logback-core - 是其它兩個模塊的基礎模塊。
  • logback-classic - 是 log4j 的一個 改良版本。此外 logback-classic 完整實現 SLF4J API 使你可以很方便地更換成其它日記系統如 log4j 或 JDK14 Logging。
  • logback-access - 訪問模塊與 Servlet 容器集成提供通過 Http 來訪問日記的功能。

官網地址:

http://logback.qos.ch/

Log4j2

官網地址:

http://logging.apache.org/log4j/2.x/

按照官方的說法,Log4j2 是 Log4j 和 Logback 的替代。

Log4j2 架構:


實用!細說 Java 主流日誌工具庫


Log4j vs Logback vs Log4j2

按照官方的說法,Log4j2 大大優於 Log4j 和 Logback。

那麼,Log4j2 相比於先問世的 Log4j 和 Logback,它具有哪些優勢呢?

  • Log4j2 旨在用作審計日誌記錄框架。Log4j 1.x 和 Logback 都會在重新配置時丟失事件。Log4j 2 不會。在 Logback 中,Appender 中的異常永遠不會對應用程序可見。在 Log4j 中,可以將 Appender 配置為允許異常滲透到應用程序。
  • Log4j2 在多線程場景中,異步 Loggers 的吞吐量比 Log4j 1.x 和 Logback 高 10 倍,延遲低幾個數量級。
  • Log4j2 對於獨立應用程序是無垃圾的,對於穩定狀態日誌記錄期間的 Web 應用程序來說是低垃圾。這減少了垃圾收集器的壓力,並且可以提供更好的響應時間性能。
  • Log4j2 使用插件系統,通過添加新的 Appender、Filter、Layout、Lookup 和 Pattern Converter,可以非常輕鬆地擴展框架,而無需對 Log4j 進行任何更改。
  • 由於插件系統配置更簡單。配置中的條目不需要指定類名。
  • 支持自定義日誌等級。
  • 支持 lambda 表達式。
  • 支持消息對象。
  • Log4j 和 Logback 的 Layout 返回的是字符串,而 Log4j2 返回的是二進制數組,這使得它能被各種 Appender 使用。
  • Syslog Appender 支持 TCP 和 UDP 並且支持 BSD 系統日誌。
  • Log4j2 利用 Java5 併發特性,儘量小粒度的使用鎖,減少鎖的開銷。

日誌門面

何謂日誌門面?

日誌門面是對不同日誌框架提供的一個 API 封裝,可以在部署的時候不修改任何配置即可接入一種日誌實現方案。

common-logging

common-logging 是 apache 的一個開源項目。也稱Jakarta Commons Logging,縮寫 JCL。

common-logging 的功能是提供日誌功能的 API 接口,本身並不提供日誌的具體實現(當然,common-logging 內部有一個 Simple logger 的簡單實現,但是功能很弱,直接忽略),而是在運行時動態的綁定日誌實現組件來工作(如 log4j、java.util.loggin)。

官網地址:

http://commons.apache.org/proper/commons-logging/

slf4j

全稱為 Simple Logging Facade for Java,即 java 簡單日誌門面。

什麼,作者又是 Ceki Gulcu!這位大神寫了 Log4j、Logback 和 slf4j,專注日誌組件開發五百年,一直只能超越自己。

類似於 Common-Logging,slf4j 是對不同日誌框架提供的一個 API 封裝,可以在部署的時候不修改任何配置即可接入一種日誌實現方案。但是,slf4j 在編譯時靜態綁定真正的 Log 庫。使用 SLF4J 時,如果你需要使用某一種日誌實現,那麼你必須選擇正確的 SLF4J 的 jar 包的集合(各種橋接包)。

官網地址:

http://www.slf4j.org/


實用!細說 Java 主流日誌工具庫


common-logging vs slf4j

slf4j 庫類似於 Apache Common-Logging。但是,他在編譯時靜態綁定真正的日誌庫。這點似乎很麻煩,其實也不過是導入橋接 jar 包而已。

slf4j 一大亮點是提供了更方便的日誌記錄方式:

不需要使用logger.isDebugEnabled()來解決日誌因為字符拼接產生的性能問題。slf4j 的方式是使用{}作為字符串替換符,形式如下:

logger.debug("id: {}, name: {} ", id, name);

總結

綜上所述,使用 slf4j + Logback 可謂是目前最理想的日誌解決方案了。

接下來,就是如何在項目中實施了。

實施日誌解決方案

使用日誌解決方案基本可分為三步:

  • 引入 jar 包
  • 配置
  • 使用 API

常見的各種日誌解決方案的第 2 步和第 3 步基本一樣,實施上的差別主要在第 1 步,也就是使用不同的庫。

引入 jar 包

這裡首選推薦使用 slf4j + logback 的組合。

如果你習慣了 common-logging,可以選擇 common-logging+log4j。

強烈建議不要直接使用日誌實現組件(logback、log4j、java.util.logging),理由前面也說過,就是無法靈活替換日誌庫。

還有一種情況:你的老項目使用了 common-logging,或是直接使用日誌實現組件。如果修改老的代碼,工作量太大,需要兼容處理。在下文,都將看到各種應對方法。

注:據我所知,當前仍沒有方法可以將 slf4j 橋接到 common-logging。如果我孤陋寡聞了,請不吝賜教。

slf4j 直接綁定日誌組件

slf4j + logback

添加依賴到 pom.xml 中即可。

logback-classic-1.0.13.jar 會自動將 slf4j-api-1.7.21.jar 和 logback-core-1.0.13.jar 也添加到你的項目中。

<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.0.13</version>
</dependency>

slf4j + log4j

添加依賴到 pom.xml 中即可。

slf4j-log4j12-1.7.21.jar 會自動將 slf4j-api-1.7.21.jar 和 log4j-1.2.17.jar 也添加到你的項目中。

<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.21</version>
</dependency>

slf4j + java.util.logging

添加依賴到 pom.xml 中即可。

slf4j-jdk14-1.7.21.jar 會自動將 slf4j-api-1.7.21.jar 也添加到你的項目中。

<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-jdk14</artifactId>
<version>1.7.21</version>
</dependency>

slf4j 兼容非 slf4j 日誌組件

在介紹解決方案前,先提一個概念——橋接

什麼是橋接呢

假如你正在開發應用程序所調用的組件當中已經使用了 common-logging,這時你需要 jcl-over-slf4j.jar 把日誌信息輸出重定向到 slf4j-api,slf4j-api 再去調用 slf4j 實際依賴的日誌組件。這個過程稱為橋接。下圖是官方的 slf4j 橋接策略圖:


實用!細說 Java 主流日誌工具庫


從圖中應該可以看出,無論你的老項目中使用的是 common-logging 或是直接使用 log4j、java.util.logging,都可以使用對應的橋接 jar 包來解決兼容問題。

slf4j 兼容 common-logging

<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
<version>1.7.12</version>
</dependency>

slf4j 兼容 log4j

<dependency>
<groupId>org.slf4j</groupId>
<artifactId>log4j-over-slf4j</artifactId>
<version>1.7.12</version>
</dependency>

slf4j 兼容 java.util.logging

<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jul-to-slf4j</artifactId>
<version>1.7.12</version>
</dependency>

spring 集成 slf4j

做 java web 開發,基本離不開 spring 框架。很遺憾,spring 使用的日誌解決方案是 common-logging + log4j。(系統的學習spring全家桶,可以在Java知音公眾號內回覆“Springboot聚合”)

所以,你需要一個橋接 jar 包:logback-ext-spring。

<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.1.3</version>
</dependency>
<dependency>
<groupId>org.logback-extensions</groupId>
<artifactId>logback-ext-spring</artifactId>
<version>0.1.2</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
<version>1.7.12</version>
</dependency>

common-logging 綁定日誌組件

common-logging + log4j

添加依賴到 pom.xml 中即可。

<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.2</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>

使用 API

slf4j 用法

使用 slf4j 的 API 很簡單。使用LoggerFactory初始化一個Logger實例,然後調用 Logger 對應的打印等級函數就行了。

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class App {
private static final Logger log = LoggerFactory.getLogger(App.class);
public static void main(String[] args) {
String msg = "print log, current level: {}";
log.trace(msg, "trace");
log.debug(msg, "debug");
log.info(msg, "info");
log.warn(msg, "warn");
log.error(msg, "error");
}
}

common-logging 用法

common-logging 用法和 slf4j 幾乎一樣,但是支持的打印等級多了一個更高級別的:fatal。

此外,common-logging 不支持{}替換參數,你只能選擇拼接字符串這種方式了。

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
public class JclTest {
private static final Log log = LogFactory.getLog(JclTest.class);
public static void main(String[] args) {
String msg = "print log, current level: ";
log.trace(msg + "trace");
log.debug(msg + "debug");
log.info(msg + "info");
log.warn(msg + "warn");
log.error(msg + "error");
log.fatal(msg + "fatal");
}
}

log4j2 配置

log4j2 基本配置形式如下:

<?xml version="1.0" encoding="UTF-8"?>;
<Configuration>
<Properties>
<Property name="name1">value</property>
<Property name="name2" value="value2"/>
</Properties>
<Filter type="type" ... />
<Appenders>
<Appender type="type" name="name">
<Filter type="type" ... />
</Appender>
...
</Appenders>
<Loggers>
<Logger name="name1">
<Filter type="type" ... />
</Logger>
...
<Root level="level">
<AppenderRef ref="name"/>
</Root>
</Loggers>
</Configuration>

配置示例:

<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="debug" strict="true" name="XMLConfigTest"
packages="org.apache.logging.log4j.test">
<Properties>
<Property name="filename">target/test.log</Property>
</Properties>
<Filter type="ThresholdFilter" level="trace"/>
<Appenders>
<Appender type="Console" name="STDOUT">
<Layout type="PatternLayout" pattern="%m MDC%X%n"/>
<Filters>
<Filter type="MarkerFilter" marker="FLOW" onMatch="DENY" onMismatch="NEUTRAL"/>
<Filter type="MarkerFilter" marker="EXCEPTION" onMatch="DENY" onMismatch="ACCEPT"/>
</Filters>
</Appender>
<Appender type="Console" name="FLOW">
<Layout type="PatternLayout" pattern="%C{1}.%M %m %ex%n"/><!-- class and line number -->
<Filters>
<Filter type="MarkerFilter" marker="FLOW" onMatch="ACCEPT" onMismatch="NEUTRAL"/>
<Filter type="MarkerFilter" marker="EXCEPTION" onMatch="ACCEPT" onMismatch="DENY"/>
</Filters>
</Appender>
<Appender type="File" name="File" fileName="${filename}">
<Layout type="PatternLayout">
<Pattern>%d %p %C{1.} [%t] %m%n</Pattern>
</Layout>
</Appender>
</Appenders>
<Loggers>
<Logger name="org.apache.logging.log4j.test1" level="debug" additivity="false">
<Filter type="ThreadContextMapFilter">
<KeyValuePair key="test" value="123"/>
</Filter>
<AppenderRef ref="STDOUT"/>
</Logger>
<Logger name="org.apache.logging.log4j.test2" level="debug" additivity="false">
<AppenderRef ref="File"/>
</Logger>
<Root level="trace">
<AppenderRef ref="STDOUT"/>
</Root>
</Loggers>
</Configuration>

logback 配置

<configuration>

作用:<configuration> 是 logback 配置文件的根元素。

要點:它有 <appender>、<logger>、<root> 三個子元素。


"

在項目開發中,為了跟蹤代碼的運行情況,常常要使用日誌來記錄信息。在 Java 世界,有很多的日誌工具庫來實現日誌功能,避免了我們重複造輪子。

我們先來逐一瞭解一下主流日誌工具。

日誌框架

java.util.logging (JUL)

JDK1.4 開始,通過 java.util.logging 提供日誌功能。

它能滿足基本的日誌需要,但是功能沒有 Log4j 強大,而且使用範圍也沒有 Log4j 廣泛。

Log4j

Log4j 是 apache 的一個開源項目,創始人 Ceki Gulcu。

Log4j 應該說是 Java 領域資格最老,應用最廣的日誌工具。從誕生之日到現在一直廣受業界歡迎。

Log4j 是高度可配置的,並可通過在運行時的外部文件配置。它根據記錄的優先級別,並提供機制,以指示記錄信息到許多的目的地,諸如:數據庫,文件,控制檯,UNIX 系統日誌等。

Log4j 中有三個主要組成部分:

  • loggers - 負責捕獲記錄信息。
  • appenders - 負責發佈日誌信息,以不同的首選目的地。
  • layouts - 負責格式化不同風格的日誌信息。

官網地址:

http://logging.apache.org/log4j/2.x/

Logback

Logback 是由 log4j 創始人 Ceki Gulcu 設計的又一個開源日記組件,目標是替代 log4j。

logback 當前分成三個模塊:logback-core、logback-classic 和 logback-access。

  • logback-core - 是其它兩個模塊的基礎模塊。
  • logback-classic - 是 log4j 的一個 改良版本。此外 logback-classic 完整實現 SLF4J API 使你可以很方便地更換成其它日記系統如 log4j 或 JDK14 Logging。
  • logback-access - 訪問模塊與 Servlet 容器集成提供通過 Http 來訪問日記的功能。

官網地址:

http://logback.qos.ch/

Log4j2

官網地址:

http://logging.apache.org/log4j/2.x/

按照官方的說法,Log4j2 是 Log4j 和 Logback 的替代。

Log4j2 架構:


實用!細說 Java 主流日誌工具庫


Log4j vs Logback vs Log4j2

按照官方的說法,Log4j2 大大優於 Log4j 和 Logback。

那麼,Log4j2 相比於先問世的 Log4j 和 Logback,它具有哪些優勢呢?

  • Log4j2 旨在用作審計日誌記錄框架。Log4j 1.x 和 Logback 都會在重新配置時丟失事件。Log4j 2 不會。在 Logback 中,Appender 中的異常永遠不會對應用程序可見。在 Log4j 中,可以將 Appender 配置為允許異常滲透到應用程序。
  • Log4j2 在多線程場景中,異步 Loggers 的吞吐量比 Log4j 1.x 和 Logback 高 10 倍,延遲低幾個數量級。
  • Log4j2 對於獨立應用程序是無垃圾的,對於穩定狀態日誌記錄期間的 Web 應用程序來說是低垃圾。這減少了垃圾收集器的壓力,並且可以提供更好的響應時間性能。
  • Log4j2 使用插件系統,通過添加新的 Appender、Filter、Layout、Lookup 和 Pattern Converter,可以非常輕鬆地擴展框架,而無需對 Log4j 進行任何更改。
  • 由於插件系統配置更簡單。配置中的條目不需要指定類名。
  • 支持自定義日誌等級。
  • 支持 lambda 表達式。
  • 支持消息對象。
  • Log4j 和 Logback 的 Layout 返回的是字符串,而 Log4j2 返回的是二進制數組,這使得它能被各種 Appender 使用。
  • Syslog Appender 支持 TCP 和 UDP 並且支持 BSD 系統日誌。
  • Log4j2 利用 Java5 併發特性,儘量小粒度的使用鎖,減少鎖的開銷。

日誌門面

何謂日誌門面?

日誌門面是對不同日誌框架提供的一個 API 封裝,可以在部署的時候不修改任何配置即可接入一種日誌實現方案。

common-logging

common-logging 是 apache 的一個開源項目。也稱Jakarta Commons Logging,縮寫 JCL。

common-logging 的功能是提供日誌功能的 API 接口,本身並不提供日誌的具體實現(當然,common-logging 內部有一個 Simple logger 的簡單實現,但是功能很弱,直接忽略),而是在運行時動態的綁定日誌實現組件來工作(如 log4j、java.util.loggin)。

官網地址:

http://commons.apache.org/proper/commons-logging/

slf4j

全稱為 Simple Logging Facade for Java,即 java 簡單日誌門面。

什麼,作者又是 Ceki Gulcu!這位大神寫了 Log4j、Logback 和 slf4j,專注日誌組件開發五百年,一直只能超越自己。

類似於 Common-Logging,slf4j 是對不同日誌框架提供的一個 API 封裝,可以在部署的時候不修改任何配置即可接入一種日誌實現方案。但是,slf4j 在編譯時靜態綁定真正的 Log 庫。使用 SLF4J 時,如果你需要使用某一種日誌實現,那麼你必須選擇正確的 SLF4J 的 jar 包的集合(各種橋接包)。

官網地址:

http://www.slf4j.org/


實用!細說 Java 主流日誌工具庫


common-logging vs slf4j

slf4j 庫類似於 Apache Common-Logging。但是,他在編譯時靜態綁定真正的日誌庫。這點似乎很麻煩,其實也不過是導入橋接 jar 包而已。

slf4j 一大亮點是提供了更方便的日誌記錄方式:

不需要使用logger.isDebugEnabled()來解決日誌因為字符拼接產生的性能問題。slf4j 的方式是使用{}作為字符串替換符,形式如下:

logger.debug("id: {}, name: {} ", id, name);

總結

綜上所述,使用 slf4j + Logback 可謂是目前最理想的日誌解決方案了。

接下來,就是如何在項目中實施了。

實施日誌解決方案

使用日誌解決方案基本可分為三步:

  • 引入 jar 包
  • 配置
  • 使用 API

常見的各種日誌解決方案的第 2 步和第 3 步基本一樣,實施上的差別主要在第 1 步,也就是使用不同的庫。

引入 jar 包

這裡首選推薦使用 slf4j + logback 的組合。

如果你習慣了 common-logging,可以選擇 common-logging+log4j。

強烈建議不要直接使用日誌實現組件(logback、log4j、java.util.logging),理由前面也說過,就是無法靈活替換日誌庫。

還有一種情況:你的老項目使用了 common-logging,或是直接使用日誌實現組件。如果修改老的代碼,工作量太大,需要兼容處理。在下文,都將看到各種應對方法。

注:據我所知,當前仍沒有方法可以將 slf4j 橋接到 common-logging。如果我孤陋寡聞了,請不吝賜教。

slf4j 直接綁定日誌組件

slf4j + logback

添加依賴到 pom.xml 中即可。

logback-classic-1.0.13.jar 會自動將 slf4j-api-1.7.21.jar 和 logback-core-1.0.13.jar 也添加到你的項目中。

<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.0.13</version>
</dependency>

slf4j + log4j

添加依賴到 pom.xml 中即可。

slf4j-log4j12-1.7.21.jar 會自動將 slf4j-api-1.7.21.jar 和 log4j-1.2.17.jar 也添加到你的項目中。

<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.21</version>
</dependency>

slf4j + java.util.logging

添加依賴到 pom.xml 中即可。

slf4j-jdk14-1.7.21.jar 會自動將 slf4j-api-1.7.21.jar 也添加到你的項目中。

<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-jdk14</artifactId>
<version>1.7.21</version>
</dependency>

slf4j 兼容非 slf4j 日誌組件

在介紹解決方案前,先提一個概念——橋接

什麼是橋接呢

假如你正在開發應用程序所調用的組件當中已經使用了 common-logging,這時你需要 jcl-over-slf4j.jar 把日誌信息輸出重定向到 slf4j-api,slf4j-api 再去調用 slf4j 實際依賴的日誌組件。這個過程稱為橋接。下圖是官方的 slf4j 橋接策略圖:


實用!細說 Java 主流日誌工具庫


從圖中應該可以看出,無論你的老項目中使用的是 common-logging 或是直接使用 log4j、java.util.logging,都可以使用對應的橋接 jar 包來解決兼容問題。

slf4j 兼容 common-logging

<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
<version>1.7.12</version>
</dependency>

slf4j 兼容 log4j

<dependency>
<groupId>org.slf4j</groupId>
<artifactId>log4j-over-slf4j</artifactId>
<version>1.7.12</version>
</dependency>

slf4j 兼容 java.util.logging

<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jul-to-slf4j</artifactId>
<version>1.7.12</version>
</dependency>

spring 集成 slf4j

做 java web 開發,基本離不開 spring 框架。很遺憾,spring 使用的日誌解決方案是 common-logging + log4j。(系統的學習spring全家桶,可以在Java知音公眾號內回覆“Springboot聚合”)

所以,你需要一個橋接 jar 包:logback-ext-spring。

<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.1.3</version>
</dependency>
<dependency>
<groupId>org.logback-extensions</groupId>
<artifactId>logback-ext-spring</artifactId>
<version>0.1.2</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
<version>1.7.12</version>
</dependency>

common-logging 綁定日誌組件

common-logging + log4j

添加依賴到 pom.xml 中即可。

<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.2</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>

使用 API

slf4j 用法

使用 slf4j 的 API 很簡單。使用LoggerFactory初始化一個Logger實例,然後調用 Logger 對應的打印等級函數就行了。

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class App {
private static final Logger log = LoggerFactory.getLogger(App.class);
public static void main(String[] args) {
String msg = "print log, current level: {}";
log.trace(msg, "trace");
log.debug(msg, "debug");
log.info(msg, "info");
log.warn(msg, "warn");
log.error(msg, "error");
}
}

common-logging 用法

common-logging 用法和 slf4j 幾乎一樣,但是支持的打印等級多了一個更高級別的:fatal。

此外,common-logging 不支持{}替換參數,你只能選擇拼接字符串這種方式了。

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
public class JclTest {
private static final Log log = LogFactory.getLog(JclTest.class);
public static void main(String[] args) {
String msg = "print log, current level: ";
log.trace(msg + "trace");
log.debug(msg + "debug");
log.info(msg + "info");
log.warn(msg + "warn");
log.error(msg + "error");
log.fatal(msg + "fatal");
}
}

log4j2 配置

log4j2 基本配置形式如下:

<?xml version="1.0" encoding="UTF-8"?>;
<Configuration>
<Properties>
<Property name="name1">value</property>
<Property name="name2" value="value2"/>
</Properties>
<Filter type="type" ... />
<Appenders>
<Appender type="type" name="name">
<Filter type="type" ... />
</Appender>
...
</Appenders>
<Loggers>
<Logger name="name1">
<Filter type="type" ... />
</Logger>
...
<Root level="level">
<AppenderRef ref="name"/>
</Root>
</Loggers>
</Configuration>

配置示例:

<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="debug" strict="true" name="XMLConfigTest"
packages="org.apache.logging.log4j.test">
<Properties>
<Property name="filename">target/test.log</Property>
</Properties>
<Filter type="ThresholdFilter" level="trace"/>
<Appenders>
<Appender type="Console" name="STDOUT">
<Layout type="PatternLayout" pattern="%m MDC%X%n"/>
<Filters>
<Filter type="MarkerFilter" marker="FLOW" onMatch="DENY" onMismatch="NEUTRAL"/>
<Filter type="MarkerFilter" marker="EXCEPTION" onMatch="DENY" onMismatch="ACCEPT"/>
</Filters>
</Appender>
<Appender type="Console" name="FLOW">
<Layout type="PatternLayout" pattern="%C{1}.%M %m %ex%n"/><!-- class and line number -->
<Filters>
<Filter type="MarkerFilter" marker="FLOW" onMatch="ACCEPT" onMismatch="NEUTRAL"/>
<Filter type="MarkerFilter" marker="EXCEPTION" onMatch="ACCEPT" onMismatch="DENY"/>
</Filters>
</Appender>
<Appender type="File" name="File" fileName="${filename}">
<Layout type="PatternLayout">
<Pattern>%d %p %C{1.} [%t] %m%n</Pattern>
</Layout>
</Appender>
</Appenders>
<Loggers>
<Logger name="org.apache.logging.log4j.test1" level="debug" additivity="false">
<Filter type="ThreadContextMapFilter">
<KeyValuePair key="test" value="123"/>
</Filter>
<AppenderRef ref="STDOUT"/>
</Logger>
<Logger name="org.apache.logging.log4j.test2" level="debug" additivity="false">
<AppenderRef ref="File"/>
</Logger>
<Root level="trace">
<AppenderRef ref="STDOUT"/>
</Root>
</Loggers>
</Configuration>

logback 配置

<configuration>

作用:<configuration> 是 logback 配置文件的根元素。

要點:它有 <appender>、<logger>、<root> 三個子元素。


實用!細說 Java 主流日誌工具庫


<appender>

作用:將記錄日誌的任務委託給名為 appender 的組件。

要點:可以配置零個或多個;它有 <file>、<filter>、<layout>、<encoder> 四個子元素。

屬性:

  • name:設置 appender 名稱。
  • class:設置具體的實例化類。


<file>

作用:設置日誌文件路徑。

<filter>

作用:設置過濾器。

要點:可以配置零個或多個。

<layout>

作用:設置 appender。

要點:可以配置零個或一個。

屬性:

  • class:設置具體的實例化類。


<encoder>

作用:設置編碼。

要點:可以配置零個或多個。

屬性:

  • class:設置具體的實例化類。



"

在項目開發中,為了跟蹤代碼的運行情況,常常要使用日誌來記錄信息。在 Java 世界,有很多的日誌工具庫來實現日誌功能,避免了我們重複造輪子。

我們先來逐一瞭解一下主流日誌工具。

日誌框架

java.util.logging (JUL)

JDK1.4 開始,通過 java.util.logging 提供日誌功能。

它能滿足基本的日誌需要,但是功能沒有 Log4j 強大,而且使用範圍也沒有 Log4j 廣泛。

Log4j

Log4j 是 apache 的一個開源項目,創始人 Ceki Gulcu。

Log4j 應該說是 Java 領域資格最老,應用最廣的日誌工具。從誕生之日到現在一直廣受業界歡迎。

Log4j 是高度可配置的,並可通過在運行時的外部文件配置。它根據記錄的優先級別,並提供機制,以指示記錄信息到許多的目的地,諸如:數據庫,文件,控制檯,UNIX 系統日誌等。

Log4j 中有三個主要組成部分:

  • loggers - 負責捕獲記錄信息。
  • appenders - 負責發佈日誌信息,以不同的首選目的地。
  • layouts - 負責格式化不同風格的日誌信息。

官網地址:

http://logging.apache.org/log4j/2.x/

Logback

Logback 是由 log4j 創始人 Ceki Gulcu 設計的又一個開源日記組件,目標是替代 log4j。

logback 當前分成三個模塊:logback-core、logback-classic 和 logback-access。

  • logback-core - 是其它兩個模塊的基礎模塊。
  • logback-classic - 是 log4j 的一個 改良版本。此外 logback-classic 完整實現 SLF4J API 使你可以很方便地更換成其它日記系統如 log4j 或 JDK14 Logging。
  • logback-access - 訪問模塊與 Servlet 容器集成提供通過 Http 來訪問日記的功能。

官網地址:

http://logback.qos.ch/

Log4j2

官網地址:

http://logging.apache.org/log4j/2.x/

按照官方的說法,Log4j2 是 Log4j 和 Logback 的替代。

Log4j2 架構:


實用!細說 Java 主流日誌工具庫


Log4j vs Logback vs Log4j2

按照官方的說法,Log4j2 大大優於 Log4j 和 Logback。

那麼,Log4j2 相比於先問世的 Log4j 和 Logback,它具有哪些優勢呢?

  • Log4j2 旨在用作審計日誌記錄框架。Log4j 1.x 和 Logback 都會在重新配置時丟失事件。Log4j 2 不會。在 Logback 中,Appender 中的異常永遠不會對應用程序可見。在 Log4j 中,可以將 Appender 配置為允許異常滲透到應用程序。
  • Log4j2 在多線程場景中,異步 Loggers 的吞吐量比 Log4j 1.x 和 Logback 高 10 倍,延遲低幾個數量級。
  • Log4j2 對於獨立應用程序是無垃圾的,對於穩定狀態日誌記錄期間的 Web 應用程序來說是低垃圾。這減少了垃圾收集器的壓力,並且可以提供更好的響應時間性能。
  • Log4j2 使用插件系統,通過添加新的 Appender、Filter、Layout、Lookup 和 Pattern Converter,可以非常輕鬆地擴展框架,而無需對 Log4j 進行任何更改。
  • 由於插件系統配置更簡單。配置中的條目不需要指定類名。
  • 支持自定義日誌等級。
  • 支持 lambda 表達式。
  • 支持消息對象。
  • Log4j 和 Logback 的 Layout 返回的是字符串,而 Log4j2 返回的是二進制數組,這使得它能被各種 Appender 使用。
  • Syslog Appender 支持 TCP 和 UDP 並且支持 BSD 系統日誌。
  • Log4j2 利用 Java5 併發特性,儘量小粒度的使用鎖,減少鎖的開銷。

日誌門面

何謂日誌門面?

日誌門面是對不同日誌框架提供的一個 API 封裝,可以在部署的時候不修改任何配置即可接入一種日誌實現方案。

common-logging

common-logging 是 apache 的一個開源項目。也稱Jakarta Commons Logging,縮寫 JCL。

common-logging 的功能是提供日誌功能的 API 接口,本身並不提供日誌的具體實現(當然,common-logging 內部有一個 Simple logger 的簡單實現,但是功能很弱,直接忽略),而是在運行時動態的綁定日誌實現組件來工作(如 log4j、java.util.loggin)。

官網地址:

http://commons.apache.org/proper/commons-logging/

slf4j

全稱為 Simple Logging Facade for Java,即 java 簡單日誌門面。

什麼,作者又是 Ceki Gulcu!這位大神寫了 Log4j、Logback 和 slf4j,專注日誌組件開發五百年,一直只能超越自己。

類似於 Common-Logging,slf4j 是對不同日誌框架提供的一個 API 封裝,可以在部署的時候不修改任何配置即可接入一種日誌實現方案。但是,slf4j 在編譯時靜態綁定真正的 Log 庫。使用 SLF4J 時,如果你需要使用某一種日誌實現,那麼你必須選擇正確的 SLF4J 的 jar 包的集合(各種橋接包)。

官網地址:

http://www.slf4j.org/


實用!細說 Java 主流日誌工具庫


common-logging vs slf4j

slf4j 庫類似於 Apache Common-Logging。但是,他在編譯時靜態綁定真正的日誌庫。這點似乎很麻煩,其實也不過是導入橋接 jar 包而已。

slf4j 一大亮點是提供了更方便的日誌記錄方式:

不需要使用logger.isDebugEnabled()來解決日誌因為字符拼接產生的性能問題。slf4j 的方式是使用{}作為字符串替換符,形式如下:

logger.debug("id: {}, name: {} ", id, name);

總結

綜上所述,使用 slf4j + Logback 可謂是目前最理想的日誌解決方案了。

接下來,就是如何在項目中實施了。

實施日誌解決方案

使用日誌解決方案基本可分為三步:

  • 引入 jar 包
  • 配置
  • 使用 API

常見的各種日誌解決方案的第 2 步和第 3 步基本一樣,實施上的差別主要在第 1 步,也就是使用不同的庫。

引入 jar 包

這裡首選推薦使用 slf4j + logback 的組合。

如果你習慣了 common-logging,可以選擇 common-logging+log4j。

強烈建議不要直接使用日誌實現組件(logback、log4j、java.util.logging),理由前面也說過,就是無法靈活替換日誌庫。

還有一種情況:你的老項目使用了 common-logging,或是直接使用日誌實現組件。如果修改老的代碼,工作量太大,需要兼容處理。在下文,都將看到各種應對方法。

注:據我所知,當前仍沒有方法可以將 slf4j 橋接到 common-logging。如果我孤陋寡聞了,請不吝賜教。

slf4j 直接綁定日誌組件

slf4j + logback

添加依賴到 pom.xml 中即可。

logback-classic-1.0.13.jar 會自動將 slf4j-api-1.7.21.jar 和 logback-core-1.0.13.jar 也添加到你的項目中。

<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.0.13</version>
</dependency>

slf4j + log4j

添加依賴到 pom.xml 中即可。

slf4j-log4j12-1.7.21.jar 會自動將 slf4j-api-1.7.21.jar 和 log4j-1.2.17.jar 也添加到你的項目中。

<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.21</version>
</dependency>

slf4j + java.util.logging

添加依賴到 pom.xml 中即可。

slf4j-jdk14-1.7.21.jar 會自動將 slf4j-api-1.7.21.jar 也添加到你的項目中。

<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-jdk14</artifactId>
<version>1.7.21</version>
</dependency>

slf4j 兼容非 slf4j 日誌組件

在介紹解決方案前,先提一個概念——橋接

什麼是橋接呢

假如你正在開發應用程序所調用的組件當中已經使用了 common-logging,這時你需要 jcl-over-slf4j.jar 把日誌信息輸出重定向到 slf4j-api,slf4j-api 再去調用 slf4j 實際依賴的日誌組件。這個過程稱為橋接。下圖是官方的 slf4j 橋接策略圖:


實用!細說 Java 主流日誌工具庫


從圖中應該可以看出,無論你的老項目中使用的是 common-logging 或是直接使用 log4j、java.util.logging,都可以使用對應的橋接 jar 包來解決兼容問題。

slf4j 兼容 common-logging

<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
<version>1.7.12</version>
</dependency>

slf4j 兼容 log4j

<dependency>
<groupId>org.slf4j</groupId>
<artifactId>log4j-over-slf4j</artifactId>
<version>1.7.12</version>
</dependency>

slf4j 兼容 java.util.logging

<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jul-to-slf4j</artifactId>
<version>1.7.12</version>
</dependency>

spring 集成 slf4j

做 java web 開發,基本離不開 spring 框架。很遺憾,spring 使用的日誌解決方案是 common-logging + log4j。(系統的學習spring全家桶,可以在Java知音公眾號內回覆“Springboot聚合”)

所以,你需要一個橋接 jar 包:logback-ext-spring。

<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.1.3</version>
</dependency>
<dependency>
<groupId>org.logback-extensions</groupId>
<artifactId>logback-ext-spring</artifactId>
<version>0.1.2</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
<version>1.7.12</version>
</dependency>

common-logging 綁定日誌組件

common-logging + log4j

添加依賴到 pom.xml 中即可。

<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.2</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>

使用 API

slf4j 用法

使用 slf4j 的 API 很簡單。使用LoggerFactory初始化一個Logger實例,然後調用 Logger 對應的打印等級函數就行了。

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class App {
private static final Logger log = LoggerFactory.getLogger(App.class);
public static void main(String[] args) {
String msg = "print log, current level: {}";
log.trace(msg, "trace");
log.debug(msg, "debug");
log.info(msg, "info");
log.warn(msg, "warn");
log.error(msg, "error");
}
}

common-logging 用法

common-logging 用法和 slf4j 幾乎一樣,但是支持的打印等級多了一個更高級別的:fatal。

此外,common-logging 不支持{}替換參數,你只能選擇拼接字符串這種方式了。

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
public class JclTest {
private static final Log log = LogFactory.getLog(JclTest.class);
public static void main(String[] args) {
String msg = "print log, current level: ";
log.trace(msg + "trace");
log.debug(msg + "debug");
log.info(msg + "info");
log.warn(msg + "warn");
log.error(msg + "error");
log.fatal(msg + "fatal");
}
}

log4j2 配置

log4j2 基本配置形式如下:

<?xml version="1.0" encoding="UTF-8"?>;
<Configuration>
<Properties>
<Property name="name1">value</property>
<Property name="name2" value="value2"/>
</Properties>
<Filter type="type" ... />
<Appenders>
<Appender type="type" name="name">
<Filter type="type" ... />
</Appender>
...
</Appenders>
<Loggers>
<Logger name="name1">
<Filter type="type" ... />
</Logger>
...
<Root level="level">
<AppenderRef ref="name"/>
</Root>
</Loggers>
</Configuration>

配置示例:

<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="debug" strict="true" name="XMLConfigTest"
packages="org.apache.logging.log4j.test">
<Properties>
<Property name="filename">target/test.log</Property>
</Properties>
<Filter type="ThresholdFilter" level="trace"/>
<Appenders>
<Appender type="Console" name="STDOUT">
<Layout type="PatternLayout" pattern="%m MDC%X%n"/>
<Filters>
<Filter type="MarkerFilter" marker="FLOW" onMatch="DENY" onMismatch="NEUTRAL"/>
<Filter type="MarkerFilter" marker="EXCEPTION" onMatch="DENY" onMismatch="ACCEPT"/>
</Filters>
</Appender>
<Appender type="Console" name="FLOW">
<Layout type="PatternLayout" pattern="%C{1}.%M %m %ex%n"/><!-- class and line number -->
<Filters>
<Filter type="MarkerFilter" marker="FLOW" onMatch="ACCEPT" onMismatch="NEUTRAL"/>
<Filter type="MarkerFilter" marker="EXCEPTION" onMatch="ACCEPT" onMismatch="DENY"/>
</Filters>
</Appender>
<Appender type="File" name="File" fileName="${filename}">
<Layout type="PatternLayout">
<Pattern>%d %p %C{1.} [%t] %m%n</Pattern>
</Layout>
</Appender>
</Appenders>
<Loggers>
<Logger name="org.apache.logging.log4j.test1" level="debug" additivity="false">
<Filter type="ThreadContextMapFilter">
<KeyValuePair key="test" value="123"/>
</Filter>
<AppenderRef ref="STDOUT"/>
</Logger>
<Logger name="org.apache.logging.log4j.test2" level="debug" additivity="false">
<AppenderRef ref="File"/>
</Logger>
<Root level="trace">
<AppenderRef ref="STDOUT"/>
</Root>
</Loggers>
</Configuration>

logback 配置

<configuration>

作用:<configuration> 是 logback 配置文件的根元素。

要點:它有 <appender>、<logger>、<root> 三個子元素。


實用!細說 Java 主流日誌工具庫


<appender>

作用:將記錄日誌的任務委託給名為 appender 的組件。

要點:可以配置零個或多個;它有 <file>、<filter>、<layout>、<encoder> 四個子元素。

屬性:

  • name:設置 appender 名稱。
  • class:設置具體的實例化類。


<file>

作用:設置日誌文件路徑。

<filter>

作用:設置過濾器。

要點:可以配置零個或多個。

<layout>

作用:設置 appender。

要點:可以配置零個或一個。

屬性:

  • class:設置具體的實例化類。


<encoder>

作用:設置編碼。

要點:可以配置零個或多個。

屬性:

  • class:設置具體的實例化類。



實用!細說 Java 主流日誌工具庫



<logger>

作用:設置 logger。

要點:可以配置零個或多個。

屬性:

  • name
  • level:設置日誌級別。不區分大小寫。可選值:TRACE、DEBUG、INFO、WARN、ERROR、ALL、OFF。
  • additivity:可選值:true 或 false。


<appender-ref>

作用:appender 引用。

要點:可以配置零個或多個。

<root>

作用:設置根 logger。

要點:只能配置一個;除了 level,不支持任何屬性。level 屬性和 <logger> 中的相同;有一個子元素 <appender-ref>,與 <logger> 中的相同。

完整的 logback.xml 參考示例

在下面的配置文件中,我為自己的項目代碼(根目錄:org.zp.notes.spring)設置了五種等級:

TRACE、DEBUG、INFO、WARN、ERROR,優先級依次從低到高。

因為關注 spring 框架本身的一些信息,我增加了專門打印 spring WARN 及以上等級的日誌。

<?xml version="1.0" encoding="UTF-8" ?>
<!-- logback中一共有5種有效級別,分別是TRACE、DEBUG、INFO、WARN、ERROR,優先級依次從低到高 -->
<configuration scan="true" scanPeriod="60 seconds" debug="false">
<property name="DIR_NAME" value="spring-helloworld"/>
<!-- 將記錄日誌打印到控制檯 -->
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{HH:mm:ss.SSS} [%thread] [%-5p] %c{36}.%M - %m%n</pattern>
</encoder>
</appender>
<!-- RollingFileAppender begin -->
<appender name="ALL" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!-- 根據時間來制定滾動策略 -->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${user.dir}/logs/${DIR_NAME}/all.%d{yyyy-MM-dd}.log</fileNamePattern>
<maxHistory>30</maxHistory>
</rollingPolicy>
<!-- 根據文件大小來制定滾動策略 -->
<triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
<maxFileSize>30MB</maxFileSize>
</triggeringPolicy>
<encoder>
<pattern>%d{HH:mm:ss.SSS} [%thread] [%-5p] %c{36}.%M - %m%n</pattern>
</encoder>
</appender>
<appender name="ERROR" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!-- 根據時間來制定滾動策略 -->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${user.dir}/logs/${DIR_NAME}/error.%d{yyyy-MM-dd}.log</fileNamePattern>
<maxHistory>30</maxHistory>
</rollingPolicy>
<!-- 根據文件大小來制定滾動策略 -->
<triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
<maxFileSize>10MB</maxFileSize>
</triggeringPolicy>
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>ERROR</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
<encoder>
<pattern>%d{HH:mm:ss.SSS} [%thread] [%-5p] %c{36}.%M - %m%n</pattern>
</encoder>
</appender>
<appender name="WARN" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!-- 根據時間來制定滾動策略 -->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${user.dir}/logs/${DIR_NAME}/warn.%d{yyyy-MM-dd}.log</fileNamePattern>
<maxHistory>30</maxHistory>
</rollingPolicy>
<!-- 根據文件大小來制定滾動策略 -->
<triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
<maxFileSize>10MB</maxFileSize>
</triggeringPolicy>
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>WARN</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
<encoder>
<pattern>%d{HH:mm:ss.SSS} [%thread] [%-5p] %c{36}.%M - %m%n</pattern>
</encoder>
</appender>
<appender name="INFO" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!-- 根據時間來制定滾動策略 -->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${user.dir}/logs/${DIR_NAME}/info.%d{yyyy-MM-dd}.log</fileNamePattern>
<maxHistory>30</maxHistory>
</rollingPolicy>
<!-- 根據文件大小來制定滾動策略 -->
<triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
<maxFileSize>10MB</maxFileSize>
</triggeringPolicy>
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>INFO</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
<encoder>
<pattern>%d{HH:mm:ss.SSS} [%thread] [%-5p] %c{36}.%M - %m%n</pattern>
</encoder>
</appender>
<appender name="DEBUG" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!-- 根據時間來制定滾動策略 -->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${user.dir}/logs/${DIR_NAME}/debug.%d{yyyy-MM-dd}.log</fileNamePattern>
<maxHistory>30</maxHistory>
</rollingPolicy>
<!-- 根據文件大小來制定滾動策略 -->
<triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
<maxFileSize>10MB</maxFileSize>
</triggeringPolicy>
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>DEBUG</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
<encoder>
<pattern>%d{HH:mm:ss.SSS} [%thread] [%-5p] %c{36}.%M - %m%n</pattern>
</encoder>
</appender>
<appender name="TRACE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!-- 根據時間來制定滾動策略 -->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${user.dir}/logs/${DIR_NAME}/trace.%d{yyyy-MM-dd}.log</fileNamePattern>
<maxHistory>30</maxHistory>
</rollingPolicy>
<!-- 根據文件大小來制定滾動策略 -->
<triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
<maxFileSize>10MB</maxFileSize>
</triggeringPolicy>
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>TRACE</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
<encoder>
<pattern>%d{HH:mm:ss.SSS} [%thread] [%-5p] %c{36}.%M - %m%n</pattern>
</encoder>
</appender>
<appender name="SPRING" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!-- 根據時間來制定滾動策略 -->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${user.dir}/logs/${DIR_NAME}/springframework.%d{yyyy-MM-dd}.log
</fileNamePattern>
<maxHistory>30</maxHistory>
</rollingPolicy>
<!-- 根據文件大小來制定滾動策略 -->
<triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
<maxFileSize>10MB</maxFileSize>
</triggeringPolicy>
<encoder>
<pattern>%d{HH:mm:ss.SSS} [%thread] [%-5p] %c{36}.%M - %m%n</pattern>
</encoder>
</appender>
<!-- RollingFileAppender end -->
<!-- logger begin -->
<!-- 本項目的日誌記錄,分級打印 -->
<logger name="org.zp.notes.spring" level="TRACE" additivity="false">
<appender-ref ref="STDOUT"/>
<appender-ref ref="ERROR"/>
<appender-ref ref="WARN"/>
<appender-ref ref="INFO"/>
<appender-ref ref="DEBUG"/>
<appender-ref ref="TRACE"/>
</logger>
<!-- SPRING框架日誌 -->
<logger name="org.springframework" level="WARN" additivity="false">
<appender-ref ref="SPRING"/>
</logger>
<root level="TRACE">
<appender-ref ref="ALL"/>
</root>
<!-- logger end -->
</configuration>

log4j 配置

完整的 log4j.xml 參考示例

log4j 的配置文件一般有 xml 格式或 properties 格式。這裡為了和 logback.xml 做個對比,就不介紹 properties 了,其實也沒太大差別。

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
<log4j:configuration xmlns:log4j='http://jakarta.apache.org/log4j/'>
<appender name="STDOUT" class="org.apache.log4j.ConsoleAppender">
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern"
value="%d{yyyy-MM-dd HH:mm:ss,SSS} [%-5p] [%t] %c{36}.%M - %m%n"/>
</layout>
<!--過濾器設置輸出的級別-->
<filter class="org.apache.log4j.varia.LevelRangeFilter">
<param name="levelMin" value="debug"/>
<param name="levelMax" value="fatal"/>
<param name="AcceptOnMatch" value="true"/>
</filter>
</appender>
<appender name="ALL" class="org.apache.log4j.DailyRollingFileAppender">
<param name="File" value="${user.dir}/logs/spring-common/jcl/all"/>
<param name="Append" value="true"/>
<!-- 每天重新生成日誌文件 -->
<param name="DatePattern" value="'-'yyyy-MM-dd'.log'"/>
<!-- 每小時重新生成日誌文件 -->
<!--<param name="DatePattern" value="'-'yyyy-MM-dd-HH'.log'"/>-->
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern"
value="%d{yyyy-MM-dd HH:mm:ss,SSS} [%-5p] [%t] %c{36}.%M - %m%n"/>
</layout>
</appender>
<!-- 指定logger的設置,additivity指示是否遵循缺省的繼承機制-->
<logger name="org.zp.notes.spring" additivity="false">
<level value="error"/>
<appender-ref ref="STDOUT"/>
<appender-ref ref="ALL"/>
</logger>
<!-- 根logger的設置-->
<root>
<level value="warn"/>
<appender-ref ref="STDOUT"/>
</root>
</log4j:configuration>
"

相關推薦

推薦中...