Author: rpopma
Date: Sat Jun 14 11:34:30 2014
New Revision: 1602578

URL: http://svn.apache.org/r1602578
Log:
LOG4J2-669: Prevent NPE when combining AsyncLoggers with AsyncLoggerConfigs

Added:
    
logging/log4j/log4j2/trunk/log4j-core/src/test/java/org/apache/logging/log4j/core/async/AsyncLoggersWithAsyncLoggerConfigTest.java
   (with props)
    
logging/log4j/log4j2/trunk/log4j-core/src/test/resources/AsyncLoggersWithAsyncLoggerConfigTest.xml
   (with props)
Modified:
    
logging/log4j/log4j2/trunk/log4j-core/src/main/java/org/apache/logging/log4j/core/async/AsyncLoggerConfigHelper.java
    
logging/log4j/log4j2/trunk/log4j-core/src/main/java/org/apache/logging/log4j/core/async/RingBufferLogEvent.java
    
logging/log4j/log4j2/trunk/log4j-core/src/test/java/org/apache/logging/log4j/core/async/RingBufferLogEventTest.java
    logging/log4j/log4j2/trunk/src/changes/changes.xml

Modified: 
logging/log4j/log4j2/trunk/log4j-core/src/main/java/org/apache/logging/log4j/core/async/AsyncLoggerConfigHelper.java
URL: 
http://svn.apache.org/viewvc/logging/log4j/log4j2/trunk/log4j-core/src/main/java/org/apache/logging/log4j/core/async/AsyncLoggerConfigHelper.java?rev=1602578&r1=1602577&r2=1602578&view=diff
==============================================================================
--- 
logging/log4j/log4j2/trunk/log4j-core/src/main/java/org/apache/logging/log4j/core/async/AsyncLoggerConfigHelper.java
 (original)
+++ 
logging/log4j/log4j2/trunk/log4j-core/src/main/java/org/apache/logging/log4j/core/async/AsyncLoggerConfigHelper.java
 Sat Jun 14 11:34:30 2014
@@ -332,10 +332,14 @@ class AsyncLoggerConfigHelper {
         }
         // LOG4J2-639: catch NPE if disruptor field was set to null after our 
check above
         try {
+            LogEvent logEvent = event;
+            if (event instanceof RingBufferLogEvent) {
+                logEvent = ((RingBufferLogEvent) event).createMemento();
+            }
             // Note: do NOT use the temp variable above!
             // That could result in adding a log event to the disruptor after 
it was shut down,
             // which could cause the publishEvent method to hang and never 
return.
-            disruptor.getRingBuffer().publishEvent(translator, event, 
asyncLoggerConfig);
+            disruptor.getRingBuffer().publishEvent(translator, logEvent, 
asyncLoggerConfig);
         } catch (NullPointerException npe) {
             LOGGER.fatal("Ignoring log event after log4j was shut down.");
         }

Modified: 
logging/log4j/log4j2/trunk/log4j-core/src/main/java/org/apache/logging/log4j/core/async/RingBufferLogEvent.java
URL: 
http://svn.apache.org/viewvc/logging/log4j/log4j2/trunk/log4j-core/src/main/java/org/apache/logging/log4j/core/async/RingBufferLogEvent.java?rev=1602578&r1=1602577&r2=1602578&view=diff
==============================================================================
--- 
logging/log4j/log4j2/trunk/log4j-core/src/main/java/org/apache/logging/log4j/core/async/RingBufferLogEvent.java
 (original)
+++ 
logging/log4j/log4j2/trunk/log4j-core/src/main/java/org/apache/logging/log4j/core/async/RingBufferLogEvent.java
 Sat Jun 14 11:34:30 2014
@@ -25,6 +25,7 @@ import org.apache.logging.log4j.Marker;
 import org.apache.logging.log4j.ThreadContext.ContextStack;
 import org.apache.logging.log4j.core.LogEvent;
 import org.apache.logging.log4j.core.config.Property;
