Tomcat 中的日志记录

目录

简介

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.util.logging 实现通过在启动 Java 时提供某些系统属性来启用。Apache Tomcat 启动脚本会为您完成此操作,但如果您使用不同的工具运行 Tomcat(例如 jsvc,或在 IDE 中运行 Tomcat),则应自行处理。

有关 java.util.logging 的更多详细信息,请参见您的 JDK 文档及其 java.util.logging 包的 Javadoc 页面。

有关 Tomcat JULI 的更多详细信息,请参见下文。

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.outSystem.err 的旧应用程序可以通过在 Context 上设置 swallowOutput 属性来处理。如果该属性设置为 true,则在请求处理期间对 System.out/err 的调用将被拦截,它们的输出将通过 jakarta.servlet.ServletContext.log(...) 调用馈送到日志子系统。
注意swallowOutput 功能实际上是一种技巧,它有其局限性。它仅适用于直接调用 System.out/err,并且仅在请求处理周期内有效。它可能不适用于应用程序可能创建的其他线程。它不能用于拦截那些本身就写入系统流的日志框架,因为这些框架启动较早,并且可能在重定向发生之前获取对流的直接引用。

访问日志记录

访问日志记录是一个相关但不同的功能,它作为 Valve 实现。它使用自包含的逻辑来写入其日志文件。访问日志记录的基本要求是以低开销处理大量连续数据流,因此它仅将 Apache Commons Logging 用于其自身的调试消息。这种实现方法避免了额外的开销和潜在的复杂配置。有关其配置的更多详细信息,包括各种报告格式,请参阅阀门文档。

使用 java.util.logging(默认)

JDK 中提供的 java.util.logging 默认实现过于有限,无法实用。主要限制是无法实现每个 Web 应用程序的日志记录,因为配置是基于每个 VM 的。因此,Tomcat 在默认配置下,将用一个名为 JULI 的容器友好型实现替换默认的 LogManager 实现,以解决这些缺点。

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,可以使用 SEVEREWARNINGINFOCONFIGFINEFINERFINESTALL 进行设置。您还可以针对特定包收集日志并指定一个级别。

要启用 Tomcat 内部一部分的调试日志记录,您应该配置相应的日志记录器和处理程序,以使用 FINESTALL 级别。例如:

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.FileHandlerorg.apache.juli.AsyncFileHandler

org.apache.juli.FileHandler 支持日志缓冲。默认情况下不启用缓冲。要配置它,请使用处理程序的 bufferSize 属性。值为 0 时使用系统默认缓冲(通常会使用 8K 缓冲区)。值为 <0 时强制每次日志写入时刷新写入器。值为 >0 时使用带有定义值的 BufferedOutputStream,但请注意,系统默认缓冲也将被应用。

org.apache.juli.AsyncFileHandlerFileHandler 的一个子类,它将日志消息排队并异步写入日志文件。其额外行为可以通过设置一些系统属性进行配置。

示例 logging.properties 文件,放置在 $CATALINA_BASE/conf 中

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

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

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

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

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

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

java.util.logging.ConsoleHandler.level = ALL
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.AsyncFileHandler

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

# 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

用于 servlet-examples Web 应用程序的 logging.properties 示例,放置在 Web 应用程序内部的 WEB-INF/classes 中

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

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

org.apache.juli.AsyncFileHandler.level = ALL
org.apache.juli.AsyncFileHandler.directory = ${catalina.base}/logs
org.apache.juli.AsyncFileHandler.prefix = ${classloader.webappName}.
org.apache.juli.AsyncFileHandler.encoding = UTF-8

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

文档参考

有关其他信息,请参阅以下资源

生产环境使用注意事项

您可能需要注意以下事项

  • 考虑从配置中移除 ConsoleHandler。默认情况下(由于 .handlers 设置),日志记录同时发送到 AsyncFileHandlerConsoleHandler。后者的输出通常会被捕获到文件中,例如 catalina.out。因此,您会得到相同消息的两份副本。
  • 考虑移除您不使用的应用程序的 AsyncFileHandler。例如,用于 host-manager 的。
  • 考虑配置访问日志

使用 Log4j