Spring 中的日誌依賴

工程 | Dave Syer | 2009年12月04日 | ...

本文件將探討 Spring 的選擇以及開發者在構建 Spring 應用程式時可用的日誌記錄選項。這篇文件的釋出恰逢 Spring 3.0 的即將釋出,並非因為我們做了很多改變(儘管我們現在對依賴元資料更加謹慎),而是為了讓您能夠做出明智的決定,瞭解如何在您的應用程式中實現和配置日誌記錄。首先,我們將簡要回顧 Spring 的強制性依賴,然後詳細討論如何設定應用程式以使用一些常見的日誌庫。作為示例,我將展示使用 Maven Central 風格的工件命名約定進行的依賴配置。

Spring 依賴和依賴於 Spring

儘管 Spring 提供了對大量企業和其他外部工具的整合和支援,但它有意將強制性依賴項保持在絕對最小值:您不必為了在簡單用例中使用 Spring 而查詢和下載(即使是自動下載)大量的 jar 庫。對於基本的依賴注入,只有一個強制性的外部依賴,那就是日誌記錄(有關日誌記錄選項的更詳細描述,請參閱下文)。如果您使用 Maven 進行依賴管理,您甚至不需要顯式提供日誌依賴。例如,要建立一個應用程式上下文並使用依賴注入來配置應用程式,您的 Maven 依賴項將如下所示:

<dependencies>
   <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      <version>3.1.2.RELEASE</version>
      <scope>runtime</scope>
   </dependency>
</dependencies>

就是這樣。請注意,範圍可以宣告為 runtime,如果您不需要編譯 Spring API,這通常是基本依賴注入用例的情況。

我們在上面的示例中使用了 Maven Central 命名約定,因此它適用於 Maven Central 或 SpringSource S3 Maven 儲存庫。要使用 S3 Maven 儲存庫(例如,用於里程碑或開發者快照),您需要在 Maven 配置中指定儲存庫位置。對於完整版本:

<repositories>
   <repository>
      <id>com.springsource.repository.maven.release</id>
      <url>http://maven.springframework.org/release/</url>
      <snapshots><enabled>false</enabled></snapshots>
   </repository>
</repositories>

對於里程碑:

<repositories>
   <repository>
      <id>com.springsource.repository.maven.milestone</id>
      <url>http://maven.springframework.org/milestone/</url>
      <snapshots><enabled>false</enabled></snapshots>
   </repository>
</repositories>

對於快照:

<repositories>
   <repository>
      <id>com.springsource.repository.maven.snapshot</id>
      <url>http://maven.springframework.org/snapshot/</url>
      <snapshots><enabled>true</enabled></snapshots>
   </repository>
</repositories>

要使用 SpringSource EBR,您需要為依賴項使用不同的命名約定。名稱通常很容易猜測,例如,在這種情況下是:

<dependencies>
   <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>org.springframework.context</artifactId>
      <version>3.0.0.RELEASE</version>
      <scope>runtime</scope>
   </dependency>
</dependencies>

您還需要顯式宣告儲存庫的位置(僅 URL 重要):

<repositories>
   <repository>
      <id>com.springsource.repository.bundles.release</id>
      <url>http://repository.springsource.com/maven/bundles/release/</url>
   </repository>
</repositories>

如果您手動管理依賴項,上述儲存庫宣告中的 URL 是不可瀏覽的,但是 http://www.springsource.com/repository 上的使用者介面可以用來搜尋和下載依賴項。它還提供了實用的 Maven 和 Ivy 配置程式碼片段,如果您正在使用這些工具,可以複製貼上。

如果您更喜歡使用 Ivy 來管理依賴項,那麼在那裡也有類似的名稱和配置選項(請參閱您的依賴管理系統的文件,或檢視一些示例程式碼 - Spring 本身在構建時使用 Ivy 來管理依賴項)。

日誌記錄

日誌記錄是 Spring 的一個非常重要的依賴項,因為 a) 它是唯一強制性的外部依賴項,b) 每個人都希望看到他們使用的工具的一些輸出,並且 c) Spring 與許多其他工具整合,所有這些工具也都選擇了日誌依賴項。應用程式開發者的一個目標通常是在一箇中心位置為整個應用程式(包括所有外部元件)配置統一的日誌記錄。這比看起來要困難,因為日誌框架的選擇非常多。

