web server apache tomcat11-22-logging 日誌

老马啸西风發表於2024-04-26

前言

整理這個官方翻譯的系列,原因是網上大部分的 tomcat 版本比較舊,此版本為 v11 最新的版本。

開源專案

從零手寫實現 tomcat minicat 別稱【嗅虎】心有猛虎,輕嗅薔薇。

系列文章

web server apache tomcat11-01-官方文件入門介紹

web server apache tomcat11-02-setup 啟動

web server apache tomcat11-03-deploy 如何部署

web server apache tomcat11-04-manager 如何管理?

web server apache tomcat11-06-Host Manager App -- Text Interface

web server apache tomcat11-07-Realm Configuration

web server apache tomcat11-08-JNDI Resources

web server apache tomcat11-09-JNDI Datasource

web server apache tomcat11-10-Class Loader

web server apache tomcat11-11-Jasper 2 JSP Engine

web server apache tomcat11-12-SSL/TLS Configuration

web server apache tomcat11-13-SSI

web server apache tomcat11-14-CGI

...

簡介

Apache Tomcat 的內部日誌記錄使用了 JULI,這是 Apache Commons Logging 的打包重新命名分支,被硬編碼為使用 java.util.logging 框架。這確保了 Tomcat 的內部日誌記錄和任何 Web 應用程式日誌記錄保持獨立,即使一個 Web 應用程式使用了 Apache Commons Logging。

要配置 Tomcat 使用替代的日誌記錄框架進行內部日誌記錄,請按照替代日誌記錄框架提供的指令,重定向使用 java.util.logging 的應用程式的日誌記錄。請注意,替代的日誌記錄框架需要能夠在存在不同類載入器中具有相同名稱的不同記錄器的環境中工作。

在 Apache Tomcat 上執行的 Web 應用程式可以:

  • 使用任何其選擇的日誌記錄框架。
  • 使用系統日誌記錄 API,java.util.logging。
  • 使用由 Java Servlets 規範提供的日誌記錄 API,jakarta.servlet.ServletContext.log(...)

不同 Web 應用程式使用的日誌記錄框架是獨立的。有關更多詳情,請參閱類載入。唯一的例外是 java.util.logging。如果您的日誌記錄庫直接或間接地使用了它,那麼它的某些元素將在 Web 應用程式之間共享,因為它是由系統類載入器載入的。

Java 日誌記錄 API — java.util.logging

Apache Tomcat 具有 java.util.logging API 的幾個關鍵元素的自己的實現。這個實現被稱為 JULI。其中的關鍵元件是一個自定義的 LogManager 實現,它瞭解正在 Tomcat 上執行的不同 Web 應用程式(及其不同的類載入器)。它支援每個應用程式私有的日誌記錄配置。它還在 Web 應用程式從記憶體中解除安裝時受到 Tomcat 的通知,以便清除對其類的引用,防止記憶體洩漏。

當啟動 Java 時,透過提供特定的系統屬性來啟用此 java.util.logging 實現。Apache Tomcat 的啟動指令碼會為您執行此操作,但如果您使用不同的工具來執行 Tomcat(如 jsvc,或在 IDE 中執行 Tomcat),則應自行處理它們。

有關 java.util.logging 的更多詳細資訊可在 JDK 的文件和其 java.util.logging 包的 Javadoc 頁面中找到。

Servlets 日誌記錄 API

對 jakarta.servlet.ServletContext.log(...) 的呼叫來寫日誌訊息是由內部的 Tomcat 日誌記錄處理的。這樣的訊息被記錄到名為

org.apache.catalina.core.ContainerBase.[${engine}].[${host}].[${context}]

的類別中。此日誌記錄根據 Tomcat 日誌記錄配置執行。您無法在 Web 應用程式中覆蓋它。

Servlets 日誌記錄 API 早於現在由 Java 提供的 java.util.logging API。因此,它並不提供太多選項。例如,您無法控制日誌級別。不過,可以注意到,在 Apache Tomcat 實現中,對 ServletContext.log(String) 或 GenericServlet.log(String) 的呼叫是以 INFO 級別記錄的。對 ServletContext.log(String, Throwable) 或 GenericServlet.log(String, Throwable) 的呼叫是以 SEVERE 級別記錄的。

控制檯

在 Unix 上執行 Tomcat 時,控制檯輸出通常會重定向到名為 catalina.out 的檔案中。該名稱可使用環境變數進行配置。(參見啟動指令碼)。所有寫入到 System.err/out 的內容都將被捕獲到該檔案中。這可能包括:

  • 由 java.lang.ThreadGroup.uncaughtException(...) 列印的未捕獲異常
  • 如果透過系統訊號請求了執行緒轉儲,則執行緒轉儲

在 Windows 上作為服務執行時,控制檯輸出也會被捕獲和重定向,但檔名不同。

Apache Tomcat 中的預設日誌記錄配置將相同的

