第 8 章:上下文 selectors

第 9 章:上下文 selectors

**这不是知识,而是学习的行为,不是占有的行为,而是到达那里的行为,它赋予最大的享受。当我弄清并用尽了一个主题之后,我便转身离开,以再次陷入黑暗。永不满足的人如果完成了一个结构,就会感到很奇怪,那不是为了和平地居住在其中,而是为了开始另一个。我想世界征服者一定会感到这样,在几乎征服了一个王国之后,谁会为其他国家伸手.

卡尔·弗里德里希·高斯(KARL FRIEDRICH GAUSS),写给 Bolyai 的信,1808 年。

*风格,如纯粹的丝绸,往往会掩盖湿疹。

-阿尔伯特·卡缪斯,秋天**

问题:记录分隔

本章涉及一个相对困难的问题,即为在同一 Web 或 EJB 容器上运行的多个应用程序提供单独的日志记录环境。在本章的其余部分,术语“应用程序”将用于互换地指代 Web 应用程序或 J2EE 应用程序。在单独的日志记录环境中,每个应用程序可以以不同的方式配置回传,以使一个应用程序的设置不会干扰另一个应用程序的设置。此问题的一个变体是将应用程序日志记录与容器本身的日志记录分离。

简单易用的方法

假设您的容器支持子类的第一级加载,则可以通过在每个应用程序中嵌入 slf4j 和 logback jar 文件的副本来完成日志记录的分离。对于 Web 应用程序,将 slf4j 和 logback jar 文件放在 Web 应用程序的* WEB-INF/lib *目录下足以为每个 Web 应用程序提供单独的日志记录环境。

Logback 提供了一种用于处理多个上下文的机制,而不会损坏数据,也不会在 Logger 上下文实例之间发生冲突。

我们知道的一件事是 JNDI 环境是独立的。因此,在每个应用程序中设置环境变量将允许给定的组件知道当前正在处理哪个应用程序。基本上,这是一种使用 logback 轻松访问正确的LoggerContext实例的机制。

Management 不同上下文的组件是ContextSelector实现。特定于 JNDI 的实现称为ContextJNDISelector

每个 Web 应用程序都提供两个环境变量。一个用于指定应用程序的LoggerContext名称,另一个用于提供将用于配置上下文的 xml 文件的路径。

服务器端

Configuring Tomcat

