diff --git a/log4j-api/src/main/java/org/apache/logging/log4j/util/SortedArrayStringMap.java b/log4j-api/src/main/java/org/apache/logging/log4j/util/SortedArrayStringMap.java index 1c1ce90..1d0f1c0 100644 --- a/log4j-api/src/main/java/org/apache/logging/log4j/util/SortedArrayStringMap.java +++ b/log4j-api/src/main/java/org/apache/logging/log4j/util/SortedArrayStringMap.java @@ -226,8 +226,11 @@ @Override public void putAll(final ReadOnlyStringMap source) { - if (source == this || source.isEmpty()) { // throw NPE if null - return; // this.putAll(this) does not modify this collection + if (source == this || source == null || source.isEmpty()) { + // this.putAll(this) does not modify this collection + // this.putAll(null) does not modify this collection + // this.putAll(empty ReadOnlyStringMap) does not modify this collection + return; } assertNotFrozen(); assertNoConcurrentModification(); diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/AbstractLogEvent.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/AbstractLogEvent.java index dddbc7f..11cc071 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/AbstractLogEvent.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/AbstractLogEvent.java @@ -35,6 +35,14 @@ private static final long serialVersionUID = 1L; + /** + * Subclasses should implement this method to provide an immutable version. + */ + @Override + public LogEvent toImmutable() { + return this; + } + @Override public ReadOnlyStringMap getContextData() { return null; diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/LogEvent.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/LogEvent.java index 1b608b9..410c4be 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/LogEvent.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/LogEvent.java @@ -23,8 +23,10 @@ import org.apache.logging.log4j.Level; import org.apache.logging.log4j.Marker; import org.apache.logging.log4j.ThreadContext; +import org.apache.logging.log4j.core.impl.Log4jLogEvent; import org.apache.logging.log4j.core.impl.ThrowableProxy; import org.apache.logging.log4j.message.Message; +import org.apache.logging.log4j.message.ReusableMessage; import org.apache.logging.log4j.util.ReadOnlyStringMap; /** @@ -49,6 +51,13 @@ public interface LogEvent extends Serializable { /** + * Returns an immutable version of this log event, which MAY BE a copy of this event. + * + * @return an immutable version of this log event + */ + LogEvent toImmutable(); + + /** * Gets the context map (also know as Mapped Diagnostic Context or MDC). * * @return The context map, never {@code null}. diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/db/jpa/AbstractLogEventWrapperEntity.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/db/jpa/AbstractLogEventWrapperEntity.java index 9485fdb..60ca692 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/db/jpa/AbstractLogEventWrapperEntity.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/db/jpa/AbstractLogEventWrapperEntity.java @@ -29,6 +29,7 @@ import org.apache.logging.log4j.util.ReadOnlyStringMap; import org.apache.logging.log4j.core.LogEvent; import org.apache.logging.log4j.core.appender.db.jpa.converter.ContextDataAttributeConverter; +import org.apache.logging.log4j.core.impl.Log4jLogEvent; import org.apache.logging.log4j.message.Message; /** @@ -96,6 +97,11 @@ this.wrappedEvent = wrappedEvent; } + @Override + public LogEvent toImmutable() { + return Log4jLogEvent.createMemento(this); + } + /** * All eventual accessor methods must call this method and delegate the method call to the underlying wrapped event. * Annotated {@link Transient} so as not to be included in the persisted entity. diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/async/RingBufferLogEvent.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/async/RingBufferLogEvent.java index 8acf3eb..6766f21 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/async/RingBufferLogEvent.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/async/RingBufferLogEvent.java @@ -114,6 +114,11 @@ this.asyncLogger = anAsyncLogger; } + @Override + public LogEvent toImmutable() { + return createMemento(); + } + private void setMessage(final Message msg) { if (msg instanceof ReusableMessage) { final ReusableMessage reusable = (ReusableMessage) msg; @@ -429,8 +434,8 @@ * @return a new immutable copy of the data in this {@code RingBufferLogEvent} */ public LogEvent createMemento() { - final LogEvent result = new Log4jLogEvent.Builder(this).build(); - return result; + return new Log4jLogEvent.Builder(this).build(); + } /** diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/Log4jLogEvent.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/Log4jLogEvent.java index 1b7acb2..3cf7694 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/Log4jLogEvent.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/Log4jLogEvent.java @@ -470,6 +470,13 @@ return new Builder(this); } + public Log4jLogEvent toImmutable() { + if (getMessage() instanceof ReusableMessage) { + makeMessageImmutable(); + } + return this; + } + /** * Returns the logging Level. * @return the Level associated with this event. @@ -727,13 +734,20 @@ throw new InvalidObjectException("Proxy required"); } + public LogEvent createMemento() { + return createMemento(this); + } + + public static LogEvent createMemento(final LogEvent logEvent) { + return new Log4jLogEvent.Builder(logEvent).build(); + } + /** * Creates and returns a new immutable copy of this {@code Log4jLogEvent}. * * @return a new immutable copy of the data in this {@code Log4jLogEvent} */ public static Log4jLogEvent createMemento(final LogEvent event, final boolean includeLocation) { - // TODO implement Log4jLogEvent.createMemento() return deserialize(serialize(event, includeLocation)); } diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/MutableLogEvent.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/MutableLogEvent.java index 4ace6cf..3b96f7a 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/MutableLogEvent.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/MutableLogEvent.java @@ -73,6 +73,10 @@ this.parameters = replacementParameters; } + public Log4jLogEvent toImmutable() { + return createMemento(); + } + /** * Initialize the fields of this {@code MutableLogEvent} from another event. * Similar in purpose and usage as {@link org.apache.logging.log4j.core.impl.Log4jLogEvent.LogEventProxy}, diff --git a/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/db/jpa/LogEventTest.java b/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/db/jpa/LogEventTest.java new file mode 100644 index 0000000..5ab1e87 --- /dev/null +++ b/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/db/jpa/LogEventTest.java @@ -0,0 +1,122 @@ +/* + * 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.appender.db.jpa; + +import java.util.Map; + +import org.apache.logging.log4j.Level; +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.impl.ThrowableProxy; +import org.apache.logging.log4j.message.Message; +import org.junit.Assert; +import org.junit.Test; + +public class LogEventTest { + + @Test + public void testToImmutable_AbstractLogEventWrapperEntity() { + final LogEvent logEvent = new AbstractLogEventWrapperEntity() { + + private static final long serialVersionUID = 1L; + + @Override + public Map getContextMap() { + return null; + } + + @Override + public ContextStack getContextStack() { + return null; + } + + @Override + public Level getLevel() { + return null; + } + + @Override + public String getLoggerFqcn() { + return null; + } + + @Override + public String getLoggerName() { + return null; + } + + @Override + public Marker getMarker() { + return null; + } + + @Override + public Message getMessage() { + return null; + } + + @Override + public long getNanoTime() { + return 0; + } + + @Override + public StackTraceElement getSource() { + return null; + } + + @Override + public long getThreadId() { + return 0; + } + + @Override + public String getThreadName() { + return null; + } + + @Override + public int getThreadPriority() { + return 0; + } + + @Override + public Throwable getThrown() { + return null; + } + + @Override + public ThrowableProxy getThrownProxy() { + return null; + } + + @Override + public long getTimeMillis() { + return 0; + } + }; + Assert.assertNotSame(logEvent, logEvent.toImmutable()); + } + + @Test + public void testToImmutable_TestBaseEntity() { + final LogEvent logEvent = new TestBaseEntity(); + Assert.assertNotSame(logEvent, logEvent.toImmutable()); + } +} diff --git a/log4j-core/src/test/java/org/apache/logging/log4j/core/async/RingBufferLogEventTest.java b/log4j-core/src/test/java/org/apache/logging/log4j/core/async/RingBufferLogEventTest.java index 0a86155..7619b88 100644 --- a/log4j-core/src/test/java/org/apache/logging/log4j/core/async/RingBufferLogEventTest.java +++ b/log4j-core/src/test/java/org/apache/logging/log4j/core/async/RingBufferLogEventTest.java @@ -30,11 +30,13 @@ import org.apache.logging.log4j.ThreadContext.ContextStack; import org.apache.logging.log4j.categories.AsyncLoggers; import org.apache.logging.log4j.core.LogEvent; +import org.apache.logging.log4j.core.appender.db.jpa.TestBaseEntity; import org.apache.logging.log4j.util.StringMap; 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.spi.MutableThreadContextStack; +import org.junit.Assert; import org.junit.Test; import org.junit.experimental.categories.Category; @@ -47,6 +49,12 @@ public class RingBufferLogEventTest { @Test + public void testToImmutable() { + final LogEvent logEvent = new RingBufferLogEvent(); + Assert.assertNotSame(logEvent, logEvent.toImmutable()); + } + + @Test public void testGetLevelReturnsOffIfNullLevelSet() { final RingBufferLogEvent evt = new RingBufferLogEvent(); final String loggerName = null; diff --git a/log4j-core/src/test/java/org/apache/logging/log4j/core/impl/Log4jLogEventTest.java b/log4j-core/src/test/java/org/apache/logging/log4j/core/impl/Log4jLogEventTest.java index 1acf0b1..09a7ec3 100644 --- a/log4j-core/src/test/java/org/apache/logging/log4j/core/impl/Log4jLogEventTest.java +++ b/log4j-core/src/test/java/org/apache/logging/log4j/core/impl/Log4jLogEventTest.java @@ -16,6 +16,14 @@ */ package org.apache.logging.log4j.core.impl; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; @@ -25,6 +33,7 @@ import java.util.Collections; import java.util.HashMap; import java.util.Map; + import javax.xml.bind.DatatypeConverter; import org.apache.logging.log4j.Level; @@ -39,15 +48,16 @@ import org.apache.logging.log4j.core.util.DummyNanoClock; import org.apache.logging.log4j.message.Message; import org.apache.logging.log4j.message.ObjectMessage; +import org.apache.logging.log4j.message.ReusableMessage; +import org.apache.logging.log4j.message.ReusableObjectMessage; import org.apache.logging.log4j.message.SimpleMessage; import org.apache.logging.log4j.util.SortedArrayStringMap; import org.apache.logging.log4j.util.StringMap; import org.apache.logging.log4j.util.Strings; import org.junit.AfterClass; +import org.junit.Assert; import org.junit.BeforeClass; import org.junit.Test; - -import static org.junit.Assert.*; public class Log4jLogEventTest { @@ -77,6 +87,20 @@ } @Test + public void testToImmutableSame() { + final LogEvent logEvent = new Log4jLogEvent(); + Assert.assertSame(logEvent, logEvent.toImmutable()); + } + + @Test + public void testToImmutableNotSame() { + final LogEvent logEvent = new Log4jLogEvent.Builder().setMessage(new ReusableObjectMessage()).build(); + LogEvent immutable = logEvent.toImmutable(); + Assert.assertSame(logEvent, immutable); + Assert.assertFalse(immutable.getMessage() instanceof ReusableMessage); + } + + @Test public void testJavaIoSerializable() throws Exception { final Log4jLogEvent evt = Log4jLogEvent.newBuilder() // .setLoggerName("some.test") // diff --git a/log4j-core/src/test/java/org/apache/logging/log4j/core/impl/MutableLogEventTest.java b/log4j-core/src/test/java/org/apache/logging/log4j/core/impl/MutableLogEventTest.java index 1d4b58f..348e7bd 100644 --- a/log4j-core/src/test/java/org/apache/logging/log4j/core/impl/MutableLogEventTest.java +++ b/log4j-core/src/test/java/org/apache/logging/log4j/core/impl/MutableLogEventTest.java @@ -27,11 +27,14 @@ import org.apache.logging.log4j.Level; import org.apache.logging.log4j.MarkerManager; import org.apache.logging.log4j.ThreadContext; +import org.apache.logging.log4j.core.LogEvent; +import org.apache.logging.log4j.core.async.RingBufferLogEvent; import org.apache.logging.log4j.message.ParameterizedMessage; import org.apache.logging.log4j.message.SimpleMessage; import org.apache.logging.log4j.util.SortedArrayStringMap; import org.apache.logging.log4j.util.StringMap; import org.apache.logging.log4j.spi.MutableThreadContextStack; +import org.junit.Assert; import org.junit.Test; import static org.junit.Assert.*; @@ -51,6 +54,12 @@ } @Test + public void testToImmutable() { + final LogEvent logEvent = new MutableLogEvent(); + Assert.assertNotSame(logEvent, logEvent.toImmutable()); + } + + @Test public void testInitFromCopiesAllFields() { // private ThrowableProxy thrownProxy; final Log4jLogEvent source = Log4jLogEvent.newBuilder() // diff --git a/log4j-flume-ng/src/main/java/org/apache/logging/log4j/flume/appender/FlumeEvent.java b/log4j-flume-ng/src/main/java/org/apache/logging/log4j/flume/appender/FlumeEvent.java index 39ba774..a1059df 100644 --- a/log4j-flume-ng/src/main/java/org/apache/logging/log4j/flume/appender/FlumeEvent.java +++ b/log4j-flume-ng/src/main/java/org/apache/logging/log4j/flume/appender/FlumeEvent.java @@ -31,6 +31,7 @@ import org.apache.logging.log4j.ThreadContext; import org.apache.logging.log4j.util.ReadOnlyStringMap; import org.apache.logging.log4j.core.LogEvent; +import org.apache.logging.log4j.core.impl.Log4jLogEvent; import org.apache.logging.log4j.core.impl.ThrowableProxy; import org.apache.logging.log4j.core.util.Patterns; import org.apache.logging.log4j.core.util.UuidUtil; @@ -170,6 +171,11 @@ context.clear(); context.putAll(map); } + + @Override + public LogEvent toImmutable() { + return Log4jLogEvent.createMemento(this); + } /** * Set the body in the event. @@ -358,4 +364,5 @@ public void setEndOfBatch(final boolean endOfBatch) { event.setEndOfBatch(endOfBatch); } + }