Spring 的強制性日誌依賴項是 Jakarta Commons Logging API (JCL)。我們針對 JCL 進行編譯,並且還為擴充套件 Spring Framework 的類公開 JCL Log 物件。所有版本的 Spring 都使用相同的日誌庫對使用者很重要:遷移很容易,因為即使是擴充套件 Spring 的應用程式也保留了向後相容性。我們的做法是讓 Spring 的一個模組顯式依賴於 commons-logging(JCL 的規範實現),然後讓所有其他模組在編譯時依賴於它。例如,如果您正在使用 Maven,並且想知道您在哪裡獲得了 commons-logging 的依賴項,那麼它來自 Spring,特別是來自名為 spring-core 的核心模組。

commons-logging 的好處是您不需要其他任何東西就能使您的應用程式正常工作。它有一個執行時發現演算法,可以在已知的類路徑位置查詢其他日誌框架,並使用它認為合適的框架(或者您可以告訴它您需要哪個)。如果沒有其他可用,您將僅從 JDK(java.util.logging 或簡稱 JUL)獲得相當不錯的日誌。您應該會發現您的 Spring 應用程式在大多數情況下都能開箱即用地工作並愉快地輸出到控制檯,這一點很重要。

不使用 Commons Logging - Spring 框架

不幸的是,commons-logging 最糟糕的地方,也是使其在新工具中不受歡迎的原因,也是其執行時發現演算法。如果我們能夠回到過去,現在重新啟動 Spring 專案,它會使用不同的日誌依賴項。也許第一個選擇將是 Simple Logging Facade for Java (SLF4J)(SLF4J),這也是許多人在應用程式中使用 Spring 時與其他工具一起使用的。

關閉 commons-logging 非常簡單:只需確保在執行時它不在類路徑上。在 Maven terms 中,您需要排除該依賴項,並且由於 Spring 依賴項的宣告方式,您只需要執行一次。

<dependencies>
   <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      <version>3.1.2.RELEASE</version>
      <scope>runtime</scope>
      <exclusions>
         <exclusion>
            <groupId>commons-logging</groupId>
            <artifactId>commons-logging</artifactId>
         </exclusion>
      </exclusions>
   </dependency>
</dependencies>

現在這個應用程式可能已損壞,因為類路徑上沒有 JCL API 的實現,因此必須提供一個新的。在下一節中,我們將以 SLF4J 為例,向您展示如何提供 JCL 的替代實現。

使用 SLF4J

SLF4J 是一個比 commons-logging 更簡潔、執行時效率更高的依賴項,因為它使用編譯時繫結而不是執行時發現它整合的其他日誌框架。這也意味著您必須更明確地說明您希望在執行時發生什麼,並相應地宣告或配置它。SLF4J 提供了許多常見日誌框架的繫結,因此您通常可以選擇您已經使用的框架,並將其繫結以進行配置和管理。

SLF4J 提供了許多常見日誌框架的繫結,包括 JCL,它也反過來:在其他日誌框架和自身之間進行橋接。因此,要將 SLF4J 與 Spring 一起使用,您需要將 commons-logging 依賴項替換為 SLF4J-JCL 橋。一旦完成,Spring 內部的日誌記錄呼叫將被翻譯成對 SLF4J API 的日誌記錄呼叫,因此如果您的應用程式中的其他庫使用該 API,那麼您就有了一個統一的地方來配置和管理日誌記錄。

一個常見的選擇可能是將 Spring 橋接到 SLF4J,然後提供從 SLF4J 到 Log4J 的顯式繫結。您需要提供 4 個依賴項(並排除現有的 commons-logging):橋接器、SLF4J API、到 Log4J 的繫結以及 Log4J 實現本身。在 Maven 中,您會這樣做:

<dependencies>
   <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      <version>3.1.2.RELEASE</version>
      <scope>runtime</scope>
      <exclusions>
         <exclusion>
           <groupId>commons-logging</groupId>
           <artifactId>commons-logging</artifactId>
         </exclusion>
      </exclusions>
   </dependency>
   <dependency>
      <groupId>org.slf4j</groupId>
      <artifactId>jcl-over-slf4j</artifactId>
      <version>1.7.0</version>
      <scope>runtime</scope>
   </dependency>
   <dependency>
      <groupId>org.slf4j</groupId>
      <artifactId>slf4j-api</artifactId>
      <version>1.7.0</version>
      <scope>runtime</scope>
   </dependency>
   <dependency>
      <groupId>org.slf4j</groupId>
      <artifactId>slf4j-log4j12</artifactId>
      <version>1.7.0</version>
      <scope>runtime</scope>
   </dependency>
   <dependency>
      <groupId>log4j</groupId>
      <artifactId>log4j</artifactId>
      <version>1.2.14</version>
      <scope>runtime</scope>
   </dependency>
