Tomcat 日志機(jī)制

2022-03-03 14:07 更新

簡(jiǎn)介

Tomcat 的內(nèi)部日志使用 JULI 組件,這是一個(gè) Apache Commons 日志的重命名的打包分支,默認(rèn)被硬編碼,使用 java.util.logging 架構(gòu)。這能保證 Tomcat 內(nèi)部日志與 Web 應(yīng)用的日志保持獨(dú)立,即使 Web 應(yīng)用使用的是 Apache Commons Logging。

假如想用另外的日志框架來替換 Tomcat 的內(nèi)部日志系統(tǒng),那么就必須采用一種能夠保持完整的 Commons 日志機(jī)制的 JULI 實(shí)現(xiàn),用它來替換通過硬編碼使用 java.util.logging 的 JULI 實(shí)現(xiàn)。通常這種替代實(shí)現(xiàn)都是以額外組件的形式出現(xiàn)的。利用 Log4j 框架用于 Tomcat 內(nèi)部日志的配置如下文所示。

在 Apache Tomcat 上運(yùn)行的 Web 應(yīng)用可以使用:

  • 任何自選的日志框架。
  • 系統(tǒng)日志 API,java.util.logging。
  • Java Servlets 規(guī)范所提供的日志 API,javax.servlet.ServletContext.log(...)。

各個(gè)應(yīng)用可以使用不同的日志框架,詳情參見類加載器java.util.logging 則是例外。如果日志庫(kù)直接或間接地用到了這一 API,那么 Web 應(yīng)用就能共享使用它的元素,因?yàn)樵?API 是由系統(tǒng)類加載器所加載的。

Java 日志 API——java.util.logging

Apache Tomcat 本身已經(jīng)實(shí)現(xiàn)了 java.util.logging API 的幾個(gè)關(guān)鍵元素。這種實(shí)現(xiàn)就是 JULI。其中的關(guān)鍵組件是一個(gè)自定義的 LogManager 實(shí)現(xiàn),它能分辨運(yùn)行在 Tomcat 上的不同 Web 應(yīng)用(以及它們所用的不同的類加載器),還能針對(duì)每一應(yīng)用進(jìn)行私有的日志配置。另外,當(dāng) Web 應(yīng)用沒能從內(nèi)存中加載時(shí),Tomcat 會(huì)給予它相應(yīng)通知,從而清除相應(yīng)的引用類,防止內(nèi)存泄露。

在啟動(dòng) Java 時(shí),通過提供特定的系統(tǒng)屬性,可以啟用 java.util.logging 實(shí)現(xiàn)。Apache Tomcat 啟動(dòng)腳本可以實(shí)現(xiàn)這個(gè)操作,但如果使用不同工具來運(yùn)行 Tomcat(比如 jsvc,或者從某個(gè) IDE 中運(yùn)行 Tomcat),就必須自己來啟用實(shí)現(xiàn)。

關(guān)于 java.util.logging 實(shí)現(xiàn)的詳細(xì)情況可以查閱 JDK 文檔,具體位于 java.util.logging 包的相關(guān) javadoc 頁(yè)面中。

關(guān)于 Tomcat JULI 的詳細(xì)介紹見下文。

Servlets logging API

Tomcat 內(nèi)部日志能夠處理對(duì) javax.servlet.ServletContext.log(...) 的調(diào)用,從而寫入日志消息。這種消息都被記錄到一種特定類別中,命名方式如下:

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

這種日志是依照 Tomcat 日志配置而執(zhí)行的,無法在 Web 應(yīng)用中重寫。

Servlets logging API 的問世要先于 Java 所提供的 java.util.logging API,所以,它無法提供太多的選項(xiàng),比如無法用它來控制日志級(jí)別。然而需要注意的是,在 Tomcat 實(shí)現(xiàn)中, 對(duì) ServletContext.log(String)GenericServlet.log(String) 的調(diào)用都被記錄在 INFO 級(jí)別。對(duì) ServletContext.log(String, Throwable)GenericServlet.log(String, Throwable) 的調(diào)用都被記錄在 ERROR 級(jí)別。

Console

在 UNIX 系統(tǒng)下運(yùn)行 Tomcat 時(shí),控制臺(tái)輸出經(jīng)常會(huì)重定向到 catalina.out 的文件中。通過一個(gè)環(huán)境變量,可以配置該文件(參見啟動(dòng)腳本)。

