Log4j2-08-Web Applications
在Web应用程序中使用Log4j
在Java EE web应用程序中使用Log4j或任何其他日志框架时,必须特别小心。
当容器关闭或web应用程序取消部署时,正确清理日志资源(关闭数据库连接、关闭文件等)是很重要的。由于web应用程序中的类加载器的特性,Log4j资源不能通过正常方式清理。
当web应用程序部署时,Log4j必须“启动”,当web应用程序取消部署时,Log4j必须“关闭”。如何工作取决于您的应用程序是Servlet 3.0或更新版本还是Servlet 2.5 web应用程序。
由于名称空间从javax更改为jakarta,对于Servlet 5.0或更新版本,您需要使用log4j-jakarta-web而不是log4j-web。
无论哪种情况,您都需要将log4j-web模块添加到您的部署中,详细信息请参见Maven、Ivy和Gradle Artifacts手册页面。
为了避免出现问题,当包含Log4j -web jar时,将自动禁用Log4j关闭钩子。
配置
Log4j允许使用log4jConfiguration上下文参数在web.xml中指定配置文件。
Log4j将通过以下方式搜索配置文件:
如果提供了一个位置,它将作为servlet上下文资源进行搜索。
例如,如果log4jConfiguration包含“logging.xml”,那么Log4j将在web应用程序的根目录中查找具有该名称的文件。
如果没有定义位置,Log4j将在WEB-INF目录中搜索以“log4j2”开头的文件。如果找到多个文件,并且存在以“log4j2-name”开头的文件(其中name是web应用程序的名称),则将使用该文件。否则将使用第一个文件。
使用类路径和文件url的“正常”搜索顺序将用于定位配置文件。
Servlet 3.0和更新的Web应用程序
Servlet 3.0或更新的web应用程序是任何版本属性值为“3.0”或更高的 ``。
当然,应用程序还必须运行在兼容的web容器中。
一些例子是:
Tomcat 7.0及更高版本
GlassFish 3.0及更高版本
JBoss 7.0及以上版本
Oracle WebLogic 12c及更高版本
IBM WebSphere 8.0及更高版本
短篇小说
Log4j 2在Servlet 3.0和更新的web应用程序中“只工作”。它能够在应用程序部署时自动启动,并在应用程序取消部署时自动关闭。多亏了Servlet 3.0中添加的ServletContainerInitializer API,相关的Filter和ServletContextListener类可以在web应用程序启动时动态注册。
重要提示!出于性能原因,容器经常忽略某些已知不包含tld或servletcontainerinitializer的jar,并且不扫描它们以查找web片段和初始化器。
重要的是,Tomcat 7
isLog4jAutoInitializationDisabled
true
一旦禁用了自动初始化,就必须像初始化Servlet 2.5 web应用程序一样初始化Log4j。您必须在执行任何其他应用程序代码(例如Spring Framework启动代码)之前进行此初始化。
您可以使用log4jContextName、log4jConfiguration和/或isLog4jContextSelectorNamed上下文参数自定义侦听器和过滤器的行为。
在下面的上下文参数部分中了解更多信息。
除非使用isLog4jAutoInitializationDisabled禁用自动初始化,否则不能在部署描述符(web.xml)或Servlet 3.0或更新的应用程序中的其他初始化器或侦听器中手动配置Log4jServletContextListener或Log4jServletFilter。这样做将导致启动错误和未指定的错误行为。
# Servlet 2.5 Web应用
Servlet 2.5 web应用程序是任何版本属性值为“2.5”的 ``。
version属性是唯一重要的东西;即使web应用程序运行在Servlet 3.0或更新的容器中,如果版本属性为“2.5”,它也是Servlet 2.5 web应用程序。
注意,Log4j 2不支持Servlet 2.4和更早的web应用程序。
如果您在Servlet 2.5 web应用程序中使用Log4j,或者如果您已经使用isLog4jAutoInitializationDisabled上下文参数禁用了自动初始化,则必须在部署描述符中或以编程方式配置Log4jServletContextListener和Log4jServletFilter。
过滤器应该匹配任何类型的所有请求。侦听器应该是应用程序中定义的第一个侦听器,过滤器应该是应用程序中定义和映射的第一个过滤器。
这很容易完成使用以下web.xml代码:
```xml
org.apache.logging.log4j.web.Log4jServletContextListener
log4jServletFilter
org.apache.logging.log4j.web.Log4jServletFilter
log4jServletFilter
/*
REQUEST
FORWARD
INCLUDE
ERROR
ASYNC
您可以使用log4jContextName、log4jConfiguration和/或isLog4jContextSelectorNamed上下文参数自定义侦听器和过滤器的行为。
在下面的上下文参数部分中了解更多信息。
上下文参数 Context Parameters
默认情况下,Log4j 2使用ServletContext的上下文名称作为LoggerContext名称,并使用标准模式来定位Log4j配置文件。
您可以使用三个上下文参数来控制此行为。
第一个是isLog4jContextSelectorNamed,指定是否应该使用JndiContextSelector来选择上下文。如果未指定isLog4jContextSelectorNamed或为非true,则假定它为false。
如果isLog4jContextSelectorNamed为true,则必须在web.xml中指定log4jContextName或display-name;否则,应用程序将以异常启动失败。在这种情况下,还应该指定log4jConfiguration,并且必须是配置文件的有效URI;但是,此参数不是必需的。
如果isLog4jContextSelectorNamed不为真,则可以选择性地指定log4jConfiguration,并且必须是一个有效的URI或配置文件的路径,或者以“classpath:”开头,以表示可以在类路径上找到的配置文件。如果没有这个参数,Log4j将使用标准机制来定位配置文件。
在指定这些上下文参数时,您必须在部署描述符(web.xml)中指定它们,即使在Servlet 3.0或never应用程序中也是如此。如果将它们添加到侦听器中的ServletContext中,Log4j将在上下文参数可用之前进行初始化,并且它们将不起作用。下面是这些上下文参数的一些示例用法。
设置日志上下文名称为“myApplication”
log4jContextName
myApplication
Set the Configuration Path/File/URI to "/etc/myApp/myLogging.xml"
log4jConfiguration
file:///etc/myApp/myLogging.xml
Use the JndiContextSelector
isLog4jContextSelectorNamed
true
log4jContextName
appWithJndiSelector
log4jConfiguration
file:///D:/conf/myLogging.xml
注意,在这种情况下,你还必须将"Log4jContextSelector"系统属性设置为"org.apache.logging.log4j.core.selector.JndiContextSelector"。
出于安全原因,从Log4j 2.17.0开始,必须通过设置系统属性log4j22 . enablejndicontextselector =true来启用JNDI。
在配置过程中使用Web应用信息
您可能希望在配置期间使用有关web应用程序的信息。例如,你可以将web应用程序的上下文路径嵌入到滚动文件追加器的名称中。有关更多信息,请参阅查找中的WebLookup。
JavaServer页面日志
您可以在jsp中使用Log4j 2,就像在任何其他Java代码中一样。只需获取一个Logger并调用它的方法来记录事件。然而,这要求您在jsp中使用Java代码,而一些开发团队确实不习惯这样做。如果您有一个不熟悉使用Java的专用用户界面开发团队,您甚至可能在jsp中禁用Java代码。
出于这个原因,Log4j 2提供了一个JSP标记库,使您能够在不使用任何Java代码的情况下记录事件。要了解更多关于使用这个标记库的信息,请阅读Log4j标记库文档。
重要提示!如上所述,容器经常忽略某些已知不包含TLD的jar,并且不扫描它们以查找TLD文件。重要的是,Tomcat 7 {
final Logger logger = LogManager.getLogger(TestAsyncServlet.class);
logger.info("Hello, servlet!");
}));
}
}
或者,您可以从ServletContext属性中获取Log4jWebLifeCycle实例,在异步线程的第一行代码中调用它的setLoggerContext方法,在异步线程的最后一行代码中调用它的clearLoggerContext方法。
下面的代码演示了这一点。它使用容器线程池执行异步请求处理,将一个匿名的内部Runnable传递给start方法。
```java
import java.io.IOException;
import javax.servlet.AsyncContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.web.Log4jWebLifeCycle;
import org.apache.logging.log4j.web.WebLoggerContextUtils;
public class TestAsyncServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
final AsyncContext asyncContext = req.startAsync();
asyncContext.start(new Runnable() {
@Override
public void run() {
final Log4jWebLifeCycle webLifeCycle =
WebLoggerContextUtils.getWebLifeCycle(TestAsyncServlet.this.getServletContext());
webLifeCycle.setLoggerContext();
try {
final Logger logger = LogManager.getLogger(TestAsyncServlet.class);
logger.info("Hello, servlet!");
} finally {
webLifeCycle.clearLoggerContext();
}
}
});
}
}
注意,一旦线程完成处理,必须调用clearLoggerContext。如果不这样做,将导致内存泄漏。如果使用线程池,它甚至会破坏容器中其他web应用程序的日志记录。
出于这个原因,这里的示例显示了在finally块中清除上下文,该块将始终执行。
使用 Servlet Appender
Log4j提供了一个Servlet Appender,它使用Servlet上下文作为日志目标。
例如:
为了避免对servlet上下文的异常进行双重日志记录,您必须在PatternLayout中使用%ex{none},如示例所示。
异常将从消息文本中省略,但它将作为实际的Throwable对象传递给servlet上下文。