</dependencies>

僅僅為了獲得一些日誌記錄,這似乎有很多依賴項。是的,但是它是可選的,並且與類載入器問題相比,它應該比普通的 commons-logging 表現更好,尤其是在 OSGi 平臺等嚴格容器中。據說還有效能優勢,因為繫結是在編譯時而不是執行時進行的。

SLF4J 使用者中更常見的選擇是直接繫結到 Logback,這需要更少的步驟並且生成的依賴項更少。這消除了額外的繫結步驟,因為 Logback 直接實現 SLF4J,所以您只需要依賴兩個庫而不是四個(jcl-over-slf4j 和 logback)。如果您這樣做,您可能還需要從其他外部依賴項(不是 Spring)中排除 slf4j-api 依賴項,因為您只需要一個版本的該 API 在類路徑上。

使用 Log4J

許多人使用 Log4j 作為日誌記錄框架來進行配置和管理。它高效且成熟,事實上,它也是我們在構建和測試 Spring 時在執行時使用的。Spring 還提供了一些用於配置和初始化 Log4j 的實用程式,因此在某些模組中它對 Log4j 具有可選的編譯時依賴項。

要使 Log4j 與預設的 JCL 依賴項(commons-logging)一起工作,您所要做的就是將 Log4j 放在類路徑上,併為其提供一個配置檔案(log4j.properties 或 log4j.xml 在類路徑的根目錄)。因此,對於 Maven 使用者,這是您的依賴項宣告:

<dependencies>
   <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      <version>3.1.2.RELEASE</version>
      <scope>runtime</scope>
   </dependency>
   <dependency>
      <groupId>log4j</groupId>
      <artifactId>log4j</artifactId>
      <version>1.2.14</version>
      <scope>runtime</scope>
   </dependency>
</dependencies>

這是一個用於將日誌記錄到控制檯的示例 log4j.properties:

log4j.rootCategory=INFO, stdout

log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE} %5p %t %c{2}:%L - %m%n

log4j.category.org.springframework.beans.factory=DEBUG

具有原生 JCL 的執行時容器

許多人將他們的 Spring 應用程式執行在本身提供 JCL 實現的容器中。IBM Websphere Application Server (WAS) 是一個典型例子。這經常會引起問題,而且不幸的是,沒有一勞永逸的解決方案;在大多數情況下,簡單地從您的應用程式中排除 commons-logging 是不夠的。

明確一點:報告的問題通常不是 JCL 本身,甚至不是 commons-logging:而是將 commons-logging 繫結到另一個框架(通常是 Log4J)。這可能會失敗,因為 commons-logging 在一些容器中找到的舊版本(1.0)和大多數人現在使用的現代版本(1.1)之間更改了執行時發現方式。Spring 不使用 JCL API 的任何不尋常部分,因此那裡沒有破損,但是一旦 Spring 或您的應用程式嘗試進行任何日誌記錄,您就會發現到 Log4J 的繫結不起作用。

在這種情況下,使用 WAS 的最簡單方法是反轉類載入器層次結構(IBM 稱之為“parent last”),以便應用程式控制 JCL 依賴項,而不是容器。這個選項並非總是開放的,但公開領域有許多關於替代方法的建議,您的體驗可能會因容器的確切版本和功能集而異。

(注意:Spring 和 slf4j 的版本已從原始文章更新,以便於複製貼上。)

獲取 Spring 新聞通訊

透過 Spring 新聞通訊保持聯絡

訂閱

領先一步

VMware 提供培訓和認證,助您加速進步。

瞭解更多

獲得支援

Tanzu Spring 提供 OpenJDK™、Spring 和 Apache Tomcat® 的支援和二進位制檔案,只需一份簡單的訂閱。

瞭解更多

即將舉行的活動

檢視 Spring 社群所有即將舉行的活動。

檢視所有