寫入 System.err/out 的任何內(nèi)容都會(huì)被 catalina.out 文件所捕獲。這些內(nèi)容可能包括:

  • java.lang.ThreadGroup.uncaughtException(..) 所輸出的未捕獲異常。
  • 線程轉(zhuǎn)儲(chǔ),如果通過系統(tǒng)信號(hào)來請(qǐng)求它們。

在 Windows 上以服務(wù)形式運(yùn)行時(shí),控制臺(tái)輸出也會(huì)被捕獲及重定向,但文件名有所不同。

Tomcat 默認(rèn)的日志配置會(huì)將同樣的消息寫入控制臺(tái)和一個(gè)日志文件中。這一特點(diǎn)非常有利于使用 Tomcat 進(jìn)行開發(fā),但往往并不適用于生產(chǎn)環(huán)境。

老的應(yīng)用可能還在使用 System.outSystem.err,可以通過在 Context 元素上設(shè)置 swallowOutput 屬性來調(diào)整。如該屬性設(shè)為 true,那么在請(qǐng)求階段對(duì) System.out/err 的調(diào)用就會(huì)被攔截,它們的輸出也會(huì)通過 javax.servlet.ServletContext.log(...) 調(diào)用反饋給日志系統(tǒng)。

注意swallowOutput 雖然是一個(gè)小技巧,但還是有局限性的:它需要直接調(diào)用 System.out/err,并且要在請(qǐng)求處理周期內(nèi)完成。而且,它可能還并不適用于應(yīng)用所創(chuàng)建的其他線程。不能將其用于攔截本身寫入系統(tǒng)流的日志框架(它們可能早先已經(jīng)啟動(dòng),并且在重定向發(fā)生前就已經(jīng)獲取了對(duì)流的直接引用)。

Acces 日志

Access 日志功能相近,但還是有所不同。它是一個(gè) Valve,使用自包含的邏輯來編寫日志文件。訪問日志的基本需求是以較低開銷處理大型連續(xù)數(shù)據(jù)流,所以只能使用 Commomns Logging 來處理自身的調(diào)試消息。這種實(shí)現(xiàn)方法避免了額外的開銷,并且可能具有較復(fù)雜的配置。請(qǐng)參考 Valves 文檔了解更多配置詳情,其中包含了各種報(bào)告格式。

使用 java.util.logging(默認(rèn))

JDK 所提供的默認(rèn) java.util.logging 實(shí)現(xiàn)功能太過局限,所以根本沒有什么使用價(jià)值。其關(guān)鍵局限在于不能實(shí)現(xiàn)針對(duì)每一應(yīng)用進(jìn)行日志記錄,因?yàn)榕渲檬轻槍?duì)每一 VM 的。所以按照默認(rèn)配置,Tomcat 會(huì)用 JULI 這種非常適用于容器的實(shí)現(xiàn)來代替默認(rèn)的 LogManager 實(shí)現(xiàn),從而避免了 LogManager 的缺點(diǎn)。

跟標(biāo)準(zhǔn) JDK 的 java.util.logging 一樣,JULI 也支持同樣的配置機(jī)制,或者使用編程方式,或者指定屬性值。它與 java.util.logging 的不同在于,它可以分別設(shè)置每一個(gè)類加載器屬性文件(能夠啟用簡(jiǎn)單的、便于重新部署的應(yīng)用配置),屬性文件還支持?jǐn)U展構(gòu)造,能夠更加自由地定義 handle 并將其指定給 logger。

JULI 是默認(rèn)啟用的,除了普通的全局 java.util.logging 配置之外,它支持每個(gè)類加載器配置。這意味著可以在下列層級(jí)來配置日志:

  • 全局范圍。${catalina.base}/conf/logging.properties 文件。該文件通過由啟動(dòng)腳本設(shè)置的系統(tǒng)屬性 java.util.logging.config.file 來指定。如果它不可讀或沒有配置,默認(rèn)采用 JRE 中的 ${java.home}/lib/logging.properties 文件。
  • 在 Web 應(yīng)用范圍內(nèi)。該文件為 WEB-INF/classes/logging.properties。