首先,将 logback jar(即 logback-classic- * VERSION * .jar,logback-core- * VERSION * .jar 和 slf4j-api- * VERSION * .jar)放置在服务器的共享类目录中。在 Tomcat 中,此目录为* TOMCAT_HOME/common/lib/*。

下一步是让 logback 知道必须使用 JNDI 来 Management 上下文实例。这要归功于系统属性。启动 Tomcat 时,请确保将* logback.ContextSelector 属性设置为 JNDI 值。这可以通过编辑 TOMCAT_HOME/bin/catalina.sh TOMCAT_HOME/bin/catalina.bat *文件并将以下行添加到 java 选项来完成:

-Dlogback.ContextSelector=JNDI

Configuring Jetty

配置 Jetty 首先需要启用 JNDI。这并不重要,因为 Jetty 发行版提供了完成此任务所需的配置文件。唯一要做的是使用以下命令启动 Jetty:

java -jar start.jar etc/jetty.xml etc/jetty-plus.xml

请注意,您需要将应用程序安装在* JETTY_HOME/webapps-plus *目录中。

在 Jetty 中,服务器共享类目录为* JETTY_HOME/lib/*。这是您需要放置 logback jar 的位置(即 logback-classic- * VERSION * .jar,logback-core- * VERSION * .jar 和 slf4j-api- * VERSION * .jar)。

下一步是让 logback 知道必须使用 JNDI 来 Management 上下文实例。这要归功于系统属性。在 Jetty 中,添加环境变量是通过将以下 xml 元素添加到嵌套在* Configuration 元素中的 JETTY_HOME/etc/jetty.xml *配置文件中来完成的:

\ <Arg> logback.ContextSelector</Arg><Arg> JNDI</Arg></Call>

请注意,启动服务器时,在 Java 命令中添加* -Dlogback.ContextSelector = JNDI *将不起作用。这样,当只有 Web 应用程序应尝试以此方式检索其LoggerContext时,由服务器实例化的LoggerFactory将尝试使用 JNDI。

配置每个 Web 应用程序

尽管每个 Web 应用程序都需要编译 logback jar,但除非您正在使用 Jetty,否则也不必将它们放置在 Web 应用程序的 WAR 文件中。

这是由于Jetty 的内部 Classloading 机制造成的。因此,运行 Jetty 时,也应将* logback-classic-VERSION.jar slf4j-api-VERSION.jar 文件放置在 Web 应用程序的 WEB-INF/lib/*目录中。

在每个 Web 应用程序的* web.xml *文件中,需要两个 JNDI 环境条目。第一个指定应用程序的LoggerContext的所需名称。它采用以下形式:

<env-entry>
  <description>JNDI logging context for this app</description>
  <env-entry-name>logback/context-name</env-entry-name>
  <env-entry-type>java.lang.String</env-entry-type>
  <env-entry-value>ContextApp-A</env-entry-value>
</env-entry>

第二个 JNDI 条目将导致重新logback到应用程序自己的 xml 配置文件。可以如下声明:

\ <description>用于配置logback上下文的 URL</description><env-entry-name> logback/configuration-resource</env-entry-name><env-entry-type> java.lang.String</env-entry-type><env-entry-value> logback-app-A.xml</env-entry-value></env-entry>

仅指定文件名将导致 logback 在 Web 应用程序的* WEB-INF/classes/*目录中进行搜索。

回收或关闭 Web 应用程序时,回收关联的LoggerContext通常非常有用。这可以通过安装ServletContextListener来完成,该ServletContextListener会将上下文与ContextSelector分离并关闭它。

logback 附带的ContextDetachingSCL类正是这样做的。要使用它,请将以下行添加到 Web 应用程序的* web.xml *文件中。

\ <listener-class> ch.qos.logback.classic.selector.servlet.ContextDetachingSCL</listener-class></listener>

使用ContextJNDISelector可能会使您的应用程序变慢,因为每次需要LoggerContext时都会发出 JNDI 调用。为避免此调用的开销,logback 附带了一个LoggerContextFilter组件。此过滤器是javax.servlet.Filter实现,可获取特定于环境的LoggerContext并将其设置为ThreadLocal变量。每次调用ContextSelector来提供 Web 应用程序自己的LoggerContext时,它将首先检查ThreadLocal变量是否已设置。如果是这样,那么将不会发出对 JNDI 环境的调用。 LoggerContextFilter类极大地提高了性能。

像所有 servlet 过滤器一样,LoggerContextFilter可以在 Web 应用程序的处理之前和之后执行操作。这允许筛选器在流程开始时设置ThreadLocal变量,并在 Web 应用程序完成对请求的处理后将其删除。此行为允许线程被回收以供另一个 Web 应用程序使用,并且仍提供正确的LoggerContext

通过将以下行添加到 Web 应用程序的* web.xml *文件中,可以使用LoggerContextFilter

\ <filter-name> LoggerContextFilter</filter-name><filter-class> ch.qos.logback.classic.selector.servlet.LoggerContextFilter</filter-class></filter><filter-mapping><filter-name> LoggerContextFilter</filter-name><url-pattern>/*</url-pattern></filter-mapping>

Some recommandations

为避免混淆,谨慎地在* web.xml *文件中命名每个 Web 应用程序,如下所示:

<display-name>Name_Of_My_WebApp</display-name>

我们建议您唯一命名logback配置资源。特别是,对于非默认 Logger 上下文,请避免将 logback 配置资源命名为* logback.xml *。

尝试配置 Web 应用程序时,logback 将使用线程上下文类加载器搜索资源* logback.xml 。因此,它将首先尝试使用特定于 Web 应用程序的类加载器来定位 logback.xml 文件。但是,如果文件 logback.xml 不存在(如果您忘记在 WEB-INF/classes 中放一个自定义文件),并且文件 logback.xml *在类加载器树的上方,我们可能最终会遇到这样的情况,即将使用与配置默认上下文相同的文件来配置 Web 应用程序的 Logger 上下文。多个存储库非自愿地共享同一配置将导致日志输出损坏。