介绍

Apache Tomcat 的内部日志记录使用 JULI,是 Apache Commons Logging 的打包重命名分支, 经过硬编码以使用 java.util.logging 框架。 这可确保 Tomcat 的内部日志记录和任何 Web 应用程序日志记录保持独立, 即使 Web 应用程序使用 Apache Commons Logging 也是如此。

要将 Tomcat 配置为使用备用日志记录框架进行内部日志记录, 请按照备用日志记录框架提供的说明为使用 java.util.logging 的应用程序重定向日志记录。 请记住,替代日志记录框架需要能够在不同类加载器中可能存在同名的不同 Logger 的环境中工作。

在 Apache Tomcat 上运行的 Web 应用程序可以:

  • 使用它选择的任何日志记录框架。

  • 使用系统日志记录 API java.util.logging

  • 使用 Java Servlets 规范提供的日志记录 API jakarta.servlet.ServletContext.log(…​)

不同 Web 应用程序使用的日志记录框架是独立的。 有关更多详细信息,请参阅 class loading。 此规则的例外是 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 页面。

有关 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 级别。

Console

在 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 将在默认配置中将默认的 LogManager 实现替换为名为 JULI 的容器友好实现,从而解决这些缺点。

JULI 支持与标准 JDK ·java.util.logging· 相同的配置机制,使用编程方法或属性文件。 主要区别在于可以设置每个 classloader 的属性文件(这支持轻松的重新部署友好的 webapp 配置), 并且属性文件支持扩展结构,这允许更自由地定义处理程序并将其分配给 Logger。

JULI 默认启用,除了常规的全局 java.util.logging 配置外, 还支持每个 classloader 配置。这意味着可以在以下层配置日志记录:

  • 全局Globally。这通常在 ${catalina.base}/conf/logging.properties 文件中完成。该文件由启动脚本设置的 java.util.logging.config.file System 属性指定。如果它不可读或未配置,则默认使用 JRE 中的 ${java.home}/lib/logging.properties 文件。

  • 在 Web 应用程序中。该文件将为 WEB-INF/classes/logging.properties

JRE 中的默认 logging.properties 指定将日志记录路由到 System.err 的 ConsoleHandler。 Apache Tomcat 中的默认 conf/logging.properties 还添加了几个写入文件的 AsyncFileHandlers

处理程序的日志级别阈值默认为 INFO,可以使用 SEVERE、WARNING、INFO、CONFIG、FINE、FINER、FINEST 或 ALL 进行设置。 还可以将特定软件包作为目标以从中收集日志记录并指定级别。

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

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

启用调试日志记录时,建议在尽可能窄的范围内启用它,因为调试日志记录可能会生成大量信息。

JULI 使用的配置与普通 java.util.logging 支持的配置相同,但使用了一些扩展, 以便在配置 Logger 和处理程序时具有更好的灵活性。主要区别在于:

  • 可以将前缀添加到处理程序名称中,以便可以实例化单个类的多个处理程序。前缀是以数字开头,以 '.' 结尾的 String。例如,22foobar。是有效的前缀。

  • 对包含 ${systemPropertyName} 的属性值执行系统属性替换。

  • 如果使用实现 org.apache.juli.WebappProperties 接口的类加载器(Tomcat 的 Web 应用程序类加载器就是这样),则还会对 ${classloader.webappName}${classloader.hostName}${classloader.serviceName} 执行属性替换,它们分别替换为 Web 应用程序名称、主机名和服务名称。

  • 默认情况下,如果 Logger 具有关联的处理程序,则不会委托给其父级。可以使用 loggerName.useParentHandlers 属性(接受布尔值)按记录器更改此值。

  • 根 Logger 可以使用 .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 的子类,它将日志消息排队并异步写入日志文件。 它的附加行为可以通过设置一些系统属性来配置。

要放置在 $CATALINA_BASE/conf 中的 logging.properties 文件示例:

handlers = 1catalina.org.apache.juli.AsyncFileHandler, \
           2localhost.org.apache.juli.AsyncFileHandler, \
           3manager.org.apache.juli.AsyncFileHandler, \

.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 应用程序放置在 Web 应用程序内的 WEB-INF/classes 中的示例 logging.properties:

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

文档参考

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

  • org.apache.juli 软件包的 Apache Tomcat Javadoc。

  • java.util.logging 软件包的 Oracle Java 11 Javadoc。

生产使用注意事项

可能需要注意以下事项:

  • 考虑从配置中删除 ConsoleHandler。默认情况下(多亏了 .handlers 设置),日志记录同时转到 AsyncFileHandlerConsoleHandler。后者的输出通常被捕获到一个文件中,例如 catalina.out。因此,最终会得到相同消息的两个副本。

  • 请考虑删除不使用的应用程序的 AsyncFileHandlers。例如,host-manager 的那个。

  • 考虑配置 Access log。