+import org.apache.logging.log4j.core.impl.Log4jLogEvent;
 import org.apache.logging.log4j.core.impl.ThrowableProxy;
 import org.apache.logging.log4j.core.lookup.StrSubstitutor;
 import org.apache.logging.log4j.message.Message;
@@ -35,135 +36,128 @@ import org.apache.logging.log4j.util.Str
 import com.lmax.disruptor.EventFactory;
 
 /**
- * When the Disruptor is started, the RingBuffer is populated with event
- * objects. These objects are then re-used during the life of the RingBuffer.
+ * When the Disruptor is started, the RingBuffer is populated with event 
objects. These objects are then re-used during
+ * the life of the RingBuffer.
  */
 public class RingBufferLogEvent implements LogEvent {
-       private static final long serialVersionUID = 8462119088943934758L;
+    private static final long serialVersionUID = 8462119088943934758L;
 
-       /**
-        * Creates the events that will be put in the RingBuffer.
-        */
-       private static class Factory implements 
EventFactory<RingBufferLogEvent> {
-
-               @Override
-               public RingBufferLogEvent newInstance() {
-                       return new RingBufferLogEvent();
-               }
-       }
-
-       /** The {@code EventFactory} for {@code RingBufferLogEvent}s. */
-       public static final Factory FACTORY = new Factory();
-
-       private AsyncLogger asyncLogger;
-       private String loggerName;
-       private Marker marker;
-       private String fqcn;
-       private Level level;
-       private Message message;
-       private transient Throwable thrown;
+    /**
+     * Creates the events that will be put in the RingBuffer.
+     */
+    private static class Factory implements EventFactory<RingBufferLogEvent> {
+
+        @Override
+        public RingBufferLogEvent newInstance() {
+            return new RingBufferLogEvent();
+        }
+    }
+
+    /** The {@code EventFactory} for {@code RingBufferLogEvent}s. */
+    public static final Factory FACTORY = new Factory();
+
+    private AsyncLogger asyncLogger;
+    private String loggerName;
+    private Marker marker;
+    private String fqcn;
+    private Level level;
+    private Message message;
+    private transient Throwable thrown;
     private ThrowableProxy thrownProxy;
-       private Map<String, String> contextMap;
-       private ContextStack contextStack;
-       private String threadName;
-       private StackTraceElement location;
-       private long currentTimeMillis;
-       private boolean endOfBatch;
-       private boolean includeLocation;
-
-       public void setValues(final AsyncLogger asyncLogger,
-                       final String loggerName, final Marker marker, final 
String fqcn,
-                       final Level level, final Message data, final Throwable 
throwable,
-                       final Map<String, String> map, final ContextStack 
contextStack,
-                       final String threadName, final StackTraceElement 
location,
-                       final long currentTimeMillis) {
-               this.asyncLogger = asyncLogger;
-               this.loggerName = loggerName;
-               this.marker = marker;
-               this.fqcn = fqcn;
-               this.level = level;
-               this.message = data;
-               this.thrown = throwable;
-               this.thrownProxy = null;
-               this.contextMap = map;
-               this.contextStack = contextStack;
-               this.threadName = threadName;
-               this.location = location;
-               this.currentTimeMillis = currentTimeMillis;
-       }
-
-       /**
-        * Event processor that reads the event from the ringbuffer can call 
this
-        * method.
-        * 
-        * @param endOfBatch
-        *            flag to indicate if this is the last event in a batch 
from the
-        *            RingBuffer
-        */
-       public void execute(final boolean endOfBatch) {
-               this.endOfBatch = endOfBatch;
-               asyncLogger.actualAsyncLog(this);
-       }
-
-       /**
-        * Returns {@code true} if this event is the end of a batch, {@code 
false}
-        * otherwise.
-        * 
-        * @return {@code true} if this event is the end of a batch, {@code 
false}
-        *         otherwise
-        */
-       @Override
-       public boolean isEndOfBatch() {
-               return endOfBatch;
-       }
-
-       @Override
-       public void setEndOfBatch(final boolean endOfBatch) {
-               this.endOfBatch = endOfBatch;
-       }
-
-       @Override
-       public boolean isIncludeLocation() {
-               return includeLocation;
-       }
-
-       @Override
-       public void setIncludeLocation(final boolean includeLocation) {
-               this.includeLocation = includeLocation;
-       }
-
-       @Override
-       public String getLoggerName() {
-               return loggerName;
-       }
-
-       @Override
-       public Marker getMarker() {
-               return marker;
-       }
-
-       @Override
-       public String getLoggerFqcn() {
-               return fqcn;
-       }
-
-       @Override
-       public Level getLevel() {
-               if (level == null) {
-                       level = Level.OFF; // LOG4J2-462, LOG4J2-465
-               }
-               return level;
-       }
-
-       @Override
-       public Message getMessage() {
-               if (message == null) {
-                       message = new SimpleMessage(Strings.EMPTY);
-               }
-               return message;
-       }
+    private Map<String, String> contextMap;
+    private ContextStack contextStack;
+    private String threadName;
+    private StackTraceElement location;
+    private long currentTimeMillis;
+    private boolean endOfBatch;
+    private boolean includeLocation;
+
+    public void setValues(final AsyncLogger asyncLogger, final String 
loggerName, final Marker marker,
+            final String fqcn, final Level level, final Message data, final 
Throwable throwable,
+            final Map<String, String> map, final ContextStack contextStack, 
final String threadName,
+            final StackTraceElement location, final long currentTimeMillis) {
+        this.asyncLogger = asyncLogger;
+        this.loggerName = loggerName;
+        this.marker = marker;
+        this.fqcn = fqcn;
+        this.level = level;
+        this.message = data;
+        this.thrown = throwable;
+        this.thrownProxy = null;
+        this.contextMap = map;
+        this.contextStack = contextStack;
+        this.threadName = threadName;
+        this.location = location;
+        this.currentTimeMillis = currentTimeMillis;
+    }
+
+    /**
+     * Event processor that reads the event from the ringbuffer can call this 
method.
+     * 
+     * @param endOfBatch flag to indicate if this is the last event in a batch 
from the RingBuffer
+     */
+    public void execute(final boolean endOfBatch) {
+        this.endOfBatch = endOfBatch;
+        asyncLogger.actualAsyncLog(this);
+    }
+
+    /**
+     * Returns {@code true} if this event is the end of a batch, {@code false} 
otherwise.
+     * 
+     * @return {@code true} if this event is the end of a batch, {@code false} 
otherwise
+     */
+    @Override
+    public boolean isEndOfBatch() {
+        return endOfBatch;
+    }
+
+    @Override
+    public void setEndOfBatch(final boolean endOfBatch) {
+        this.endOfBatch = endOfBatch;
+    }
+
+    @Override
+    public boolean isIncludeLocation() {
+        return includeLocation;
+    }
+
+    @Override
+    public void setIncludeLocation(final boolean includeLocation) {
+        this.includeLocation = includeLocation;
+    }
+
+    @Override
+    public String getLoggerName() {
+        return loggerName;
+    }
+
+    @Override
+    public Marker getMarker() {
+        return marker;
+    }
+
+    @Override
+    public String getLoggerFqcn() {
+        return fqcn;
+    }
+
+    @Override
+    public Level getLevel() {
+        if (level == null) {
+            level = Level.OFF; // LOG4J2-462, LOG4J2-465
+        }
+        return level;
+    }
+
+    @Override
+    public Message getMessage() {
+        if (message == null) {
+            message = new SimpleMessage(Strings.EMPTY);
+        }
+        return message;
+    }
 
-       @Override
+    @Override
     public Throwable getThrown() {
         // after deserialization, thrown is null but thrownProxy may be 
non-null
         if (thrown == null) {
@@ -174,100 +168,107 @@ public class RingBufferLogEvent implemen
         return thrown;
     }
 
-       @Override
-       public ThrowableProxy getThrownProxy() {
-           // lazily instantiate the (expensive) ThrowableProxy
-           if (thrownProxy == null) {
-               if (thrown != null) {
-                   thrownProxy = new ThrowableProxy(thrown);
-               }
-           }
-               return this.thrownProxy;
-       }
-
-       @Override
-       public Map<String, String> getContextMap() {
-               return contextMap;
-       }
-
-       @Override
-       public ContextStack getContextStack() {
-               return contextStack;
-       }
-
-       @Override
-       public String getThreadName() {
-               return threadName;
-       }
-
-       @Override
-       public StackTraceElement getSource() {
-               return location;
-       }
-
-       @Override
-       public long getTimeMillis() {
-               Message msg = getMessage();
-               if (msg instanceof TimestampMessage) { // LOG4J2-455
-                       return ((TimestampMessage) msg).getTimestamp();
-               }
-               return currentTimeMillis;
-       }
-
-       /**
-        * Merges the contents of the specified map into the contextMap, after
-        * replacing any variables in the property values with the
-        * StrSubstitutor-supplied actual values.
-        * 
-        * @param properties
-        *            configured properties
-        * @param strSubstitutor
-        *            used to lookup values of variables in properties
-        */
-       public void mergePropertiesIntoContextMap(
-                       final Map<Property, Boolean> properties,
-                       final StrSubstitutor strSubstitutor) {
-               if (properties == null) {
-                       return; // nothing to do
-               }
-
-               final Map<String, String> map = contextMap == null ? new 
HashMap<String, String>()
-                               : new HashMap<String, String>(contextMap);
-
-               for (final Map.Entry<Property, Boolean> entry : 
properties.entrySet()) {
-                       final Property prop = entry.getKey();
-                       if (map.containsKey(prop.getName())) {
-                               continue; // contextMap overrides config 
properties
-                       }
-                       final String value = entry.getValue().booleanValue() ? 
strSubstitutor.replace(prop
-                                       .getValue()) : prop.getValue();
-                       map.put(prop.getName(), value);
-               }
-               contextMap = map;
-       }
-
-       /**
-        * Release references held by ring buffer to allow objects to be
-        * garbage-collected.
-        */
-       public void clear() {
-               setValues(null, // asyncLogger
-                               null, // loggerName
-                               null, // marker
-                               null, // fqcn
-                               null, // level
-                               null, // data
-                               null, // t
-                               null, // map
-                               null, // contextStack
-                               null, // threadName
-                               null, // location
-                               0 // currentTimeMillis
-               );
-       }
-       
-       private void writeObject(java.io.ObjectOutputStream out) throws 
IOException {
-           getThrownProxy(); // initialize the ThrowableProxy before 
serializing
-           out.defaultWriteObject();
-       }
+    @Override
+    public ThrowableProxy getThrownProxy() {
+        // lazily instantiate the (expensive) ThrowableProxy
+        if (thrownProxy == null) {
+            if (thrown != null) {
+                thrownProxy = new ThrowableProxy(thrown);
+            }
+        }
+        return this.thrownProxy;
+    }
+
+    @Override
+    public Map<String, String> getContextMap() {
+        return contextMap;
+    }
+
+    @Override
+    public ContextStack getContextStack() {
+        return contextStack;
+    }
+
+    @Override
+    public String getThreadName() {
+        return threadName;
+    }
+
+    @Override
+    public StackTraceElement getSource() {
+        return location;
+    }
+
+    @Override
+    public long getTimeMillis() {
+        Message msg = getMessage();
+        if (msg instanceof TimestampMessage) { // LOG4J2-455
+            return ((TimestampMessage) msg).getTimestamp();
+        }
+        return currentTimeMillis;
+    }
+
+    /**
+     * Merges the contents of the specified map into the contextMap, after 
replacing any variables in the property
+     * values with the StrSubstitutor-supplied actual values.
+     * 
+     * @param properties configured properties
+     * @param strSubstitutor used to lookup values of variables in properties
+     */
+    public void mergePropertiesIntoContextMap(final Map<Property, Boolean> 
properties,
+            final StrSubstitutor strSubstitutor) {
+        if (properties == null) {
+            return; // nothing to do
+        }
+
+        final Map<String, String> map = contextMap == null ? new 
HashMap<String, String>()
+                : new HashMap<String, String>(contextMap);
+
+        for (final Map.Entry<Property, Boolean> entry : properties.entrySet()) 
{
+            final Property prop = entry.getKey();
+            if (map.containsKey(prop.getName())) {
+                continue; // contextMap overrides config properties
+            }
+            final String value = entry.getValue().booleanValue() ? 
strSubstitutor.replace(prop.getValue()) : prop
+                    .getValue();
+            map.put(prop.getName(), value);
+        }
+        contextMap = map;
+    }
+
+    /**
+     * Release references held by ring buffer to allow objects to be 
garbage-collected.
+     */
+    public void clear() {
+        setValues(null, // asyncLogger
+                null, // loggerName
+                null, // marker
+                null, // fqcn
+                null, // level
+                null, // data
+                null, // t
+                null, // map
+                null, // contextStack
+                null, // threadName
+                null, // location
+                0 // currentTimeMillis
+        );
+    }
+
+    private void writeObject(java.io.ObjectOutputStream out) throws 
IOException {
+        getThrownProxy(); // initialize the ThrowableProxy before serializing
+        out.defaultWriteObject();
+    }
+
+    /**
+     * Creates and returns a new immutable copy of this {@code 
RingBufferLogEvent}.
+     * 
+     * @return a new immutable copy of the data in this {@code 
RingBufferLogEvent}
+     */
+    public LogEvent createMemento() {
+        // Ideally, would like to use the LogEventFactory here but signature 
does not match:
+        // results in factory re-creating the timestamp, context map and 
context stack, which we don't want.
+        return new Log4jLogEvent(loggerName, marker, fqcn, level, message, 
thrown, contextMap, contextStack,
+                threadName, location, currentTimeMillis);
+    }
 }

Added: 
logging/log4j/log4j2/trunk/log4j-core/src/test/java/org/apache/logging/log4j/core/async/AsyncLoggersWithAsyncLoggerConfigTest.java
URL: 
http://svn.apache.org/viewvc/logging/log4j/log4j2/trunk/log4j-core/src/test/java/org/apache/logging/log4j/core/async/AsyncLoggersWithAsyncLoggerConfigTest.java?rev=1602578&view=auto
==============================================================================
--- 
logging/log4j/log4j2/trunk/log4j-core/src/test/java/org/apache/logging/log4j/core/async/AsyncLoggersWithAsyncLoggerConfigTest.java
 (added)
+++ 
logging/log4j/log4j2/trunk/log4j-core/src/test/java/org/apache/logging/log4j/core/async/AsyncLoggersWithAsyncLoggerConfigTest.java
 Sat Jun 14 11:34:30 2014
@@ -0,0 +1,74 @@
+/*
+ * 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
+ *
+ *      http://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.
+ */
+package org.apache.logging.log4j.core.async;
+
+import java.util.List;
+
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.apache.logging.log4j.core.LoggerContext;
+import org.apache.logging.log4j.core.config.Configuration;
+import org.apache.logging.log4j.core.config.ConfigurationFactory;
+import org.apache.logging.log4j.core.util.Constants;
+import org.apache.logging.log4j.status.StatusLogger;
+import org.apache.logging.log4j.test.appender.ListAppender;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+public class AsyncLoggersWithAsyncLoggerConfigTest {
+    private static Configuration config;
+    private static ListAppender listAppender;
+    private static LoggerContext ctx;
+
+    @BeforeClass
+    public static void beforeClass() {
+        System.setProperty(ConfigurationFactory.CONFIGURATION_FILE_PROPERTY,
+                "AsyncLoggersWithAsyncLoggerConfigTest.xml");
+        System.setProperty(Constants.LOG4J_CONTEXT_SELECTOR, 
AsyncLoggerContextSelector.class.getName());
+        ctx = (LoggerContext) LogManager.getContext(false);
+        config = ctx.getConfiguration();
+        listAppender = (ListAppender) config.getAppender("List");
+    }
+
+    @AfterClass
+    public static void cleanupClass() {
+        System.clearProperty(ConfigurationFactory.CONFIGURATION_FILE_PROPERTY);
+        System.clearProperty(Constants.LOG4J_CONTEXT_SELECTOR);
+        ctx.reconfigure();
+        StatusLogger.getLogger().reset();
+    }
+
+    @Test
+    public void testLoggingWorks() throws Exception {        
+        final Logger logger = LogManager.getLogger();
+        logger.error("This is a test");
+        logger.warn("Hello world!");
+        Thread.sleep(100);
+        final List<String> list = listAppender.getMessages();
+        assertNotNull("No events generated", list);
+        assertTrue("Incorrect number of events. Expected 2, got " + 
list.size(), list.size() == 2);
+        String msg = list.get(0);
+        String expected = getClass().getName() + " This is a test";
+        assertTrue("Expected " + expected + ", Actual " + msg, 
expected.equals(msg));
+        msg = list.get(1);
+        expected = getClass().getName() + " Hello world!";
+        assertTrue("Expected " + expected + ", Actual " + msg, 
expected.equals(msg));
+    }
+}

Propchange: 
logging/log4j/log4j2/trunk/log4j-core/src/test/java/org/apache/logging/log4j/core/async/AsyncLoggersWithAsyncLoggerConfigTest.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: 
logging/log4j/log4j2/trunk/log4j-core/src/test/java/org/apache/logging/log4j/core/async/RingBufferLogEventTest.java
URL: 
http://svn.apache.org/viewvc/logging/log4j/log4j2/trunk/log4j-core/src/test/java/org/apache/logging/log4j/core/async/RingBufferLogEventTest.java?rev=1602578&r1=1602577&r2=1602578&view=diff
==============================================================================
--- 
logging/log4j/log4j2/trunk/log4j-core/src/test/java/org/apache/logging/log4j/core/async/RingBufferLogEventTest.java
 (original)
+++ 
logging/log4j/log4j2/trunk/log4j-core/src/test/java/org/apache/logging/log4j/core/async/RingBufferLogEventTest.java
 Sat Jun 14 11:34:30 2014
@@ -22,15 +22,20 @@ import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.io.ObjectInputStream;
 import java.io.ObjectOutputStream;
+import java.util.Arrays;
+import java.util.HashMap;
 import java.util.Map;
 
 import org.apache.logging.log4j.Level;
 import org.apache.logging.log4j.Marker;
+import org.apache.logging.log4j.MarkerManager;
 import org.apache.logging.log4j.ThreadContext.ContextStack;
+import org.apache.logging.log4j.core.LogEvent;
 import org.apache.logging.log4j.core.impl.ThrowableProxy;
 import org.apache.logging.log4j.message.Message;
 import org.apache.logging.log4j.message.SimpleMessage;
 import org.apache.logging.log4j.message.TimestampMessage;
+import org.apache.logging.log4j.spi.MutableThreadContextStack;
 import org.junit.Test;
 
 import static org.junit.Assert.*;
@@ -188,4 +193,37 @@ public class RingBufferLogEventTest {
         assertEquals(location, other.getSource());
         assertEquals(currentTimeMillis, other.getTimeMillis());
     }
+    
+    @Test
+    public void testCreateMementoReturnsCopy() {
+        RingBufferLogEvent evt = new RingBufferLogEvent();
+        String loggerName = "logger.name";
+        Marker marker = MarkerManager.getMarker("marked man");
+        String fqcn = "f.q.c.n";
+        Level level = Level.TRACE;
+        Message data = new SimpleMessage("message");
+        Throwable t = new InternalError("not a real error");
+        Map<String, String> map = new HashMap<String, String>();
+        map.put("key", "value");
+        ContextStack contextStack = new 
MutableThreadContextStack(Arrays.asList("a", "b"));
+        String threadName = "main";
+        StackTraceElement location = null;
+        long currentTimeMillis = 12345;
+        evt.setValues(null, loggerName, marker, fqcn, level, data, t, map,
+                contextStack, threadName, location, currentTimeMillis);
+        
+        LogEvent actual = evt.createMemento();
+        assertEquals(evt.getLoggerName(), actual.getLoggerName());
+        assertEquals(evt.getMarker(), actual.getMarker());
+        assertEquals(evt.getLoggerFqcn(), actual.getLoggerFqcn());
+        assertEquals(evt.getLevel(), actual.getLevel());
+        assertEquals(evt.getMessage(), actual.getMessage());
+        assertEquals(evt.getThrown(), actual.getThrown());
+        assertEquals(evt.getContextMap(), actual.getContextMap());
+        assertEquals(evt.getContextStack(), actual.getContextStack());
+        assertEquals(evt.getThreadName(), actual.getThreadName());
+        assertEquals(evt.getTimeMillis(), actual.getTimeMillis());
+        assertEquals(evt.getSource(), actual.getSource());
+        assertEquals(evt.getThrownProxy(), actual.getThrownProxy());
+    }
 }

