logging-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From nickwilli...@apache.org
Subject svn commit: r1562365 - in /logging/log4j/log4j2/trunk: log4j-core/src/main/java/org/apache/logging/log4j/core/web/ log4j-core/src/test/java/org/apache/logging/log4j/core/web/ src/changes/ src/site/ src/site/xdoc/manual/
Date Wed, 29 Jan 2014 06:17:09 GMT
Author: nickwilliams
Date: Wed Jan 29 06:17:08 2014
New Revision: 1562365

URL: http://svn.apache.org/r1562365
Log:
Fixing LOG4J2-452 and LOG4J2-512 (Part 2-Documentation): Significantly improving web application
documentation

Modified:
    logging/log4j/log4j2/trunk/log4j-core/src/main/java/org/apache/logging/log4j/core/web/Log4jWebInitializerImpl.java
    logging/log4j/log4j2/trunk/log4j-core/src/main/java/org/apache/logging/log4j/core/web/Log4jWebSupport.java
    logging/log4j/log4j2/trunk/log4j-core/src/test/java/org/apache/logging/log4j/core/web/Log4jWebInitializerImplTest.java
    logging/log4j/log4j2/trunk/src/changes/changes.xml
    logging/log4j/log4j2/trunk/src/site/site.xml
    logging/log4j/log4j2/trunk/src/site/xdoc/manual/webapp.xml

Modified: logging/log4j/log4j2/trunk/log4j-core/src/main/java/org/apache/logging/log4j/core/web/Log4jWebInitializerImpl.java
URL: http://svn.apache.org/viewvc/logging/log4j/log4j2/trunk/log4j-core/src/main/java/org/apache/logging/log4j/core/web/Log4jWebInitializerImpl.java?rev=1562365&r1=1562364&r2=1562365&view=diff
==============================================================================
--- logging/log4j/log4j2/trunk/log4j-core/src/main/java/org/apache/logging/log4j/core/web/Log4jWebInitializerImpl.java
(original)
+++ logging/log4j/log4j2/trunk/log4j-core/src/main/java/org/apache/logging/log4j/core/web/Log4jWebInitializerImpl.java
Wed Jan 29 06:17:08 2014
@@ -79,8 +79,10 @@ final class Log4jWebInitializerImpl impl
             this.initialized = true;
 
             this.name = this.substitutor.replace(this.servletContext.getInitParameter(LOG4J_CONTEXT_NAME));
-            final String location = this.substitutor.replace(this.servletContext.getInitParameter(LOG4J_CONFIG_LOCATION));
-            final boolean isJndi = "true".equals(this.servletContext.getInitParameter(IS_LOG4J_CONTEXT_SELECTOR_NAMED));
+            final String location =
+                    this.substitutor.replace(this.servletContext.getInitParameter(LOG4J_CONFIG_LOCATION));
+            final boolean isJndi =
+                    "true".equalsIgnoreCase(this.servletContext.getInitParameter(IS_LOG4J_CONTEXT_SELECTOR_NAMED));
 
             if (isJndi) {
                 this.initializeJndi(location);
@@ -179,6 +181,17 @@ final class Log4jWebInitializerImpl impl
         ContextAnchor.THREAD_CONTEXT.remove();
     }
 