JRE 中默認(rèn)的 logging.properties 指定了 ConsoleHandler,用于將日志輸出至 System.err。Tomcat 中默認(rèn)的 conf/logging.properties 也添加了幾個(gè)能夠?qū)懭胛募?FileHandlers。

handler 的日志級(jí)別容差值默認(rèn)為 INFO,取值范圍為:SEVERE、WARNING、INFO、CONFIG、FINE、FINER、FINEST 或 ALL。你也可以從特殊的包中收集日志,然后為這種日志指定相應(yīng)的級(jí)別。

為了啟用 部分 Tomcat 內(nèi)部的調(diào)試日志功能,應(yīng)該配置適合的 logger 和 handle 來使用 FINESTALL 級(jí)別。比如:

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

當(dāng)啟用調(diào)試日志功能時(shí),建議將范圍盡量縮小,因?yàn)樵摴δ軙?huì)產(chǎn)生大量信息。

JULI 所使用的配置與純 java.util.logging 所支持的配置基本相同,只不過使用了一些擴(kuò)展,以便更靈活地配置 logger 和 handler。主要的差別在于:

  • handler 名稱前可以加上前綴,所以同一類可以實(shí)例化出多個(gè) handler。前綴是一個(gè)以數(shù)字開頭的字符串,并以 . 結(jié)尾。比如 22foobar. 就是個(gè)有效的前綴。
  • 系統(tǒng)屬性

還有一些額外的實(shí)現(xiàn)類,它們可以與 Java 所提供的類一起使用。在這些類中,最著名的就是 org.apache.juli.FileHandler。

org.apache.juli.FileHandler 支持日志緩存。日志緩存默認(rèn)是沒有啟用的。使用 handler 的 bufferSize 屬性可以配置它:屬性值為 0 時(shí),代表使用系統(tǒng)默認(rèn)的緩存(通常使用 8k 緩存);屬性值小于 0 時(shí),將在每個(gè)日志寫入上強(qiáng)制使用 writer flush(將緩存區(qū)中的數(shù)據(jù)強(qiáng)制寫出到系統(tǒng)輸出)功能;屬性值大于 0 時(shí),則使用帶有定義值的 BufferedOutputStream 類——但要注意的是,這也將應(yīng)用于系統(tǒng)默認(rèn)的緩存。

以下是一個(gè) $CATALINA_BASE/conf 中的 logging.properties 文件:

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.

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

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

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

############################################################
# 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

下例是一個(gè)用于 servlet-examples 應(yīng)用的 WEB-INF/classes 中的 logging.properties 文件:

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.SimpleFormatter

1. 文檔引用

查看下列資源獲取額外的詳細(xì)信息:

2. 生產(chǎn)環(huán)境使用中的注意事項(xiàng)

可能需要注意以下方面:

  • ConsoleHandler 從配置中移除。默認(rèn)(多謝 .handlers 設(shè)置)日志會(huì)使用 FileHandlerConsoleHandler。后者的輸出經(jīng)常會(huì)被捕獲到一個(gè)文件中,比如 catalina.out。從而導(dǎo)致同一消息可能生成了兩個(gè)副本。
  • 對(duì)于不使用的應(yīng)用(比如 host-manager),可以考慮將 FileHandlers 移除。
  • handler 默認(rèn)使用系統(tǒng)缺省編碼來寫入日志文件,通過 encoding 屬性可以修改設(shè)置,詳情查看相關(guān)的 javadoc 文檔。
  • 配置 Access log 。

使用 Log4j

前面介紹了用于 Tomcat 內(nèi)部日志的 java.util.logging,接下來本部分內(nèi)容介紹如何通過配置 Tomcat 使用 log4j。

注意:當(dāng)你想重新配置 Tomcat 以便利用 log4j 來進(jìn)行自身日志記錄時(shí),下面的步驟都是必需的;而當(dāng)你只是想在自己的 Web 應(yīng)用上使用 log4j 時(shí),這些步驟則不是必需的。在后一種情況下,只需將 log4j.jarlog4j.properties 放到 Web 應(yīng)用的 WEB-INF/libWEB-INF/classes 中即可。

通過下列步驟可配置 log4j 輸出 Tomcat 的內(nèi)部日志:

