diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/rewrite/MapRewritePolicy.java b/log4j-1.2-api/src/main/java/org/apache/log4j/rewrite/MapRewritePolicy.java index f62f9fb0f6c..5841920d674 100644 --- a/log4j-1.2-api/src/main/java/org/apache/log4j/rewrite/MapRewritePolicy.java +++ b/log4j-1.2-api/src/main/java/org/apache/log4j/rewrite/MapRewritePolicy.java @@ -105,7 +105,6 @@ public LoggingEvent rewrite(final LoggingEvent source) { .setThrown(source.getThrowableInformation().getThrowable()) .setTimeMillis(source.getTimeStamp()) .setNanoTime(0) - .setThrownProxy(null) .build(); } return new LogEventAdapter(event); diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/rewrite/PropertyRewritePolicy.java b/log4j-1.2-api/src/main/java/org/apache/log4j/rewrite/PropertyRewritePolicy.java index fd99674f405..1fd4c9fcd59 100644 --- a/log4j-1.2-api/src/main/java/org/apache/log4j/rewrite/PropertyRewritePolicy.java +++ b/log4j-1.2-api/src/main/java/org/apache/log4j/rewrite/PropertyRewritePolicy.java @@ -110,7 +110,6 @@ public LoggingEvent rewrite(final LoggingEvent source) { .setThrown(source.getThrowableInformation().getThrowable()) .setTimeMillis(source.getTimeStamp()) .setNanoTime(0) - .setThrownProxy(null) .build(); } return new LogEventAdapter(event); diff --git a/log4j-api/src/main/java/org/apache/logging/log4j/message/ReusableObjectMessage.java b/log4j-api/src/main/java/org/apache/logging/log4j/message/ReusableObjectMessage.java index 08c86259358..0eaad81e380 100644 --- a/log4j-api/src/main/java/org/apache/logging/log4j/message/ReusableObjectMessage.java +++ b/log4j-api/src/main/java/org/apache/logging/log4j/message/ReusableObjectMessage.java @@ -128,7 +128,9 @@ public void forEachParameter(final ParameterConsumer action, final S stat @Override public Message memento() { - return new ObjectMessage(obj); + Message message = new ObjectMessage(obj); + message.getFormattedMessage(); + return message; } @Override diff --git a/log4j-api/src/main/java/org/apache/logging/log4j/message/ReusableParameterizedMessage.java b/log4j-api/src/main/java/org/apache/logging/log4j/message/ReusableParameterizedMessage.java index 053a5bf8f1c..c69996bd925 100644 --- a/log4j-api/src/main/java/org/apache/logging/log4j/message/ReusableParameterizedMessage.java +++ b/log4j-api/src/main/java/org/apache/logging/log4j/message/ReusableParameterizedMessage.java @@ -114,7 +114,9 @@ public void forEachParameter(final ParameterConsumer action, final S stat @Override public Message memento() { - return new ParameterizedMessage(messagePattern, getTrimmedParams()); + Message message = new ParameterizedMessage(messagePattern, getTrimmedParams()); + message.getFormattedMessage(); + return message; } private void init(final String messagePattern, final int argCount, final Object[] args) { diff --git a/log4j-api/src/main/java/org/apache/logging/log4j/message/ReusableSimpleMessage.java b/log4j-api/src/main/java/org/apache/logging/log4j/message/ReusableSimpleMessage.java index a2722709c93..fca89cb1807 100644 --- a/log4j-api/src/main/java/org/apache/logging/log4j/message/ReusableSimpleMessage.java +++ b/log4j-api/src/main/java/org/apache/logging/log4j/message/ReusableSimpleMessage.java @@ -85,7 +85,9 @@ public void forEachParameter(final ParameterConsumer action, final S stat @Override public Message memento() { - return new SimpleMessage(charSequence); + SimpleMessage message = new SimpleMessage(charSequence); + message.getFormattedMessage(); + return message; } // CharSequence impl diff --git a/log4j-core-test/src/main/java/org/apache/logging/log4j/core/test/appender/ListAppender.java b/log4j-core-test/src/main/java/org/apache/logging/log4j/core/test/appender/ListAppender.java index 95c9fae1956..f941db12674 100644 --- a/log4j-core-test/src/main/java/org/apache/logging/log4j/core/test/appender/ListAppender.java +++ b/log4j-core-test/src/main/java/org/apache/logging/log4j/core/test/appender/ListAppender.java @@ -35,7 +35,6 @@ import org.apache.logging.log4j.core.config.plugins.PluginBuilderFactory; import org.apache.logging.log4j.core.config.plugins.PluginElement; import org.apache.logging.log4j.core.config.plugins.validation.constraints.Required; -import org.apache.logging.log4j.core.impl.MutableLogEvent; import org.apache.logging.log4j.core.layout.SerializedLayout; import org.awaitility.Awaitility; @@ -121,12 +120,7 @@ public ListAppender( public void append(final LogEvent event) { final Layout layout = getLayout(); if (layout == null) { - if (event instanceof MutableLogEvent) { - // must take snapshot or subsequent calls to logger.log() will modify this event - events.add(((MutableLogEvent) event).createMemento()); - } else { - events.add(event); - } + events.add(event.toImmutable()); } else if (layout instanceof SerializedLayout) { final byte[] header = layout.getHeader(); final byte[] content = layout.toByteArray(event); diff --git a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/async/BlockingAppender.java b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/async/BlockingAppender.java index ff45e5ce67d..1185b5a7589 100644 --- a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/async/BlockingAppender.java +++ b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/async/BlockingAppender.java @@ -33,7 +33,6 @@ import org.apache.logging.log4j.core.config.plugins.PluginElement; import org.apache.logging.log4j.core.config.plugins.PluginFactory; import org.apache.logging.log4j.core.config.plugins.validation.constraints.Required; -import org.apache.logging.log4j.core.impl.Log4jLogEvent; /** * Appender that can be halted and resumed, for testing queue-full scenarios. @@ -58,7 +57,7 @@ public void append(final LogEvent event) { // may be a reusable event, make a copy, don't keep a reference to the original event final List events = logEvents; if (events != null) { - events.add(Log4jLogEvent.createMemento(event)); + events.add(event.toImmutable()); } if (countDownLatch == null) { diff --git a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/async/RingBufferLogEventTest.java b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/async/RingBufferLogEventTest.java index ffd26bed644..922eaccebc3 100644 --- a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/async/RingBufferLogEventTest.java +++ b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/async/RingBufferLogEventTest.java @@ -29,6 +29,7 @@ import org.apache.logging.log4j.Level; import org.apache.logging.log4j.Marker; import org.apache.logging.log4j.MarkerManager; +import org.apache.logging.log4j.ThreadContext; import org.apache.logging.log4j.ThreadContext.ContextStack; import org.apache.logging.log4j.core.LogEvent; import org.apache.logging.log4j.core.impl.ThrowableProxy; @@ -202,7 +203,7 @@ void testSerializationDeserialization() throws IOException, ClassNotFoundExcepti final Level level = Level.TRACE; final Message data = new SimpleMessage("message"); final Throwable t = new InternalError("not a real error"); - final ContextStack contextStack = null; + final ContextStack contextStack = ThreadContext.getImmutableStack(); final String threadName = "main"; final StackTraceElement location = null; evt.setValues( @@ -223,25 +224,31 @@ void testSerializationDeserialization() throws IOException, ClassNotFoundExcepti new DummyNanoClock(1)); ((StringMap) evt.getContextData()).putValue("key", "value"); - final RingBufferLogEvent other = SerialUtil.deserialize(SerialUtil.serialize(evt)); - assertThat(other.getLoggerName()).isEqualTo(loggerName); - assertThat(other.getMarker()).isEqualTo(marker); - assertThat(other.getLoggerFqcn()).isEqualTo(fqcn); - assertThat(other.getLevel()).isEqualTo(level); - assertThat(other.getMessage()).isEqualTo(data); - assertThat(other.getThrown()).isNull(); - assertThat(other.getThrownProxy()).isEqualTo(new ThrowableProxy(t)); - assertThat(other.getContextData()).isEqualTo(evt.getContextData()); - assertThat(other.getContextStack()).isEqualTo(contextStack); - assertThat(other.getThreadName()).isEqualTo(threadName); - assertThat(other.getSource()).isEqualTo(location); - assertThat(other.getTimeMillis()).isEqualTo(12345); - assertThat(other.getInstant().getNanoOfMillisecond()).isEqualTo(678); + final LogEvent other = SerialUtil.deserialize(SerialUtil.serialize(evt)); + assertThat(other.getLoggerName()).as("Logger name").isEqualTo(loggerName); + assertThat(other.getMarker()).as("Marker").isEqualTo(marker); + assertThat(other.getLoggerFqcn()) + .as("Fully qualified class name of logger implementation") + .isEqualTo(fqcn); + assertThat(other.getLevel()).as("Log event level").isEqualTo(level); + assertThat(other.getMessage()).as("Log event message").isEqualTo(data); + assertThat(other.getThrown()).as("Thrown exception").isNull(); + assertThat(other.getThrownProxy()) + .as("Serialization proxy for thrown exception") + .isEqualTo(new ThrowableProxy(t)); + assertThat(other.getContextData()).as("Context data map").isEqualTo(evt.getContextData()); + assertThat(other.getContextStack()).as("Context data stack").isEqualTo(contextStack); + assertThat(other.getThreadName()).as("Thread name").isEqualTo(threadName); + assertThat(other.getSource()).as("Log event location").isEqualTo(location); + assertThat(other.getTimeMillis()).as("Log event timestamp in millis").isEqualTo(12345); + assertThat(other.getInstant().getNanoOfMillisecond()) + .as("Log event timestamp in nanos of millis") + .isEqualTo(678); } @SuppressWarnings("deprecation") @Test - void testCreateMementoReturnsCopy() { + void testToImmutableReturnsCopy() { final RingBufferLogEvent evt = new RingBufferLogEvent(); final String loggerName = "logger.name"; final Marker marker = MarkerManager.getMarker("marked man"); @@ -270,7 +277,7 @@ void testCreateMementoReturnsCopy() { new DummyNanoClock(1)); ((StringMap) evt.getContextData()).putValue("key", "value"); - final LogEvent actual = evt.createMemento(); + final LogEvent actual = evt.toImmutable(); assertThat(actual.getLoggerName()).isEqualTo(evt.getLoggerName()); assertThat(actual.getMarker()).isEqualTo(evt.getMarker()); assertThat(actual.getLoggerFqcn()).isEqualTo(evt.getLoggerFqcn()); @@ -289,7 +296,7 @@ void testCreateMementoReturnsCopy() { } @Test - void testCreateMementoRetainsParametersAndFormat() { + void testToImmutableRetainsParametersAndFormat() { final RingBufferLogEvent evt = new RingBufferLogEvent(); // Initialize the event with parameters evt.swapParameters(new Object[10]); @@ -322,7 +329,7 @@ void testCreateMementoRetainsParametersAndFormat() { new DummyNanoClock(1)); ((StringMap) evt.getContextData()).putValue("key", "value"); - final Message actual = evt.createMemento().getMessage(); + final Message actual = evt.toImmutable().getMessage(); assertThat(actual.getFormat()).isEqualTo("Hello {}!"); assertThat(actual.getParameters()).isEqualTo(new String[] {"World"}); assertThat(actual.getFormattedMessage()).isEqualTo("Hello World!"); diff --git a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/impl/MutableLogEventTest.java b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/impl/MutableLogEventTest.java index 0723d6ff735..1955c337c61 100644 --- a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/impl/MutableLogEventTest.java +++ b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/impl/MutableLogEventTest.java @@ -137,7 +137,7 @@ public void testInitFromReusableCopiesFormatString() { assertEquals("msg in a bottle", memento.getFormattedMessage(), "formatted"); assertArrayEquals(new String[] {"bottle"}, memento.getParameters(), "parameters"); - final Message eventMementoMessage = mutable.createMemento().getMessage(); + final Message eventMementoMessage = mutable.toImmutable().getMessage(); assertEquals("msg in a {}", eventMementoMessage.getFormat(), "format"); assertEquals("msg in a bottle", eventMementoMessage.getFormattedMessage(), "formatted"); assertArrayEquals(new String[] {"bottle"}, eventMementoMessage.getParameters(), "parameters"); diff --git a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/net/SmtpManagerTest.java b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/net/SmtpManagerTest.java index ae7cd9e55dc..a6872557970 100644 --- a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/net/SmtpManagerTest.java +++ b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/net/SmtpManagerTest.java @@ -16,16 +16,13 @@ */ package org.apache.logging.log4j.core.net; -import static org.hamcrest.CoreMatchers.instanceOf; -import static org.hamcrest.CoreMatchers.is; -import static org.hamcrest.MatcherAssert.assertThat; +import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertEquals; import org.apache.logging.log4j.core.LogEvent; import org.apache.logging.log4j.core.appender.SmtpAppender; import org.apache.logging.log4j.core.async.RingBufferLogEvent; import org.apache.logging.log4j.core.impl.Log4jLogEvent; -import org.apache.logging.log4j.core.impl.MementoMessage; import org.apache.logging.log4j.core.impl.MutableLogEvent; import org.apache.logging.log4j.core.util.ClockFactory; import org.apache.logging.log4j.core.util.DummyNanoClock; @@ -72,17 +69,14 @@ private void testAdd(final LogEvent event) { .setBufferSize(10) .build(); final MailManager mailManager = appender.getManager(); - assertThat("is instance of SmtpManager", mailManager instanceof SmtpManager); + assertThat(mailManager).isInstanceOf(SmtpManager.class); final SmtpManager smtpManager = (SmtpManager) mailManager; smtpManager.removeAllBufferedEvents(); // in case this smtpManager is reused smtpManager.add(event); final LogEvent[] bufferedEvents = smtpManager.removeAllBufferedEvents(); - assertThat("unexpected number of buffered events", bufferedEvents.length, is(1)); - assertThat( - "expected the immutable version of the event to be buffered", - bufferedEvents[0].getMessage(), - is(instanceOf(MementoMessage.class))); + assertThat(bufferedEvents).as("Buffered events").hasSize(1); + assertThat(bufferedEvents[0].getMessage()).as("Immutable message").isNotInstanceOf(ReusableMessage.class); } // LOG4J2-3172: make sure existing protections are not violated 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 32001652bb0..63daa9d3bbe 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 @@ -51,6 +51,7 @@ 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 + * @since 2.8.1 */ LogEvent toImmutable(); @@ -187,7 +188,9 @@ public interface LogEvent extends Serializable { * Gets throwable proxy associated with logging request. * * @return throwable, may be null. + * @deprecated since 2.25.0. This method should be replaced with {@link #getThrown()}. */ + @Deprecated ThrowableProxy getThrownProxy(); /** diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/async/AsyncLogger.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/async/AsyncLogger.java index 0378d010ccc..b63fac9866e 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/async/AsyncLogger.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/async/AsyncLogger.java @@ -235,7 +235,6 @@ private void logWithThreadLocalTranslator( final RingBufferLogEventTranslator translator = getCachedTranslator(); initTranslator(translator, fqcn, level, marker, message, thrown); - initTranslatorThreadValues(translator); publish(translator); } @@ -263,7 +262,6 @@ private void logWithThreadLocalTranslator( final RingBufferLogEventTranslator translator = getCachedTranslator(); initTranslator(translator, fqcn, location, level, marker, message, thrown); - initTranslatorThreadValues(translator); publish(translator); } @@ -355,13 +353,6 @@ private void initTranslator( ); } - private void initTranslatorThreadValues(final RingBufferLogEventTranslator translator) { - // constant check should be optimized out when using default (CACHED) - if (THREAD_NAME_CACHING_STRATEGY == ThreadNameCachingStrategy.UNCACHED) { - translator.updateThreadValues(); - } - } - /** * Returns the caller location if requested, {@code null} otherwise. * diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/async/AsyncLoggerConfig.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/async/AsyncLoggerConfig.java index 092206f01e7..c4483467535 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/async/AsyncLoggerConfig.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/async/AsyncLoggerConfig.java @@ -183,7 +183,9 @@ private void handleQueueFull(final LogEvent event) { private void populateLazilyInitializedFields(final LogEvent event) { event.getSource(); + event.getThreadId(); event.getThreadName(); + event.getThreadPriority(); } void logInBackgroundThread(final LogEvent event) { diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/async/AsyncLoggerConfigDisruptor.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/async/AsyncLoggerConfigDisruptor.java index ba5fd4d54aa..eec1dbe05b2 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/async/AsyncLoggerConfigDisruptor.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/async/AsyncLoggerConfigDisruptor.java @@ -367,14 +367,14 @@ private LogEvent prepareEvent(final LogEvent event) { LogEvent logEvent = ensureImmutable(event); if (logEvent.getMessage() instanceof ReusableMessage) { if (logEvent instanceof Log4jLogEvent) { - ((Log4jLogEvent) logEvent).makeMessageImmutable(); + logEvent = logEvent.toImmutable(); } else if (logEvent instanceof MutableLogEvent) { // MutableLogEvents need to be translated into the RingBuffer by the MUTABLE_TRANSLATOR. // That translator calls MutableLogEvent.initFrom to copy the event, which will makeMessageImmutable the // message. if (translator != MUTABLE_TRANSLATOR) { // should not happen... // TRANSLATOR expects an immutable LogEvent - logEvent = ((MutableLogEvent) logEvent).createMemento(); + logEvent = logEvent.toImmutable(); } } else { // custom log event, with a ReusableMessage showWarningAboutCustomLogEventWithReusableMessage(logEvent); @@ -436,7 +436,7 @@ private LogEvent ensureImmutable(final LogEvent event) { // The original event will be re-used and modified in an application thread later, // so take a snapshot of it, which can be safely processed in the // some-loggers-async background thread. - result = ((RingBufferLogEvent) event).createMemento(); + result = event.toImmutable(); } return result; } 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 d3ab5a10e8d..71d1b55f5d4 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 @@ -18,6 +18,8 @@ import com.lmax.disruptor.EventFactory; import java.io.IOException; +import java.io.InvalidObjectException; +import java.io.ObjectInputStream; import java.util.Arrays; import java.util.Map; import org.apache.logging.log4j.Level; @@ -83,7 +85,6 @@ public RingBufferLogEvent newInstance() { private StringBuilder messageText; private Object[] parameters; private transient Throwable thrown; - private ThrowableProxy thrownProxy; private StringMap contextData = ContextDataFactory.createContextData(); private Marker marker; private String fqcn; @@ -117,7 +118,6 @@ public void setValues( initTime(clock); this.nanoTime = nanoClock.nanoTime(); this.thrown = aThrowable; - this.thrownProxy = null; this.marker = aMarker; this.fqcn = theFqcn; this.location = aLocation; @@ -137,7 +137,7 @@ private void initTime(final Clock clock) { @Override public LogEvent toImmutable() { - return createMemento(); + return Log4jLogEvent.createMemento(this); } private void setMessage(final Message msg) { @@ -334,24 +334,12 @@ public CharSequence subSequence(final int start, final int end) { @Override public Throwable getThrown() { - // after deserialization, thrown is null but thrownProxy may be non-null - if (thrown == null) { - if (thrownProxy != null) { - thrown = thrownProxy.getThrowable(); - } - } return thrown; } @Override public ThrowableProxy getThrownProxy() { - // lazily instantiate the (expensive) ThrowableProxy - if (thrownProxy == null) { - if (thrown != null) { - thrownProxy = new ThrowableProxy(thrown); - } - } - return this.thrownProxy; + return thrown != null ? new ThrowableProxy(thrown) : null; } @Override @@ -420,7 +408,6 @@ public void clear() { this.loggerName = null; clearMessage(); this.thrown = null; - this.thrownProxy = null; clearContextData(); this.marker = null; this.fqcn = null; @@ -458,26 +445,32 @@ private void clearContextData() { } } - private void writeObject(final java.io.ObjectOutputStream out) throws IOException { - getThrownProxy(); // initialize the ThrowableProxy before serializing - out.defaultWriteObject(); + private Object writeReplace() throws IOException { + return Log4jLogEvent.serialize(this, this.includeLocation); + } + + private void readObject(final ObjectInputStream stream) throws InvalidObjectException { + throw new InvalidObjectException("Proxy required"); } /** * Creates and returns a new immutable copy of this {@code RingBufferLogEvent}. * * @return a new immutable copy of the data in this {@code RingBufferLogEvent} + * @deprecated since 2.25.0. Use {@link LogEvent#toImmutable()} instead. */ + @Deprecated public LogEvent createMemento() { - final Log4jLogEvent.Builder builder = new Log4jLogEvent.Builder(); - initializeBuilder(builder); - return builder.build(); + return toImmutable(); } /** * Initializes the specified {@code Log4jLogEvent.Builder} from this {@code RingBufferLogEvent}. * @param builder the builder whose fields to populate + * + * @deprecated since 2.25.0. Use {@link Log4jLogEvent.Builder#Builder(LogEvent)} instead. */ + @Deprecated public void initializeBuilder(final Log4jLogEvent.Builder builder) { // If the data is not frozen, make a copy of it. final StringMap oldContextData = this.contextData; @@ -503,7 +496,6 @@ public void initializeBuilder(final Log4jLogEvent.Builder builder) { .setThreadName(threadName) // .setThreadPriority(threadPriority) // .setThrown(getThrown()) // may deserialize from thrownProxy - .setThrownProxy(thrownProxy) // avoid unnecessarily creating thrownProxy .setInstant(instant) // ; } diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/async/RingBufferLogEventTranslator.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/async/RingBufferLogEventTranslator.java index 9763ff7fce4..f6fee9fcf5a 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/async/RingBufferLogEventTranslator.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/async/RingBufferLogEventTranslator.java @@ -33,6 +33,9 @@ * the ringbuffer {@code RingBufferLogEvent}. After this translator populated * the ringbuffer event, the disruptor will update the sequence number so that * the event can be consumed by another thread. + *

+ * Usage note: This class is only used on the thread that created it. + *

*/ public class RingBufferLogEventTranslator implements EventTranslator { @@ -45,13 +48,15 @@ public class RingBufferLogEventTranslator implements EventTranslator getPropertiesWithLookups( final Throwable t, final List props) { final List results = new ArrayList<>(props.size()); - final LogEvent event = Log4jLogEvent.newBuilder() - .setMessage(data) - .setMarker(marker) - .setLevel(level) - .setLoggerName(loggerName) - .setLoggerFqcn(fqcn) - .setThrown(t) - .build(); for (int i = 0; i < props.size(); i++) { final Property prop = props.get(i); final String value = prop.evaluate(config.getStrSubstitutor()); // since LOG4J2-1575 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 7185bc7bc8a..e47fa88049e 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 @@ -29,7 +29,7 @@ import org.apache.logging.log4j.ThreadContext; import org.apache.logging.log4j.core.ContextDataInjector; import org.apache.logging.log4j.core.LogEvent; -import org.apache.logging.log4j.core.async.RingBufferLogEvent; +import org.apache.logging.log4j.core.async.InternalAsyncUtil; import org.apache.logging.log4j.core.config.LoggerConfig; import org.apache.logging.log4j.core.config.Property; import org.apache.logging.log4j.core.time.Instant; @@ -62,96 +62,116 @@ public class Log4jLogEvent implements LogEvent { private static volatile NanoClock nanoClock = new DummyNanoClock(); private static final ContextDataInjector CONTEXT_DATA_INJECTOR = ContextDataInjectorFactory.createInjector(); + // 1. Fields with an immutable type, initialized in the constructor private final String loggerFqcn; - private final Marker marker; private final Level level; private final String loggerName; - private Message message; - private final MutableInstant instant = new MutableInstant(); + private final Marker marker; private final transient Throwable thrown; - private ThrowableProxy thrownProxy; - private final StringMap contextData; - private final ThreadContext.ContextStack contextStack; - private long threadId; - private String threadName; - private int threadPriority; - private StackTraceElement source; - private boolean includeLocation; - private boolean endOfBatch = false; /** @since Log4J 2.4 */ private final transient long nanoTime; + // This field is mutable, but its state is not shared with other objects. + private final MutableInstant instant = new MutableInstant(); + + // 2. Fields with setters, initialized in the constructor. + private boolean endOfBatch; + private boolean includeLocation; + + // 3. Fields with an immutable type, initialized lazily. + // These fields self-initialize if not provided. + private StackTraceElement source; + private String threadName; + private long threadId; + private int threadPriority; + + // 4. Fields with a potentially mutable type. + // These fields can cause mutability problems for Log4jLogEvent. + private Message message; + private final StringMap contextData; + private final ThreadContext.ContextStack contextStack; + + // 5. Deprecated fields, only used for serialization + private ThrowableProxy thrownProxy; /** LogEvent Builder helper class. */ public static class Builder implements org.apache.logging.log4j.core.util.Builder { + // 1. Fields with an immutable type, initialized eagerly. + // These fields always keep the value assigned. private String loggerFqcn; - private Marker marker; private Level level; private String loggerName; - private Message message; + private Marker marker; private Throwable thrown; + private boolean endOfBatch; + private boolean includeLocation; + private long nanoTime; + // This field is mutable, but it is always copied. private final MutableInstant instant = new MutableInstant(); - private ThrowableProxy thrownProxy; - private StringMap contextData = createContextData((List) null); - private ThreadContext.ContextStack contextStack = ThreadContext.getImmutableStack(); - private long threadId; + + // 2. Fields with an immutable type, initialized lazily. + // These fields self-initialize if not provided. + private StackTraceElement source; private String threadName; + private long threadId; private int threadPriority; - private StackTraceElement source; - private boolean includeLocation; - private boolean endOfBatch = false; - private long nanoTime; - public Builder() {} + // 3. Fields with a mutable type. + // These fields require special handling. + private Message message; + private StringMap contextData; + private ThreadContext.ContextStack contextStack; + public Builder() { + this.contextData = createContextData((List) null); + this.contextStack = ThreadContext.getImmutableStack(); + } + + /** + * Initializes the builder with an immutable instance or a copy of the log event fields. + * + * @param other The log event to copy. + */ public Builder(final LogEvent other) { Objects.requireNonNull(other); - if (other instanceof RingBufferLogEvent) { - ((RingBufferLogEvent) other).initializeBuilder(this); - return; - } - if (other instanceof MutableLogEvent) { - ((MutableLogEvent) other).initializeBuilder(this); - return; - } + // These can be safely copied, since the getters have no side effects. this.loggerFqcn = other.getLoggerFqcn(); - this.marker = other.getMarker(); this.level = other.getLevel(); this.loggerName = other.getLoggerName(); - this.message = other.getMessage(); - this.instant.initFrom(other.getInstant()); + this.marker = other.getMarker(); this.thrown = other.getThrown(); - this.contextStack = other.getContextStack(); - this.includeLocation = other.isIncludeLocation(); this.endOfBatch = other.isEndOfBatch(); + this.includeLocation = other.isIncludeLocation(); this.nanoTime = other.getNanoTime(); + this.instant.initFrom(other.getInstant()); - // Avoid unnecessarily initializing thrownProxy, threadName and source if possible - if (other instanceof Log4jLogEvent) { - final Log4jLogEvent evt = (Log4jLogEvent) other; - this.contextData = evt.contextData; - this.thrownProxy = evt.thrownProxy; - this.source = evt.source; - this.threadId = evt.threadId; - this.threadName = evt.threadName; - this.threadPriority = evt.threadPriority; - } else { - if (other.getContextData() instanceof StringMap) { - this.contextData = (StringMap) other.getContextData(); - } else { - if (this.contextData.isFrozen()) { - this.contextData = ContextDataFactory.createContextData(); - } else { - this.contextData.clear(); - } - this.contextData.putAll(other.getContextData()); - } - this.thrownProxy = other.getThrownProxy(); - this.source = other.getSource(); - this.threadId = other.getThreadId(); - this.threadName = other.getThreadName(); - this.threadPriority = other.getThreadPriority(); - } + // These getters are: + // * side-effect-free in RingBufferLogEvent and MutableLogEvent, + // * have side effects in Log4jLogEvent, + // but since we are copying the event, we want to call them. + this.threadId = other.getThreadId(); + this.threadPriority = other.getThreadPriority(); + this.threadName = other.getThreadName(); + // The `getSource()` method is: + // * side-effect-free in RingBufferLogEvent, + // * have side effects in Log4jLogEvent and MutableLogEvent, + // but since we are copying the event, we want to call it. + this.source = other.getSource(); + + Message message = other.getMessage(); + this.message = message instanceof ReusableMessage + ? ((ReusableMessage) message).memento() + : InternalAsyncUtil.makeMessageImmutable(message); + + ReadOnlyStringMap contextData = other.getContextData(); + this.contextData = contextData instanceof StringMap && ((StringMap) contextData).isFrozen() + ? (StringMap) contextData + : contextData != null + ? ContextDataFactory.createContextData(contextData) + : ContextDataFactory.emptyFrozenContextData(); + + // TODO: The immutability of the context stack is not checked. + this.contextStack = other.getContextStack(); } public Builder setLevel(final Level level) { @@ -194,8 +214,11 @@ public Builder setInstant(final Instant instant) { return this; } + /** + * @deprecated since 2.25.0 without a replacement + */ + @Deprecated public Builder setThrownProxy(final ThrowableProxy thrownProxy) { - this.thrownProxy = thrownProxy; return this; } @@ -271,7 +294,6 @@ public Log4jLogEvent build() { level, message, thrown, - thrownProxy, contextData, contextStack, threadId, @@ -311,7 +333,6 @@ public Log4jLogEvent() { (Throwable) null, null, null, - null, 0, null, 0, @@ -332,7 +353,6 @@ public Log4jLogEvent(final long timestamp) { Strings.EMPTY, null, null, - (Throwable) null, null, null, null, @@ -392,7 +412,6 @@ public Log4jLogEvent( level, message, t, - null, createContextData(properties), ThreadContext.getDepth() == 0 ? null : ThreadContext.cloneStack(), // mutable copy 0, // thread id @@ -430,7 +449,6 @@ public Log4jLogEvent( level, message, t, - null, createContextData(properties), ThreadContext.getDepth() == 0 ? null : ThreadContext.cloneStack(), // mutable copy 0, // thread id @@ -476,7 +494,6 @@ public Log4jLogEvent( level, message, t, - null, createContextData(mdc), ndc, 0, @@ -496,7 +513,7 @@ public Log4jLogEvent( * @param level The logging Level. * @param message The Message. * @param thrown A Throwable or null. - * @param thrownProxy A ThrowableProxy or null. + * @param ignoredThrownProxy Ignored. * @param mdc The mapped diagnostic context. * @param ndc the nested diagnostic context. * @param threadName The name of the thread. @@ -513,7 +530,7 @@ public static Log4jLogEvent createEvent( final Level level, final Message message, final Throwable thrown, - final ThrowableProxy thrownProxy, + final ThrowableProxy ignoredThrownProxy, final Map mdc, final ThreadContext.ContextStack ndc, final String threadName, @@ -526,7 +543,6 @@ public static Log4jLogEvent createEvent( level, message, thrown, - thrownProxy, createContextData(mdc), ndc, 0, @@ -547,7 +563,6 @@ public static Log4jLogEvent createEvent( * @param level The logging Level. * @param message The Message. * @param thrown A Throwable or null. - * @param thrownProxy A ThrowableProxy or null. * @param contextData The key-value pairs from the context. * @param contextStack the nested diagnostic context. * @param threadId the thread ID @@ -566,7 +581,6 @@ private Log4jLogEvent( final Level level, final Message message, final Throwable thrown, - final ThrowableProxy thrownProxy, final StringMap contextData, final ThreadContext.ContextStack contextStack, final long threadId, @@ -583,7 +597,6 @@ private Log4jLogEvent( level, message, thrown, - thrownProxy, contextData, contextStack, threadId, @@ -603,7 +616,6 @@ private Log4jLogEvent( final Level level, final Message message, final Throwable thrown, - final ThrowableProxy thrownProxy, final StringMap contextData, final ThreadContext.ContextStack contextStack, final long threadId, @@ -619,7 +631,6 @@ private Log4jLogEvent( level, message, thrown, - thrownProxy, contextData, contextStack, threadId, @@ -641,7 +652,6 @@ private Log4jLogEvent( final Level level, final Message message, final Throwable thrown, - final ThrowableProxy thrownProxy, final StringMap contextData, final ThreadContext.ContextStack contextStack, final long threadId, @@ -655,7 +665,6 @@ private Log4jLogEvent( this.level = level == null ? Level.OFF : level; // LOG4J2-462, LOG4J2-465 this.message = message; this.thrown = thrown; - this.thrownProxy = thrownProxy; this.contextData = contextData == null ? ContextDataFactory.createContextData() : contextData; this.contextStack = contextStack == null ? ThreadContext.EMPTY_STACK : contextStack; this.threadId = threadId; @@ -720,9 +729,17 @@ public Log4jLogEvent toImmutable() { if (getMessage() instanceof ReusableMessage) { makeMessageImmutable(); } + populateLazilyInitializedFields(); return this; } + private void populateLazilyInitializedFields() { + getSource(); + getThreadId(); + getThreadPriority(); + getThreadName(); + } + /** * Returns the logging Level. * @return the Level associated with this event. @@ -751,7 +768,9 @@ public Message getMessage() { } public void makeMessageImmutable() { - message = new MementoMessage(message.getFormattedMessage(), message.getFormat(), message.getParameters()); + message = message instanceof ReusableMessage + ? ((ReusableMessage) message).memento() + : InternalAsyncUtil.makeMessageImmutable(message); } @Override @@ -814,10 +833,8 @@ public Throwable getThrown() { */ @Override public ThrowableProxy getThrownProxy() { - if (thrownProxy == null && thrown != null) { - thrownProxy = new ThrowableProxy(thrown); - } - return thrownProxy; + // The `thrownProxy` field is non-null only after deserialization. + return thrownProxy != null ? thrownProxy : thrown != null ? new ThrowableProxy(thrown) : null; } /** @@ -912,7 +929,6 @@ public long getNanoTime() { * @return a LogEventProxy. */ protected Object writeReplace() { - getThrownProxy(); // ensure ThrowableProxy is initialized return new LogEventProxy(this, this.includeLocation); } @@ -927,7 +943,6 @@ protected Object writeReplace() { */ public static Serializable serialize(final LogEvent event, final boolean includeLocation) { if (event instanceof Log4jLogEvent) { - event.getThrownProxy(); // ensure ThrowableProxy is initialized return new LogEventProxy((Log4jLogEvent) event, includeLocation); } return new LogEventProxy(event, includeLocation); @@ -943,7 +958,6 @@ public static Serializable serialize(final LogEvent event, final boolean include * @see #serialize(LogEvent, boolean) */ public static Serializable serialize(final Log4jLogEvent event, final boolean includeLocation) { - event.getThrownProxy(); // ensure ThrowableProxy is initialized return new LogEventProxy(event, includeLocation); } @@ -962,7 +976,6 @@ public static Log4jLogEvent deserialize(final Serializable event) { proxy.level, proxy.message, proxy.thrown, - proxy.thrownProxy, proxy.contextData, proxy.contextStack, proxy.threadId, @@ -983,6 +996,12 @@ private void readObject(final ObjectInputStream stream) throws InvalidObjectExce throw new InvalidObjectException("Proxy required"); } + /** + * Creates a new immutable copy of a {@link LogEvent}. + * + * @param logEvent The log event to copy. + * @return An immutable log event. + */ public static LogEvent createMemento(final LogEvent logEvent) { return new Log4jLogEvent.Builder(logEvent).build(); } @@ -993,7 +1012,14 @@ public static LogEvent createMemento(final LogEvent logEvent) { * @return a new immutable copy of the data in this {@code Log4jLogEvent} */ public static Log4jLogEvent createMemento(final LogEvent event, final boolean includeLocation) { - return deserialize(serialize(event, includeLocation)); + // In the case `includeLocation` is false, we temporarily disable its computation. + if (event.isIncludeLocation() && !includeLocation) { + event.setIncludeLocation(false); + Log4jLogEvent memento = (Log4jLogEvent) createMemento(event); + event.setIncludeLocation(true); + return memento; + } + return (Log4jLogEvent) createMemento(event); } @Override @@ -1065,9 +1091,6 @@ public boolean equals(final Object o) { if (thrown != null ? !thrown.equals(that.thrown) : that.thrown != null) { return false; } - if (thrownProxy != null ? !thrownProxy.equals(that.thrownProxy) : that.thrownProxy != null) { - return false; - } return true; } @@ -1083,7 +1106,6 @@ public int hashCode() { result = 31 * result + instant.hashCode(); result = 31 * result + (int) (nanoTime ^ (nanoTime >>> 32)); result = 31 * result + (thrown != null ? thrown.hashCode() : 0); - result = 31 * result + (thrownProxy != null ? thrownProxy.hashCode() : 0); result = 31 * result + (contextData != null ? contextData.hashCode() : 0); result = 31 * result + (contextStack != null ? contextStack.hashCode() : 0); result = 31 * result + (int) (threadId ^ (threadId >>> 32)); @@ -1146,7 +1168,7 @@ public LogEventProxy(final Log4jLogEvent event, final boolean includeLocation) { this.timeMillis = event.instant.getEpochMillisecond(); this.nanoOfMillisecond = event.instant.getNanoOfMillisecond(); this.thrown = event.thrown; - this.thrownProxy = event.thrownProxy; + this.thrownProxy = event.getThrownProxy(); this.contextData = event.contextData; this.contextStack = event.contextStack; this.source = includeLocation ? event.getSource() : event.source; @@ -1172,9 +1194,14 @@ public LogEventProxy(final LogEvent event, final boolean includeLocation) { this.thrownProxy = event.getThrownProxy(); this.contextData = memento(event.getContextData()); this.contextStack = event.getContextStack(); - this.source = includeLocation - ? event.getSource() - : event instanceof MutableLogEvent ? ((MutableLogEvent) event).source : null; + // In the case `includeLocation` is false, we temporarily disable its computation. + if (event.isIncludeLocation() && !includeLocation) { + event.setIncludeLocation(false); + this.source = event.getSource(); + event.setIncludeLocation(true); + } else { + this.source = event.getSource(); + } this.threadId = event.getThreadId(); this.threadName = event.getThreadName(); this.threadPriority = event.getThreadPriority(); @@ -1218,8 +1245,8 @@ protected Object readResolve() { loggerFQCN, level, message(), - thrown, - thrownProxy, + // `thrown` is always null after deserialization + thrownProxy != null ? thrownProxy.getThrowable() : null, contextData, contextStack, threadId, @@ -1231,6 +1258,7 @@ protected Object readResolve() { nanoTime); result.setEndOfBatch(isEndOfBatch); result.setIncludeLocation(isLocationRequired); + result.thrownProxy = thrownProxy; return result; } 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 10fe0345ef3..151f4d4bfa5 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 @@ -64,7 +64,6 @@ public class MutableLogEvent implements LogEvent, ReusableMessage, ParameterVisi private StringBuilder messageText; private Object[] parameters; private Throwable thrown; - private ThrowableProxy thrownProxy; private StringMap contextData = ContextDataFactory.createContextData(); private Marker marker; private String loggerFqcn; @@ -82,9 +81,15 @@ public MutableLogEvent(final StringBuilder msgText, final Object[] replacementPa this.parameters = replacementParameters; } + /** + * {@inheritDoc} + *

+ * If {@link #isIncludeLocation()} is true, caller information for this instance will also be computed. + *

+ */ @Override public Log4jLogEvent toImmutable() { - return createMemento(); + return (Log4jLogEvent) Log4jLogEvent.createMemento(this); } /** @@ -94,6 +99,14 @@ public Log4jLogEvent toImmutable() { *

* This method is used on async logger ringbuffer slots holding MutableLogEvent objects in each slot. *

+ *

+ * Warning: If {@code event.getMessage()} is an instance of {@link ReusableMessage}, this method + * remove the parameter references from the original message. Callers should: + *

+ *
    + *
  1. Either make sure that the {@code event} will not be used again.
  2. + *
  3. Or call {@link LogEvent#toImmutable()} before calling this method.
  4. + *
* * @param event the event to copy data from */ @@ -103,7 +116,6 @@ public void initFrom(final LogEvent event) { this.level = event.getLevel(); this.loggerName = event.getLoggerName(); this.thrown = event.getThrown(); - this.thrownProxy = event.getThrownProxy(); this.instant.initFrom(event.getInstant()); @@ -134,7 +146,6 @@ public void clear() { message = null; messageFormat = null; thrown = null; - thrownProxy = null; source = null; if (contextData != null) { if (contextData.isFrozen()) { // came from CopyOnWrite thread context @@ -212,6 +223,26 @@ public Message getMessage() { return message; } + /** + * Sets the log message of the event. + * + *

+ * Warning: This method mutates the state of the {@code message} + * parameter: + *

+ *
    + *
  1. + * If the message is a {@link org.apache.logging.log4j.message.ReusableMessage}, this method will remove its + * parameter references, which prevents it from being used again. + *
  2. + *
  3. + * Otherwise the lazy {@link Message#getFormattedMessage()} message might be called. + * See {@code log4j2.formatMsgAsync} + * for details. + *
  4. + *
+ * @param msg The log message. The object passed will be modified by this method and should not be reused. + */ public void setMessage(final Message msg) { if (msg instanceof ReusableMessage) { final ReusableMessage reusable = (ReusableMessage) msg; @@ -349,10 +380,7 @@ public Instant getInstant() { */ @Override public ThrowableProxy getThrownProxy() { - if (thrownProxy == null && thrown != null) { - thrownProxy = new ThrowableProxy(thrown); - } - return thrownProxy; + return thrown != null ? new ThrowableProxy(thrown) : null; } public void setSource(StackTraceElement source) { @@ -461,7 +489,7 @@ public void setNanoTime(final long nanoTime) { * @return a LogEventProxy. */ protected Object writeReplace() { - return new Log4jLogEvent.LogEventProxy(this, this.includeLocation); + return Log4jLogEvent.serialize(this, this.includeLocation); } private void readObject(final ObjectInputStream stream) throws InvalidObjectException { @@ -473,15 +501,20 @@ private void readObject(final ObjectInputStream stream) throws InvalidObjectExce * If {@link #isIncludeLocation()} is true, this will obtain caller location information. * * @return a new immutable copy of the data in this {@code MutableLogEvent} + * @deprecated since 2.25.0. Use {@link LogEvent#toImmutable()} instead. */ + @Deprecated public Log4jLogEvent createMemento() { - return Log4jLogEvent.deserialize(Log4jLogEvent.serialize(this, includeLocation)); + return toImmutable(); } /** * Initializes the specified {@code Log4jLogEvent.Builder} from this {@code MutableLogEvent}. * @param builder the builder whose fields to populate + * + * @deprecated since 2.25.0. Use {@link Log4jLogEvent.Builder#Builder(LogEvent)} instead. */ + @Deprecated public void initializeBuilder(final Log4jLogEvent.Builder builder) { builder.setContextData(contextData) // .setContextStack(contextStack) // @@ -498,7 +531,6 @@ public void initializeBuilder(final Log4jLogEvent.Builder builder) { .setThreadName(threadName) // .setThreadPriority(threadPriority) // .setThrown(getThrown()) // may deserialize from thrownProxy - .setThrownProxy(thrownProxy) // avoid unnecessarily creating thrownProxy .setInstant(instant) // ; } diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/ReusableLogEventFactory.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/ReusableLogEventFactory.java index e954d9b7f71..7b5d23ab8a5 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/ReusableLogEventFactory.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/ReusableLogEventFactory.java @@ -66,13 +66,27 @@ public LogEvent createEvent( /** * Creates a log event. - * + *

+ * Implementation note: This method mutates the state of the {@code message} + * parameter: + *

+ *
    + *
  1. + * If the message is a {@link org.apache.logging.log4j.message.ReusableMessage}, this method will remove its + * parameter references, which prevents it from being used again. + *
  2. + *
  3. + * Otherwise the lazy {@link Message#getFormattedMessage()} message might be called. + * See {@code log4j2.formatMsgAsync} + * for details. + *
  4. + *
* @param loggerName The name of the Logger. * @param marker An optional Marker. * @param fqcn The fully qualified class name of the caller. * @param location The location of the caller. * @param level The event Level. - * @param message The Message. + * @param message The log message. The object passed will be modified by this method and should not be reused. * @param properties Properties to be added to the log event. * @param t An optional Throwable. * @return The LogEvent. @@ -105,8 +119,11 @@ public LogEvent createEvent( ThreadContext.getDepth() == 0 ? ThreadContext.EMPTY_STACK : ThreadContext.cloneStack()); // mutable copy if (THREAD_NAME_CACHING_STRATEGY == ThreadNameCachingStrategy.UNCACHED) { - result.setThreadName(Thread.currentThread().getName()); // Thread.getName() allocates Objects on each call - result.setThreadPriority(Thread.currentThread().getPriority()); + Thread currentThread = Thread.currentThread(); + result.setThreadId(currentThread.getId()); + // Before JRE 8u102, Thread.getName() allocated objects on each call + result.setThreadName(currentThread.getName()); + result.setThreadPriority(currentThread.getPriority()); } return result; } diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/layout/AbstractJacksonLayout.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/layout/AbstractJacksonLayout.java index b05ceb56bc3..4de5d210b8f 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/layout/AbstractJacksonLayout.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/layout/AbstractJacksonLayout.java @@ -36,7 +36,6 @@ import org.apache.logging.log4j.core.config.Configuration; import org.apache.logging.log4j.core.config.plugins.PluginBuilderAttribute; import org.apache.logging.log4j.core.config.plugins.PluginElement; -import org.apache.logging.log4j.core.impl.Log4jLogEvent; import org.apache.logging.log4j.core.impl.ThrowableProxy; import org.apache.logging.log4j.core.jackson.XmlConstants; import org.apache.logging.log4j.core.lookup.StrSubstitutor; @@ -336,8 +335,8 @@ public String toSerializable(final LogEvent event) { private static LogEvent convertMutableToLog4jEvent(final LogEvent event) { // TODO Jackson-based layouts have certain filters set up for Log4jLogEvent. // TODO Need to set up the same filters for MutableLogEvent but don't know how... - // This is a workaround. - return event instanceof Log4jLogEvent ? event : Log4jLogEvent.createMemento(event); + // This is a workaround, since `toImmutable()` currently returns a Log4jLogEvent. + return event.toImmutable(); } protected Object wrapLogEvent(final LogEvent event) { diff --git a/log4j-jakarta-smtp/src/test/java/org/apache/logging/log4j/smtp/SmtpManagerTest.java b/log4j-jakarta-smtp/src/test/java/org/apache/logging/log4j/smtp/SmtpManagerTest.java index d8017afe4e2..6ba85d15e29 100644 --- a/log4j-jakarta-smtp/src/test/java/org/apache/logging/log4j/smtp/SmtpManagerTest.java +++ b/log4j-jakarta-smtp/src/test/java/org/apache/logging/log4j/smtp/SmtpManagerTest.java @@ -16,15 +16,12 @@ */ package org.apache.logging.log4j.smtp; -import static org.hamcrest.CoreMatchers.instanceOf; -import static org.hamcrest.CoreMatchers.is; -import static org.hamcrest.MatcherAssert.assertThat; +import static org.assertj.core.api.Assertions.assertThat; import org.apache.logging.log4j.core.LogEvent; import org.apache.logging.log4j.core.appender.SmtpAppender; import org.apache.logging.log4j.core.async.RingBufferLogEvent; import org.apache.logging.log4j.core.impl.Log4jLogEvent; -import org.apache.logging.log4j.core.impl.MementoMessage; import org.apache.logging.log4j.core.impl.MutableLogEvent; import org.apache.logging.log4j.core.net.MailManager; import org.apache.logging.log4j.core.util.ClockFactory; @@ -54,17 +51,14 @@ private void testAdd(final LogEvent event) { .setBufferSize(10) .build(); final MailManager mailManager = appender.getManager(); - assertThat("is instance of SmtpManager", mailManager instanceof SmtpManager); + assertThat(mailManager).isInstanceOf(SmtpManager.class); final SmtpManager smtpManager = (SmtpManager) mailManager; smtpManager.removeAllBufferedEvents(); // in case this smtpManager is reused smtpManager.add(event); final LogEvent[] bufferedEvents = smtpManager.removeAllBufferedEvents(); - assertThat("unexpected number of buffered events", bufferedEvents.length, is(1)); - assertThat( - "expected the immutable version of the event to be buffered", - bufferedEvents[0].getMessage(), - is(instanceOf(MementoMessage.class))); + assertThat(bufferedEvents).as("Buffered events").hasSize(1); + assertThat(bufferedEvents[0].getMessage()).as("Immutable message").isNotInstanceOf(ReusableMessage.class); } // LOG4J2-3172: make sure existing protections are not violated diff --git a/src/changelog/.2.x.x/throwable-proxy-clean-up.xml b/src/changelog/.2.x.x/throwable-proxy-clean-up.xml new file mode 100644 index 00000000000..acc6378895c --- /dev/null +++ b/src/changelog/.2.x.x/throwable-proxy-clean-up.xml @@ -0,0 +1,9 @@ + + + + Improve implementations of `LogEvent.toImmutable()` and `ReusableMessage.memento()` and remove usage of `ThrowableProxy`. + +