+    @Override
+    public void wrapExecution(Runnable runnable) {
+        this.setLoggerContext();
+
+        try {
+            runnable.run();
+        } finally {
+            this.clearLoggerContext();
+        }
+    }
+
     private ClassLoader getClassLoader() {
         try {
             // if container is Servlet 3.0, use its getClassLoader method

Modified: logging/log4j/log4j2/trunk/log4j-core/src/main/java/org/apache/logging/log4j/core/web/Log4jWebSupport.java
URL: http://svn.apache.org/viewvc/logging/log4j/log4j2/trunk/log4j-core/src/main/java/org/apache/logging/log4j/core/web/Log4jWebSupport.java?rev=1562365&r1=1562364&r2=1562365&view=diff
==============================================================================
--- logging/log4j/log4j2/trunk/log4j-core/src/main/java/org/apache/logging/log4j/core/web/Log4jWebSupport.java
(original)
+++ logging/log4j/log4j2/trunk/log4j-core/src/main/java/org/apache/logging/log4j/core/web/Log4jWebSupport.java
Wed Jan 29 06:17:08 2014
@@ -56,7 +56,15 @@ public interface Log4jWebSupport {
     void setLoggerContext();
 
     /**
-     * Clears the logger context set up in {@link #setLoggerContext()}.
+     * Clears the logger context set up in {@link #setLoggerContext}.
      */
     void clearLoggerContext();
+
+    /**
+     * Sets the logger context by calling {@link #setLoggerContext}, executes the runnable
argument, then clears the
+     * logger context by calling {@link #clearLoggerContext}.
+     *
+     * @param runnable The runnable to execute wrapped with a configured logger context
+     */
+    void wrapExecution(Runnable runnable);
 }

Modified: logging/log4j/log4j2/trunk/log4j-core/src/test/java/org/apache/logging/log4j/core/web/Log4jWebInitializerImplTest.java
URL: http://svn.apache.org/viewvc/logging/log4j/log4j2/trunk/log4j-core/src/test/java/org/apache/logging/log4j/core/web/Log4jWebInitializerImplTest.java?rev=1562365&r1=1562364&r2=1562365&view=diff
==============================================================================
--- logging/log4j/log4j2/trunk/log4j-core/src/test/java/org/apache/logging/log4j/core/web/Log4jWebInitializerImplTest.java
(original)
+++ logging/log4j/log4j2/trunk/log4j-core/src/test/java/org/apache/logging/log4j/core/web/Log4jWebInitializerImplTest.java
Wed Jan 29 06:17:08 2014
@@ -22,6 +22,7 @@ import javax.servlet.UnavailableExceptio
 import org.apache.logging.log4j.core.LoggerContext;
 import org.apache.logging.log4j.core.impl.ContextAnchor;
 import org.easymock.Capture;
+import org.easymock.IAnswer;
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
@@ -417,4 +418,74 @@ public class Log4jWebInitializerImplTest
 
         assertNull("The context should finally still be null.", ContextAnchor.THREAD_CONTEXT.get());
     }
+
+    @Test
+    public void testWrapExecutionWithNoParameters() throws Exception {
+        Capture<Object> loggerContextCapture = new Capture<Object>();
+
+        expect(this.servletContext.getInitParameter(Log4jWebSupport.LOG4J_CONTEXT_NAME)).andReturn(null);
+        expect(this.servletContext.getInitParameter(Log4jWebSupport.LOG4J_CONFIG_LOCATION)).andReturn(null);
+        expect(this.servletContext.getInitParameter(Log4jWebSupport.IS_LOG4J_CONTEXT_SELECTOR_NAMED))
+                .andReturn(null);
+        expect(this.servletContext.getServletContextName()).andReturn("helloWorld01");
+        this.servletContext.setAttribute(eq(Log4jWebSupport.CONTEXT_ATTRIBUTE), capture(loggerContextCapture));
+        expectLastCall();
+
+        replay(this.servletContext);
+
+        assertNull("The context should be null.", ContextAnchor.THREAD_CONTEXT.get());
+
+        this.initializer.initialize();
+
+        assertNotNull("The context attribute should not be null.", loggerContextCapture.getValue());
+        assertTrue("The context attribute is not correct.",
+                loggerContextCapture.getValue() instanceof org.apache.logging.log4j.spi.LoggerContext);
+        final org.apache.logging.log4j.spi.LoggerContext loggerContext =
+                (org.apache.logging.log4j.spi.LoggerContext)loggerContextCapture.getValue();
+
+        verify(this.servletContext);
+        reset(this.servletContext);
+
+        assertNull("The context should still be null.", ContextAnchor.THREAD_CONTEXT.get());
+
+        Runnable runnable = createStrictMock(Runnable.class);
+        runnable.run();
+        expectLastCall().andAnswer(new IAnswer<Void>() {
+            @Override
+            public Void answer() {
+                final LoggerContext context = ContextAnchor.THREAD_CONTEXT.get();
+                assertNotNull("The context should not be null.", context);
+                assertSame("The context is not correct.", loggerContext, context);
+                return null;
+            }
+        });
+
+        replay(this.servletContext, runnable);
+
+        this.initializer.wrapExecution(runnable);
+
+        assertNull("The context should be null again.", ContextAnchor.THREAD_CONTEXT.get());
+
+        verify(this.servletContext, runnable);
+        reset(this.servletContext);
+
+        this.servletContext.log(anyObject(String.class));
+        expectLastCall();
+        this.servletContext.removeAttribute(Log4jWebSupport.CONTEXT_ATTRIBUTE);
+        expectLastCall();
+
+        replay(this.servletContext);
+
+        this.initializer.deinitialize();
+
+        verify(this.servletContext);
+        reset(this.servletContext);
+        replay(this.servletContext);
+
+        assertNull("The context should again still be null.", ContextAnchor.THREAD_CONTEXT.get());
+
+        this.initializer.setLoggerContext();
+
+        assertNull("The context should finally still be null.", ContextAnchor.THREAD_CONTEXT.get());
+    }
 }