訊息寫入控制檯和日誌檔案。這在開發時使用 Tomcat 時非常方便,但在生產環境中通常是不需要的。

仍在使用 System.out 或 System.err 的舊應用程式可以透過在 Context 上設定 swallowOutput 屬性來欺騙。如果將屬性設定為 true,則在請求處理過程中對 System.out/err 的呼叫將被攔截,並將其輸出透過 jakarta.servlet.ServletContext.log(...) 呼叫饋送到日誌子系統中。
請注意,swallowOutput 功能實際上是一個技巧,它有其侷限性。它僅適用於對 System.out/err 的直接呼叫,並且僅在請求處理週期中起作用。它可能無法攔截由應用程式建立的其他執行緒中的日誌記錄框架。它不能用於攔截那些自己寫入系統流的日誌記錄框架,因為它們早在重定向發生之前就開始,並且可能直接獲得對流的引用。

訪問日誌記錄

訪問日誌記錄是一個相關但不同的功能,它作為一個閥門實現。它使用自包含的邏輯來寫入其日誌檔案。訪問日誌記錄的基本要求是處理大量連續資料而不增加太多開銷,因此它僅使用 Apache Commons Logging 用於自身的除錯訊息。這種實現方法避免了額外的開銷和潛在的複雜配置。有關其配置的更多詳細資訊,請參閱閥門文件,其中包括各種報告格式。

使用 java.util.logging(預設)

JDK 提供的 java.util.logging 的預設實現過於受限,以至於無法使用。關鍵限制是無法實現每個 Web 應用程式的日誌記錄,因為配置是基於 VM 的。因此,在預設配置下,Tomcat 將替換預設的 LogManager 實現為一個容器友好型的實現,稱為 JULI,它解決了這些缺陷。

JULI 支援與標準 JDK java.util.logging 相同的配置機制,使用程式設計方法或屬性檔案。主要區別在於可以設定每個類載入器屬性檔案(這樣就可以輕鬆地重新部署友好的 Web 應用程式配置),並且屬性檔案支援擴充套件結構,允許更自由地定義處理程式並將其分配給記錄器。

JULI 預設啟用,並支援每個類載入器配置,除了常規的全域性 java.util.logging 配置之外。這意味著日誌記錄可以在以下層次進行配置:

  • 全域性配置。通常在 ${catalina.base}/conf/logging.properties 檔案中完成。該檔案由 java.util.logging.config.file 系統屬性指定,該屬性由啟動指令碼設定。如果該檔案不可讀或未配置,則預設使用 JRE 中的 ${java.home}/lib/logging.properties 檔案。
  • 在 Web 應用程式中。該檔案將是 WEB-INF/classes/logging.properties

JRE 中的預設 logging.properties 指定了一個 ConsoleHandler,將日誌路由到 System.err。Apache Tomcat 中預設的 conf/logging.properties 還新增了幾個 AsyncFileHandler,用於寫入檔案。

處理程式的日誌級別閾值預設為 INFO,可以使用 SEVERE、WARNING、INFO、CONFIG、FINE、FINER、FINEST 或 ALL 進行設定。您還可以針對特定的包來收集日誌,並指定一個級別。

要為 Tomcat 的部分內部啟用除錯日誌記錄,應同時配置適當的記錄器和適當的處理程式,以使用 FINEST 或 ALL 級別。例如:

org.apache.catalina.session.level=ALL
java.util.logging.ConsoleHandler.level=ALL

在啟用除錯日誌記錄時,建議將其限制在可能的最窄範圍內,因為除錯日誌記錄可能會產生大量資訊。

JULI 使用的配置與純 java.util.logging 支援的配置

相同,但使用了一些擴充套件,以便更好地靈活地配置記錄器和處理程式。主要的區別包括:

  • 可以向處理程式名稱新增字首,以便可以例項化單個類的多個處理程式。字首是一個以數字開頭、以 '.' 結尾的字串。例如,22foobar. 是一個有效的字首。
  • 對於包含 ${systemPropertyName} 的屬性值,將執行系統屬性替換。
  • 如果使用實現了 org.apache.juli.WebappProperties 介面的類載入器(Tomcat 的 Web 應用程式類載入器會這樣做),則還會對 ${classloader.webappName}、${classloader.hostName} 和 ${classloader.serviceName} 進行屬性替換,分別替換為 Web 應用程式名稱、主機名和服務名稱。
  • 預設情況下,如果日誌記錄器具有關聯處理程式,則日誌記錄器將不委託給其父級。可以使用 loggerName.useParentHandlers 屬性更改每個記錄器的此行為,該屬性接受布林值。
  • 根記錄器可以使用 .handlers 屬性定義其處理程式集。
  • 預設情況下,日誌檔案將在檔案系統上保留 90 天。可以使用 handlerName.maxDays 屬性為每個處理程式更改此設定。如果屬性的指定值 ≤0,則日誌檔案將永遠保留在檔案系統上,否則它們將保留指定的最大天數。
  • 還有一些其他的實現類,可以與 Java 提供的實現類一起使用。值得注意的是 org.apache.juli.FileHandler 和 org.apache.juli.AsyncFileHandler。

