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 Servlet 规范提供的日志记录 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 的更多详细信息,请参见下文。

Servlet 日志记录 API

jakarta.servlet.ServletContext.log(...) 的调用以写入日志消息由内部 Tomcat 日志记录处理。此类消息将记录到名为

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

此日志记录是根据 Tomcat 日志记录配置执行的。您不能在 Web 应用程序中覆盖它。

Servlet 日志记录 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 来记录自己的调试消息。这种实现方法避免了额外的开销和潜在的复杂配置。有关其配置的更多详细信息,包括各种报告格式,请参阅Valves文档。

使用 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.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

servlet-examples web 应用程序的示例 logging.properties 文件,放置在 web 应用程序的 WEB-INF/classes 目录下

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

文档参考

有关更多信息,请参阅以下资源

生产环境使用注意事项

您可能需要注意以下几点

  • 考虑从配置中删除 ConsoleHandler。默认情况下(由于 .handlers 设置),日志记录会同时发送到 FileHandlerConsoleHandler。后者的输出通常会被捕获到一个文件中,例如 catalina.out。因此,您最终会得到两份相同消息的副本。
  • 考虑删除您不使用的应用程序的 FileHandler。例如,host-managerFileHandler
  • 默认情况下,处理程序使用系统默认编码来写入日志文件。可以使用 encoding 属性进行配置。有关详细信息,请参阅 Javadoc。
  • 考虑配置一个 访问日志