You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@logging.apache.org by rp...@apache.org on 2014/06/14 13:34:30 UTC
svn commit: r1602578 - in /logging/log4j/log4j2/trunk:
log4j-core/src/main/java/org/apache/logging/log4j/core/async/
log4j-core/src/test/java/org/apache/logging/log4j/core/async/
log4j-core/src/test/resources/ src/changes/
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>