org.apache.juli.FileHandler 支援日誌的緩衝。預設情況下,不啟用緩衝。要配置它,請使用處理程式的 bufferSize 屬性。值為 0 使用系統預設緩衝(通常將使用 8K 緩衝)。值 <0 強制在每次日誌寫入時重新整理寫入器。值 >0 使用具有定義值的 BufferedOutputStream,但請注意還會應用系統預設緩衝。

org.apache.juli.AsyncFileHandler 是 FileHandler 的子類,它將日誌訊息排隊並非同步寫入日誌檔案。可以透過設定一些系統屬性來配置其附加行為。

示例 logging.properties 檔案,放置在 $CATALINA_BASE/conf 中:

handlers = 1catalina.org.apache.juli.FileHandler, \
           2localhost.org.apache.juli.FileHandler, \
           3manager.org.apache.juli.FileHandler, \
           java.util.logging.ConsoleHandler

.handlers = 1catalina.org.apache.juli.FileHandler, java.util.logging.ConsoleHandler

############################################################
# Handler specific properties.
# Describes specific configuration info for Handlers.
############################################################

1catalina.org.apache.juli.FileHandler.level = FINE
1catalina.org.apache.juli.FileHandler.directory = ${catalina.base}/logs
1catalina.org.apache.juli.FileHandler.prefix = catalina.
1catalina.org.apache.juli.FileHandler.maxDays = 90
1catalina.org.apache.juli.FileHandler.encoding = UTF-8

2localhost.org.apache.juli.FileHandler.level = FINE
2localhost.org.apache.juli.FileHandler.directory = ${catalina.base}/logs
2localhost.org.apache.juli.FileHandler.prefix = localhost.
2localhost.org.apache.juli.FileHandler.maxDays = 90
2localhost.org.apache.juli.FileHandler.encoding = UTF-8

3manager.org.apache.juli.FileHandler.level = FINE
3manager.org.apache.juli.FileHandler.directory = ${catalina.base}/logs
3manager.org.apache.juli.FileHandler.prefix = manager.
3manager.org.apache.juli.FileHandler.bufferSize = 16384
3manager.org.apache.juli.FileHandler.maxDays = 90
3manager.org.apache.juli.FileHandler.encoding = UTF-8

java.util.logging.ConsoleHandler.level = FINE
java.util.logging.ConsoleHandler.formatter = java.util.logging.OneLineFormatter
java.util.logging.ConsoleHandler.encoding = UTF-8

############################################################
# Facility specific properties.
# Provides extra control for each logger.
############################################################

org.apache.catalina.core.ContainerBase.[Catalina].[localhost].level = INFO
org.apache.catalina.core.ContainerBase.[Catalina].[localhost].handlers = \
   2localhost.org.apache.juli.FileHandler

org.apache.catalina.core.ContainerBase.[Catalina].[localhost].[/manager].level = INFO
org.apache.catalina.core.ContainerBase.[Catalina].[localhost].[/manager].handlers = \
   3manager.org.apache.juli.FileHandler

# For example, set the org.apache.catalina.util.LifecycleBase logger to log
# each component that extends LifecycleBase changing state:
#org.apache.catalina.util.LifecycleBase.level = FINE

Example logging.properties for the servlet-examples web application to be placed in WEB-INF/classes inside the web application:

handlers = org.apache.juli.FileHandler, java.util.logging.ConsoleHandler

############################################################
# Handler specific properties.
# Describes specific configuration info for Handlers.
############################################################

org.apache.juli.FileHandler.level = FINE
org.apache.juli.FileHandler.directory = ${catalina.base}/logs
org.apache.juli.FileHandler.prefix = ${classloader.webappName}.

java.util.logging.ConsoleHandler.level = FINE
java.util.logging.ConsoleHandler.formatter = java.util.logging.OneLineFormatter

文件參考

請查閱以下資源獲取更多資訊:

Apache Tomcat 的 org.apache.juli 包的 Javadoc。

Oracle Java 11 的 java.util.logging 包的 Javadoc。

生產使用考慮事項

您可能需要注意以下事項:

考慮從配置中移除 ConsoleHandler。預設情況下(透過 .handlers 設定),日誌會同時傳送到 FileHandler 和 ConsoleHandler。後者的輸出通常會被捕獲到一個檔案中,比如 catalina.out。因此,您最終會得到相同訊息的兩份複製。
考慮移除那些您不使用的應用程式的 FileHandlers。例如,host-manager 的一個。
處理程式預設使用系統預設編碼來寫入日誌檔案。可以使用 encoding 屬性進行配置。請參閱 Javadoc 獲取詳細資訊。
考慮配置訪問日誌。

參考資料

https://tomcat.apache.org/tomcat-11.0-doc/monitoring.html

相關文章