| //// |
| Licensed to the Apache Software Foundation (ASF) under one or more |
| contributor license agreements. See the NOTICE file distributed with |
| this work for additional information regarding copyright ownership. |
| The ASF licenses this file to You under the Apache License, Version 2.0 |
| (the "License"); you may not use this file except in compliance with |
| the License. You may obtain a copy of the License at |
| |
| https://www.apache.org/licenses/LICENSE-2.0 |
| |
| Unless required by applicable law or agreed to in writing, software |
| distributed under the License is distributed on an "AS IS" BASIS, |
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| See the License for the specific language governing permissions and |
| limitations under the License. |
| //// |
| = Web Applications |
| Nick Williams <nickwilliams@apache.org>; Matt Sicker <mattsicker@apache.org> |
| |
| == Using Log4j 2 in Web Applications |
| |
| You must take particular care when using Log4j or any other logging |
| framework within a Java EE web application. It's important for logging |
| resources to be properly cleaned up (database connections closed, files |
| closed, etc.) when the container shuts down or the web application is |
| undeployed. Because of the nature of class loaders within web |
| applications, Log4j resources cannot be cleaned up through normal means. |
| Log4j must be "started" when the web application deploys and "shut down" |
| when the web application undeploys. How this works varies depending on |
| whether your application is a link:#Servlet-3.0[Servlet 3.0 or newer] or |
| link:#Servlet-2.5[Servlet 2.5] web application. |
| |
| In either case, you'll need to add the `log4j-web` module to your |
| deployment as detailed in the link:../maven-artifacts.html[Maven, Ivy, |
| and Gradle Artifacts] manual page. |
| |
| NOTE: To avoid problems the Log4j shutdown hook will automatically be |
| disabled when the log4j-web jar is included. |
| |
| [#Configuration] |
| == Configuration |
| |
| Log4j allows the configuration file to be specified in web.xml using the |
| `log4jConfiguration` context parameter. Log4j will search for |
| configuration files by: |
| |
| 1. If a location is provided it will be searched for as a servlet |
| context resource. For example, if `log4jConfiguration` contains |
| "logging.xml" then Log4j will look for a file with that name in the root |
| directory of the web application. |
| 2. If no location is defined Log4j will search for a file that starts |
| with "log4j2" in the WEB-INF directory. If more than one file is found, |
| and if a file that starts with "log4j2-name" is present, where name is |
| the name of the web application, then it will be used. Otherwise the |
| first file will be used. |
| 3. The "normal" search sequence using the classpath and file URLs will |
| be used to locate the configuration file. |
| |
| [#Servlet-3.0] |
| == Servlet 3.0 and Newer Web Applications |
| |
| A Servlet 3.0 or newer web application is any `<web-app>` whose |
| `version` attribute has a value of "3.0" or higher. Of course, the |
| application must also be running in a compatible web container. |
| |
| Some examples are: |
| |
| - Tomcat 7.0 and higher |
| - GlassFish 3.0 and higher |
| - JBoss 7.0 and higher |
| - Oracle WebLogic 12c and higher |
| - IBM WebSphere 8.0 and higher. |
| |
| === The Short Story |
| |
| Log4j 2 "just works" in Servlet 3.0 and newer web applications. It is |
| capable of automatically starting when the application deploys and |
| shutting down when the application undeploys. Thanks to the |
| https://docs.oracle.com/javaee/6/api/javax/servlet/ServletContainerInitializer.html[`ServletContainerInitializer`] |
| API added to Servlet 3.0, the relevant `Filter` and |
| `ServletContextListener` classes can be registered dynamically on web |
| application startup. |
| |
| NOTE: For performance reasons, containers often ignore |
| certain JARs known not to contain TLDs or `ServletContainerInitializer` |
| and do not scan them for web-fragments and initializers. Importantly, |
| Tomcat 7 <7.0.43 ignores all JAR files named log4j*.jar, which prevents |
| this feature from working. This has been fixed in Tomcat 7.0.43, Tomcat |
| 8, and later. In Tomcat 7 <7.0.43 you will need to change |
| `catalina.properties` and remove "log4j*.jar" from the `jarsToSkip` |
| property. You may need to do something similar on other containers if |
| they skip scanning Log4j JAR files. |
| |
| === The Long Story |
| |
| The Log4j 2 Web JAR file is a web-fragment configured to order before |
| any other web fragments in your application. It contains a |
| `ServletContainerInitializer` |
| (link:../log4j-web/apidocs/org/apache/logging/log4j/web/Log4jServletContainerInitializer |
| .html[`Log4jServletContainerInitializer`]) |
| that the container automatically discovers and initializes. This adds |
| the |
| link:../log4j-web/apidocs/org/apache/logging/log4j/web/Log4jServletContextListener.html[`Log4jServletContextListener`] |
| and |
| link:../log4j-web/apidocs/org/apache/logging/log4j/web/Log4jServletFilter.html[`Log4jServletFilter`] |
| to the `ServletContext`. These classes properly initialize and |
| deinitialize the Log4j configuration. |
| |
| For some users, automatically starting Log4j is problematic or |
| undesirable. You can easily disable this feature using the |
| `isLog4jAutoInitializationDisabled` context parameter. Simply add it to |
| your deployment descriptor with the value "true" to disable |
| auto-initialization. You _must_ define the context parameter in |
| `web.xml`. If you set in programmatically, it will be too late for Log4j |
| to detect the setting. |
| |
| [source,xml] |
| ---- |
| <context-param> |
| <param-name>isLog4jAutoInitializationDisabled</param-name> |
| <param-value>true</param-value> |
| </context-param> |
| ---- |
| |
| Once you disable auto-initialization, you must initialize Log4j as you |
| would a link:#Servlet-2.5[Servlet 2.5 web application]. You must do so |
| in a way that this initialization happens before any other application |
| code (such as Spring Framework startup code) executes. |
| |
| You can customize the behavior of the listener and filter using the |
| `log4jContextName`, `log4jConfiguration`, and/or |
| `isLog4jContextSelectorNamed` context parameters. Read more about this |
| in the link:#ContextParams[Context Parameters] section below. You _must |
| not_ manually configure the `Log4jServletContextListener` or |
| `Log4jServletFilter` in your deployment descriptor (`web.xml`) or in |
| another initializer or listener in a Servlet 3.0 or newer application |
| _unless you disable auto-initialization_ with |
| `isLog4jAutoInitializationDisabled`. Doing so will result in startup |
| errors and unspecified erroneous behavior. |
| |
| [#Servlet-2.5] |
| == Servlet 2.5 Web Applications |
| |
| A Servlet 2.5 web application is any `<web-app>` whose `version` |
| attribute has a value of "2.5." The `version` attribute is the only |
| thing that matters; even if the web application is running in a Servlet |
| 3.0 or newer container, it is a Servlet 2.5 web application if the |
| `version` attribute is "2.5." Note that Log4j 2 does not support Servlet |
| 2.4 and older web applications. |
| |
| If you are using Log4j in a Servlet 2.5 web application, or if you have |
| disabled auto-initialization with the |
| `isLog4jAutoInitializationDisabled` context parameter, you _must_ |
| configure the |
| link:../log4j-web/apidocs/org/apache/logging/log4j/web/Log4jServletContextListener.html[`Log4jServletContextListener`] |
| and |
| link:../log4j-web/apidocs/org/apache/logging/log4j/web/Log4jServletFilter.html[`Log4jServletFilter`] |
| in the deployment descriptor or programmatically. The filter should |
| match all requests of any type. The listener should be the very first |
| listener defined in your application, and the filter should be the very |
| first filter defined and mapped in your application. This is easily |
| accomplished using the following `web.xml` code: |
| |
| [source,xml] |
| ---- |
| <listener> |
| <listener-class>org.apache.logging.log4j.web.Log4jServletContextListener</listener-class> |
| </listener> |
| |
| <filter> |
| <filter-name>log4jServletFilter</filter-name> |
| <filter-class>org.apache.logging.log4j.web.Log4jServletFilter</filter-class> |
| </filter> |
| <filter-mapping> |
| <filter-name>log4jServletFilter</filter-name> |
| <url-pattern>/*</url-pattern> |
| <dispatcher>REQUEST</dispatcher> |
| <dispatcher>FORWARD</dispatcher> |
| <dispatcher>INCLUDE</dispatcher> |
| <dispatcher>ERROR</dispatcher> |
| <dispatcher>ASYNC</dispatcher><!-- Servlet 3.0 w/ disabled auto-initialization only; not supported in 2.5 --> |
| </filter-mapping> |
| ---- |
| |
| You can customize the behavior of the listener and filter using the |
| `log4jContextName`, `log4jConfiguration`, and/or |
| `isLog4jContextSelectorNamed` context parameters. Read more about this |
| in the link:#ContextParams[Context Parameters] section below. |
| |
| [#ContextParams] |
| == Context Parameters |
| |
| By default, Log4j 2 uses the `ServletContext`'s |
| https://docs.oracle.com/javaee/6/api/javax/servlet/ServletContext.html#getServletContextName()[context |
| name] as the `LoggerContext` name and uses the standard pattern for |
| locating the Log4j configuration file. There are three context |
| parameters that you can use to control this behavior. The first, |
| `isLog4jContextSelectorNamed`, specifies whether the context should be |
| selected using the |
| link:../log4j-core/apidocs/org/apache/logging/log4j/core/selector/JndiContextSelector.html[`JndiContextSelector`]. |
| If `isLog4jContextSelectorNamed` is not specified or is anything other |
| than `true`, it is assumed to be `false`. |
| |
| If `isLog4jContextSelectorNamed` is `true`, `log4jContextName` must be |
| specified or `display-name` must be specified in `web.xml`; otherwise, |
| the application will fail to start with an exception. |
| `log4jConfiguration` _should_ also be specified in this case, and must |
| be a valid URI for the configuration file; however, this parameter is |
| not required. |
| |
| If `isLog4jContextSelectorNamed` is not `true`, `log4jConfiguration` may |
| optionally be specified and must be a valid URI or path to a |
| configuration file or start with "classpath:" to denote a configuration |
| file that can be found on the classpath. Without this parameter, Log4j |
| will use the standard mechanisms for locating the configuration file. |
| |
| When specifying these context parameters, you must specify them in the |
| deployment descriptor (`web.xml`) even in a Servlet 3.0 or never |
| application. If you add them to the `ServletContext` within a listener, |
| Log4j will initialize before the context parameters are available and |
| they will have no effect. Here are some sample uses of these context |
| parameters. |
| |
| === Set the Logging Context Name to "myApplication" |
| |
| [source,xml] |
| ---- |
| <context-param> |
| <param-name>log4jContextName</param-name> |
| <param-value>myApplication</param-value> |
| </context-param> |
| ---- |
| |
| === Set the Configuration Path/File/URI to "/etc/myApp/myLogging.xml" |
| |
| [source,xml] |
| ---- |
| <context-param> |
| <param-name>log4jConfiguration</param-name> |
| <param-value>file:///etc/myApp/myLogging.xml</param-value> |
| </context-param> |
| ---- |
| |
| === Use the `JndiContextSelector` |
| |
| [source,xml] |
| ---- |
| <context-param> |
| <param-name>isLog4jContextSelectorNamed</param-name> |
| <param-value>true</param-value> |
| </context-param> |
| <context-param> |
| <param-name>log4jContextName</param-name> |
| <param-value>appWithJndiSelector</param-value> |
| </context-param> |
| <context-param> |
| <param-name>log4jConfiguration</param-name> |
| <param-value>file:///D:/conf/myLogging.xml</param-value> |
| </context-param> |
| ---- |
| |
| Note that in this case you must also set the "Log4jContextSelector" |
| system property to |
| "org.apache.logging.log4j.core.selector.JndiContextSelector". |
| |
| [#WebLookup] |
| == Using Web Application Information During the Configuration |
| |
| You may want to use information about the web application during |
| configuration. For example, you could embed the web application's |
| context path in the name of a Rolling File Appender. See WebLookup in |
| link:./lookups.html#WebLookup[Lookups] for more information. |
| |
| [#JspLogging] |
| == JavaServer Pages Logging |
| |
| You may use Log4j 2 within JSPs just as you would within any other Java |
| code. Simple obtain a `Logger` and call its methods to log events. |
| However, this requires you to use Java code within your JSPs, and some |
| development teams rightly are not comfortable with doing this. If you |
| have a dedicated user interface development team that is not familiar |
| with using Java, you may even have Java code disabled in your JSPs. |
| |
| For this reason, Log4j 2 provides a JSP Tag Library that enables you to |
| log events without using any Java code. To read more about using this |
| tag library, link:../log4j-taglib/index.html[read the Log4j Tag Library |
| documentation.] |
| |
| NOTE: As noted above, containers often ignore certain |
| JARs known not to contain TLDs and do not scan them for TLD files. |
| Importantly, Tomcat 7 <7.0.43 ignores all JAR files named log4j*.jar, |
| which prevents the JSP tag library from being automatically discovered. |
| This does not affect Tomcat 6.x and has been fixed in Tomcat 7.0.43, |
| Tomcat 8, and later. In Tomcat 7 <7.0.43 you will need to change |
| `catalina.properties` and remove "log4j*.jar" from the `jarsToSkip` |
| property. You may need to do something similar on other containers if |
| they skip scanning Log4j JAR files. |
| |
| [#Async] |
| == Asynchronous Requests and Threads |
| |
| The handling of asynchronous requests is tricky, and regardless of |
| Servlet container version or configuration Log4j cannot handle |
| everything automatically. When standard requests, forwards, includes, |
| and error resources are processed, the `Log4jServletFilter` binds the |
| `LoggerContext` to the thread handling the request. After request |
| processing completes, the filter unbinds the `LoggerContext` from the |
| thread. |
| |
| Similarly, when an internal request is dispatched using a |
| `javax.servlet.AsyncContext`, the `Log4jServletFilter` also binds the |
| `LoggerContext` to the thread handling the request and unbinds it when |
| request processing completes. However, this only happens for requests |
| _dispatched_ through the `AsyncContext`. There are other asynchronous |
| activities that can take place other than internal dispatched requests. |
| |
| For example, after starting an `AsyncContext` you could start up a |
| separate thread to process the request in the background, possibly |
| writing the response with the `ServletOutputStream`. Filters cannot |
| intercept the execution of this thread. Filters also cannot intercept |
| threads that you start in the background during non-asynchronous |
| requests. This is true whether you use a brand new thread or a thread |
| borrowed from a thread pool. So what can you do for these special |
| threads? |
| |
| You may not need to do anything. If you didn't use the |
| `isLog4jContextSelectorNamed` context parameter, there is no need to |
| bind the `LoggerContext` to the thread. Log4j can safely locate the |
| `LoggerContext` on its own. In these cases, the filter provides only |
| very modest performance gains, and only when creating new `Logger` instances. |
| However, if you _did_ specify the `isLog4jContextSelectorNamed` context |
| parameter with the value "true", you will need to manually bind the |
| `LoggerContext` to asynchronous threads. Otherwise, Log4j will not be |
| able to locate it. |
| |
| Thankfully, Log4j provides a simple mechanism for binding the |
| `LoggerContext` to asynchronous threads in these special circumstances. |
| The simplest way to do this is to wrap the `Runnable` instance that is |
| passed to the `AsyncContext.start()` method. |
| |
| [source,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.WebLoggerContextUtils; |
| |
| public class TestAsyncServlet extends HttpServlet { |
| |
| @Override |
| protected void doGet(final HttpServletRequest req, final HttpServletResponse resp) throws ServletException, IOException { |
| final AsyncContext asyncContext = req.startAsync(); |
| asyncContext.start(WebLoggerContextUtils.wrapExecutionContext(this.getServletContext(), new Runnable() { |
| @Override |
| public void run() { |
| final Logger logger = LogManager.getLogger(TestAsyncServlet.class); |
| logger.info("Hello, servlet!"); |
| } |
| })); |
| } |
| |
| @Override |
| protected void doPost(final HttpServletRequest req, final HttpServletResponse resp) throws ServletException, IOException { |
| final AsyncContext asyncContext = req.startAsync(); |
| asyncContext.start(new Runnable() { |
| @Override |
| public void run() { |
| final Log4jWebSupport webSupport = |
| WebLoggerContextUtils.getWebLifeCycle(TestAsyncServlet.this.getServletContext()); |
| webSupport.setLoggerContext(); |
| // do stuff |
| webSupport.clearLoggerContext(); |
| } |
| }); |
| } |
| } |
| ---- |
| |
| This can be slightly more convenient when using Java 1.8 and lambda |
| functions as demonstrated below. |
| |
| [source,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.WebLoggerContextUtils; |
| |
| public class TestAsyncServlet extends HttpServlet { |
| @Override |
| protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { |
| final AsyncContext asyncContext = req.startAsync(); |
| asyncContext.start(WebLoggerContextUtils.wrapExecutionContext(this.getServletContext(), () -> { |
| final Logger logger = LogManager.getLogger(TestAsyncServlet.class); |
| logger.info("Hello, servlet!"); |
| })); |
| } |
| } |
| ---- |
| |
| Alternatively, you can obtain the |
| link:../log4j-web/apidocs/org/apache/logging/log4j/web/Log4jWebLifeCycle.html[`Log4jWebLifeCycle`] |
| instance from the `ServletContext` attributes, call its |
| `setLoggerContext` method as the very first line of code in your |
| asynchronous thread, and call its `clearLoggerContext` method as the |
| very last line of code in your asynchronous thread. The following code |
| demonstrates this. It uses the container thread pool to execute |
| asynchronous request processing, passing an anonymous inner `Runnable` |
| to the `start` method. |
| |
| [source,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(); |
| } |
| } |
| }); |
| } |
| } |
| ---- |
| |
| Note that you _must_ call `clearLoggerContext` once your thread is |
| finished processing. Failing to do so will result in memory leaks. If |
| using a thread pool, it can even disrupt the logging of other web |
| applications in your container. For that reason, the example here shows |
| clearing the context in a `finally` block, which will always execute. |
| |
| == Using the Servlet Appender |
| |
| Log4j provides a Servlet Appender that uses the servlet context as the |
| log target. For example: |
| |
| [source,xml] |
| ---- |
| <Configuration status="WARN" name="ServletTest"> |
| |
| <Appenders> |
| <Servlet name="Servlet"> |
| <PatternLayout pattern="%m%n%ex{none}"/> |
| </Servlet> |
| </Appenders> |
| |
| <Loggers> |
| <Root level="debug"> |
| <AppenderRef ref="Servlet"/> |
| </Root> |
| </Loggers> |
| |
| </Configuration> |
| ---- |
| |
| To avoid double logging of exceptions to the servlet context, you must |
| use `%ex{none}` in your `PatternLayout` as shown in the example. The |
| exception will be omitted from the message text but it is passed to the |
| servlet context as the actual `Throwable` object. |