Added: 
logging/log4j/log4j2/trunk/log4j-core/src/test/resources/AsyncLoggersWithAsyncLoggerConfigTest.xml
URL: 
http://svn.apache.org/viewvc/logging/log4j/log4j2/trunk/log4j-core/src/test/resources/AsyncLoggersWithAsyncLoggerConfigTest.xml?rev=1602578&view=auto
==============================================================================
--- 
logging/log4j/log4j2/trunk/log4j-core/src/test/resources/AsyncLoggersWithAsyncLoggerConfigTest.xml
 (added)
+++ 
logging/log4j/log4j2/trunk/log4j-core/src/test/resources/AsyncLoggersWithAsyncLoggerConfigTest.xml
 Sat Jun 14 11:34:30 2014
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Configuration status="ERROR">
+  <Appenders>
+    <List name="List">
+      <PatternLayout pattern="%c %m"/>
+    </List>
+  </Appenders>
+  
+  <Loggers>
+    <AsyncRoot level="trace">
+      <AppenderRef ref="List"/>
+    </AsyncRoot>
+  </Loggers>
+</Configuration>
\ No newline at end of file

Propchange: 
logging/log4j/log4j2/trunk/log4j-core/src/test/resources/AsyncLoggersWithAsyncLoggerConfigTest.xml
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: logging/log4j/log4j2/trunk/src/changes/changes.xml
URL: 
http://svn.apache.org/viewvc/logging/log4j/log4j2/trunk/src/changes/changes.xml?rev=1602578&r1=1602577&r2=1602578&view=diff
==============================================================================
--- logging/log4j/log4j2/trunk/src/changes/changes.xml (original)
+++ logging/log4j/log4j2/trunk/src/changes/changes.xml Sat Jun 14 11:34:30 2014
@@ -22,6 +22,9 @@
   </properties>
   <body>
     <release version="2.0-rc2" date="2014-MM-DD" description="Bug fixes and 
enhancements">
+      <action issue="LOG4J2-669" dev="rpopma" type="fix">
+        Prevent NPE when combining AsyncLoggers with AsyncLoggerConfigs.
+      </action>
       <action issue="LOG4J2-42" dev="rgoers" type="add">
         Create an appender to route log events to the ServletContext log.
       </action>


Reply via email to