Modified: logging/log4j/log4j2/trunk/src/changes/changes.xml
URL: http://svn.apache.org/viewvc/logging/log4j/log4j2/trunk/src/changes/changes.xml?rev=1562365&r1=1562364&r2=1562365&view=diff
==============================================================================
--- logging/log4j/log4j2/trunk/src/changes/changes.xml (original)
+++ logging/log4j/log4j2/trunk/src/changes/changes.xml Wed Jan 29 06:17:08 2014
@@ -21,7 +21,7 @@
   </properties>
   <body>
     <release version="2.0-RC1" date="2014-MM-DD" description="Bug fixes and enhancements">
-      <action issue="LOG4J2-452" dev="nickwilliams" type="fix" due-to="">
+      <action issue="LOG4J2-452" dev="nickwilliams" type="fix">
         Added a ServletContext attribute that, when set to "true", disables Log4j's auto-initialization
in
         Servlet 3.0+ web applications.
       </action>

Modified: logging/log4j/log4j2/trunk/src/site/site.xml
URL: http://svn.apache.org/viewvc/logging/log4j/log4j2/trunk/src/site/site.xml?rev=1562365&r1=1562364&r2=1562365&view=diff
==============================================================================
--- logging/log4j/log4j2/trunk/src/site/site.xml (original)
+++ logging/log4j/log4j2/trunk/src/site/site.xml Wed Jan 29 06:17:08 2014
@@ -65,8 +65,9 @@
         <item name="Servlet 3.0 and Newer" href="/manual/webapp.html#Servlet-3.0" />
         <item name="Servlet 2.5" href="/manual/webapp.html#Servlet-2.5" />
         <item name="Context Parameters" href="/manual/webapp.html#ContextParams" />
-        <item name="Configuration" href="/manual/webapp.html#WebLookup" />
+        <item name="Configuration Lookups" href="/manual/webapp.html#WebLookup" />
         <item name="JavaServer Pages Logging" href="/manual/webapp.html#JspLogging" />
+        <item name="Asynchronous Requests" href="/manual/webapp.html#Async" />
       </item>
 
       <item name="Plugins" href="/manual/plugins.html" collapse="true">

Modified: logging/log4j/log4j2/trunk/src/site/xdoc/manual/webapp.xml
URL: http://svn.apache.org/viewvc/logging/log4j/log4j2/trunk/src/site/xdoc/manual/webapp.xml?rev=1562365&r1=1562364&r2=1562365&view=diff
==============================================================================
--- logging/log4j/log4j2/trunk/src/site/xdoc/manual/webapp.xml (original)
+++ logging/log4j/log4j2/trunk/src/site/xdoc/manual/webapp.xml Wed Jan 29 06:17:08 2014
@@ -67,12 +67,30 @@
           and deinitialize the Log4j configuration.
         </p>
         <p>