1.創(chuàng)建一個(gè)包含下列配置的 log4j.properties 文件,將其保存到 $CATALINA_BASE/lib

    log4j.rootLogger = INFO, CATALINA

    # Define all the appenders
    log4j.appender.CATALINA = org.apache.log4j.DailyRollingFileAppender
    log4j.appender.CATALINA.File = ${catalina.base}/logs/catalina
    log4j.appender.CATALINA.Append = true
    log4j.appender.CATALINA.Encoding = UTF-8
    # Roll-over the log once per day
    log4j.appender.CATALINA.DatePattern = '.'yyyy-MM-dd'.log'
    log4j.appender.CATALINA.layout = org.apache.log4j.PatternLayout
    log4j.appender.CATALINA.layout.ConversionPattern = %d [%t] %-5p %c- %m%n

    log4j.appender.LOCALHOST = org.apache.log4j.DailyRollingFileAppender
    log4j.appender.LOCALHOST.File = ${catalina.base}/logs/localhost
    log4j.appender.LOCALHOST.Append = true
    log4j.appender.LOCALHOST.Encoding = UTF-8
    log4j.appender.LOCALHOST.DatePattern = '.'yyyy-MM-dd'.log'
    log4j.appender.LOCALHOST.layout = org.apache.log4j.PatternLayout
    log4j.appender.LOCALHOST.layout.ConversionPattern = %d [%t] %-5p %c- %m%n

    log4j.appender.MANAGER = org.apache.log4j.DailyRollingFileAppender
    log4j.appender.MANAGER.File = ${catalina.base}/logs/manager
    log4j.appender.MANAGER.Append = true
    log4j.appender.MANAGER.Encoding = UTF-8
    log4j.appender.MANAGER.DatePattern = '.'yyyy-MM-dd'.log'
    log4j.appender.MANAGER.layout = org.apache.log4j.PatternLayout
    log4j.appender.MANAGER.layout.ConversionPattern = %d [%t] %-5p %c- %m%n

    log4j.appender.HOST-MANAGER = org.apache.log4j.DailyRollingFileAppender
    log4j.appender.HOST-MANAGER.File = ${catalina.base}/logs/host-manager
    log4j.appender.HOST-MANAGER.Append = true
    log4j.appender.HOST-MANAGER.Encoding = UTF-8
    log4j.appender.HOST-MANAGER.DatePattern = '.'yyyy-MM-dd'.log'
    log4j.appender.HOST-MANAGER.layout = org.apache.log4j.PatternLayout
    log4j.appender.HOST-MANAGER.layout.ConversionPattern = %d [%t] %-5p %c- %m%n

    log4j.appender.CONSOLE = org.apache.log4j.ConsoleAppender
    log4j.appender.CONSOLE.Encoding = UTF-8
    log4j.appender.CONSOLE.layout = org.apache.log4j.PatternLayout
    log4j.appender.CONSOLE.layout.ConversionPattern = %d [%t] %-5p %c- %m%n

    # Configure which loggers log to which appenders
    log4j.logger.org.apache.catalina.core.ContainerBase.[Catalina].[localhost] = INFO, LOCALHOST
    log4j.logger.org.apache.catalina.core.ContainerBase.[Catalina].[localhost].[/manager] =\
  INFO, MANAGER
    log4j.logger.org.apache.catalina.core.ContainerBase.[Catalina].[localhost].[/host-manager] =\
  INFO, HOST-MANAGER

2.下載 log4j(Tomcat 需要 1.2.x 版本)。

3.下載或構(gòu)建 tomcat-juli.jartomcat-juli-adapters.jar,以便作為 Tomcat 的額外組件使用。詳情參考 Additional Components documentation。

`tomcat-juli.jar` 跟默認(rèn)的版本不同。它包含所有的 Commons Logging 實(shí)現(xiàn),從而能夠發(fā)現(xiàn) log4j 并配置自身。  

4.如果希望全局性地使用 log4j,則如下配置 Tomcat:

  • log4j.jartomcat-juli-adapters.jar 從 extras 中放入 $CATALINA_HOME/lib 中。
  • 用 extras 中的 tomcat-juli.jar 替換 $CATALINA_HOME/bin/tomcat-juli.jar。