+          For some users, automatically starting Log4j is problematic or undesirable. You
can easily disable this
+          feature using the <code>isLog4jAutoInitializationDisabled</code> context
parameter. Simply add it to your
+          deployment descriptor with the value "true" to disable auto-initialization. You
<em>must</em> define the
+          context parameter in <code>web.xml</code>. If you set in programmatically,
it will be too late for Log4j
+          to detect the setting.
+          <pre class="prettyprint linenums"><![CDATA[    <context-param>
+        <param-name>isLog4jAutoInitializationDisabled</param-name>
+        <param-value>true</param-value>
+    </context-param>]]></pre>
+        </p>
+        <p>
+          Once you disable auto-initialization, you must initialize Log4j as you would a
+          <a href="#Servlet-2.5">Servlet 2.5 web application</a>. You must do
so in a way that this initialization
+          happens before any other application code (such as Spring Framework startup code)
executes.
+        </p>
+        <p>
           You can customize the behavior of the listener and filter using the <code>log4jContextName</code>,
           <code>log4jConfiguration</code>, and/or <code>isLog4jContextSelectorNamed</code>
context parameters. Read more
           about this in the <a href="#ContextParams">Context Parameters</a> section
below. You <em>must not</em>
           manually configure the <code>Log4jServletContextListener</code> or
<code>Log4jServletFilter</code> in your
           deployment descriptor (<code>web.xml</code>) or in another initializer
or listener in a Servlet 3.0 or newer
-          application. Doing so will result in startup errors and unspecified erroneous behavior.
+          application <em>unless you disable auto-initialization</em> with
+          <code>isLog4jAutoInitializationDisabled</code>. Doing so will result
in startup errors and unspecified
+          erroneous behavior.
         </p>
       </subsection>
       <a name="Servlet-2.5" />
@@ -85,14 +103,15 @@
           applications.
         </p>
         <p>
-          If you are using Log4j in a Servlet 2.5 web application, you <em>must</em>
configure the
+          If you are using Log4j in a Servlet 2.5 web application, or if you have disabled
auto-initialization with
+          the <code>isLog4jAutoInitializationDisabled</code> context parameter,
you <em>must</em> configure the
           <a href="../log4j-core/apidocs/org/apache/logging/log4j/core/web/Log4jServletContextListener.html"
             >Log4jServletContextListener</a> and
           <a href="../log4j-core/apidocs/org/apache/logging/log4j/core/web/Log4jServletFilter.html"
-            >Log4jServletFilter</a> in the deployment descriptor. The filter should
match all requests of any type. The
-          listener should be the very first listener defined in the deployment descriptor,
and the filter should be the
-          very first filter defined and mapped in the deployment descriptor. This is easily
accomplished using the
-          following <code>web.xml</code> code:
+            >Log4jServletFilter</a> 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 <code>web.xml</code> code:
           <pre class="prettyprint linenums"><![CDATA[    <listener>
         <listener-class>org.apache.logging.log4j.core.web.Log4jServletContextListener</listener-class>
     </listener>
@@ -108,6 +127,7 @@
         <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>]]></pre>
         </p>
         <p>
@@ -178,11 +198,11 @@
         </p>
       </subsection>
       <a name="WebLookup" />
-      <subsection name="Using Web Application information during the configuration">
+      <subsection name="Using Web Application Information During the Configuration">
         <p>
-            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 <a href="./lookups.html#WebLookup">Lookups</a> for
more information.
+          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
+          <a href="./lookups.html#WebLookup">Lookups</a> for more information.
         </p>
       </subsection>        
       <a name="JspLogging" />
@@ -208,6 +228,97 @@
           property. You may need to do something similar on other containers if they skip
scanning Log4j JAR files.
         </p>
       </subsection>