5.如果是利用獨(dú)立的 $CATALINA_HOME$CATALINA_BASE 來運(yùn)行 Tomcat,并想在一個(gè) $CATALINA_BASE 中配置使用 log4j,則需要:

  • 創(chuàng)建 $CATALINA_BASE/bin$CATALINA_BASE/lib 目錄——如果它們不存在的話。
  • 將 extras 中的 log4j.jartomcat-juli-adapters.jar 從 extras 放入 $CATALINA_BASE/lib 中。
  • 將 extras 中的 tomcat-juli.jar 轉(zhuǎn)換成 $CATALINA_BASE/bin/tomcat-juli.jar。
  • 如果使用安全管理器運(yùn)行,則需要編輯 $CATALINA_BASE/conf/catalina.policy 文件來修改它,以便使用不同版本的 tomcat-juli.jar。

    注意:其中的工作原理在于:優(yōu)先將庫(kù)加載到 $CATALINA_HOME 中同樣的庫(kù)中。

    注意tomcat-juli.jar 之所以從 $CATALINA_BASE/bin 加載(而不是從 $CATALINA_BASE/lib 加載),是因?yàn)樗怯米饕龑?dǎo)進(jìn)程的,而引導(dǎo)類都是從 bin 加載的。

6.刪除 $CATALINA_BASE/conf/logging.properties,以防止 java.util.logging 生成零長(zhǎng)度的日志文件。

7.啟動(dòng) Tomcat。

log4j 配置沿用了默認(rèn)的 java.util.logging 設(shè)置:管理器與主機(jī)管理器應(yīng)用各自獲得了獨(dú)立的日志文件,而所有其余內(nèi)容都發(fā)送到 catalina.log 日志文件中。

你可以(也應(yīng)該)更加挑剔地選擇日志所包括的包。Tomcat 使用 Engine 和 Host 名稱來定義 logger。比如,要想得到更詳細(xì)的 Catalina localhost log,可以將它放在 log4j.properties 屬性中。注意,在 log4j 基于 XML 的配置文件的命名慣例上,目前存在一些問題,所以建議使用所前所述的屬性文件,直到未來版本的 log4j 允許使用這種慣例。

log4j.logger.org.apache.catalina.core.ContainerBase.[Catalina].[localhost]=DEBUG
log4j.logger.org.apache.catalina.core=DEBUG
log4j.logger.org.apache.catalina.session=DEBUG

警告:設(shè)定為 DEBUG 級(jí)別,會(huì)產(chǎn)生數(shù)以兆計(jì)的日志,從而拖慢 Tomcat 的啟動(dòng)。只有當(dāng)需要調(diào)試 Tomcat 內(nèi)部操作,才應(yīng)該使用這一級(jí)別。

你的 Web 應(yīng)用當(dāng)然應(yīng)該使用各自的 log4j 配置。上面的配置是有效的。你可以將相似的 log4j.properties 文件放到你的 Web 應(yīng)用的 WEB-INF/classes 目錄中,將 log4jx.y.z.jar 放入 WEB-INF/lib 中。 然后指定包級(jí)別日志。這是基本的 log4j 配置方法,不需要 Commons-Logging。更多選項(xiàng)可參考 log4j 文檔,該頁(yè)面只是一種引導(dǎo)指南。

額外注意

  • 通過 Commons 類加載器將 log4j 庫(kù)暴露給 Web 應(yīng)用。詳見類加載器文檔。
    正是由于這一點(diǎn),使用 [Apache Commons Logging] 庫(kù)的 Web 應(yīng)用和庫(kù)有可能自動(dòng)會(huì)將 log4j 選為底層日志實(shí)現(xiàn)。
  • java.util.logging API 仍適用于直接使用它的 Web 應(yīng)用。${catalina.base}/conf/logging.properties 文件仍然可被 Tomcat 啟動(dòng)腳本所引用。詳情可查看本頁(yè)的簡(jiǎn)介部分

    如前面相關(guān)步驟所述,刪除了 ${catalina.base}/conf/logging.properties 文件,會(huì)導(dǎo)致 java.util.logging 回退到 JRE 默認(rèn)的配置,從而使用 ConsoleHandler,然而卻不創(chuàng)建任何標(biāo)準(zhǔn)日志文件。所以必須確保:在禁止標(biāo)準(zhǔn)機(jī)制之前,所有的日志文件必須是由 log4j 創(chuàng)建的。

  • Access Log Valve ExtendedAccessLogValve 使用它們自包含的日志實(shí)現(xiàn),所以無法配置使用 log4j,詳情參看 Valves。
以上內(nèi)容是否對(duì)您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)