+      <a name="Async" />
+      <subsection name="Asynchronous Requests and Threads">
+        <p>
+          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 <code>Log4jServletFilter</code> binds the <code>LoggerContext</code>
to the thread handling
+          the request. After request processing completes, the filter unbinds the <code>LoggerContext</code>
from the
+          thread.
+        </p>
+        <p>
+          Similarly, when an internal request is dispatched using a <code>javax.servlet.AsyncContext</code>,
the
+          <code>Log4jServletFilter</code> also binds the <code>LoggerContext</code>
to the thread handling the request
+          and unbinds it when request processing completes. However, this only happens for
requests <em>dispatched</em>
+          through the <code>AsyncContext</code>. There are other asynchronous
activities that can take place other than
+          internal dispatched requests.
+        </p>
+        <p>
+          For example, after starting an <code>AsyncContext</code> you could
start up a separate thread to process the
+          request in the background, possibly writing the response with the <code>ServletOutputStream</code>.
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?
+        </p>
+        <p>
+          You may not need to do anything. If you didn't use the <code>isLog4jContextSelectorNamed</code>
context
+          parameter, there is no need to bind the <code>LoggerContext</code>
to the thread. Log4j can safely locate the
+          <code>LoggerContext</code> on its own. In these cases, the filter provides
only very modest performance
+          gains, and only when creating new <code>Logger</code>s. However, if
you <em>did</em> specify the
+          <code>isLog4jContextSelectorNamed</code> context parameter with the
value "true", you will need to manually
+          bind the <code>LoggerContext</code> to asynchronous threads. Otherwise,
Log4j will not be able to locate it.
+        </p>
+        <p>
+          Thankfully, Log4j provides a simple mechanism for binding the <code>LoggerContext</code>
to asynchronous
+          threads in these special circumstances. The key is to obtain a
+          <a href="../log4j-core/apidocs/org/apache/logging/log4j/core/web/Log4jWebSupport.html"
+              >Log4jWebSupport</a> instance from the <code>ServletContext</code>
attributes, call its
+          <code>setLoggerContext</code> method as the very first line of code
in your asynchronous thread, and call
+          its <code>clearLoggerContext</code> 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 <code>Runnable</code> to the
<code>start</code> method.
+          <pre class="prettyprint linenums"><![CDATA[public class AsyncServlet extends
HttpServlet
+{
+    @Override
+    protected void doGet(HttpServletRequest request, HttpServletResponse response)
+            throws ServletException, IOException
+    {
+        final AsyncContext context = request.startAsync(request, response);
+        context.setTimeout(timeout);
+        context.start(new Runnable() {
+            @Override
+            public void run() {
+                Log4jWebSupport support = (Log4jWebSupport)this.getServletContext()
+                        .getAttribute(Log4jWebSupport.SUPPORT_ATTRIBUTE);
+                support.setLoggerContext();
+
+                try {
+                    // miscellaneous asynchronous request handling
+                } finally {
+                    support.clearLoggerContext();
+                }
+            }
+        });
+    }
+}]]></pre>
+        </p>
+        <p>
+          Note that you <em>must</em> call <code>clearLoggerContext</code>
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 <code>finally</code> block, which will always execute.
As a shortcut, you can use the
+          <code>wrapExecution</code> method, which takes care of setting the
context, executing the code you pass in,
+          and clearing the context in a finally black. This is especially convenient when
using Java 8 lambdas.
+          <pre class="prettyprint linenums"><![CDATA[public class AsyncServlet extends
HttpServlet
+{
+    @Override
+    protected void doGet(HttpServletRequest request, HttpServletResponse response)
+            throws ServletException, IOException
+    {
+        final AsyncContext context = request.startAsync(request, response);
+        context.setTimeout(timeout);
+        context.start(() => {
+            Log4jWebSupport support = (Log4jWebSupport)this.getServletContext()
+                    .getAttribute(Log4jWebSupport.SUPPORT_ATTRIBUTE);
+            support.wrapExecution(() => {
+                // miscellaneous asynchronous request handling
+            });
+        });
+    }
+}]]></pre>
+        </p>
+      </subsection>
     </section>
   </body>
 



Mime
View raw message