Skip to content

Commit dc6c53a

Browse files
authored
Improve implementations of LogEvent.toImmutable() and ReusableMessage.memento() (#3171)
The implementations of `LogEvent.toImmutable()` either use serialization or one of the `Log4jLogEvent.Builder` constructors and custom per class code. This PR: - Redirects all implementation of `LogEvent.toImmutable` to the `Builder(LogEvent)` constructor (except `Log4jLogEvent.toImmutable()`) - Improve the constructor to create really immutable and thread-safe instances. - Removes the usage of `ThrowableProxy` for purposes not related to serialization. - Fixes some implementations of `ReusableMessage.memento()`. - Uses `ReusableMessage.memento()` instead of `ImmutableMessage` where applicable.
1 parent ac34697 commit dc6c53a

File tree

23 files changed

+292
-220
lines changed

23 files changed

+292
-220
lines changed

log4j-1.2-api/src/main/java/org/apache/log4j/rewrite/MapRewritePolicy.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,6 @@ public LoggingEvent rewrite(final LoggingEvent source) {
105105
.setThrown(source.getThrowableInformation().getThrowable())
106106
.setTimeMillis(source.getTimeStamp())
107107
.setNanoTime(0)
108-
.setThrownProxy(null)
109108
.build();
110109
}
111110
return new LogEventAdapter(event);

log4j-1.2-api/src/main/java/org/apache/log4j/rewrite/PropertyRewritePolicy.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,6 @@ public LoggingEvent rewrite(final LoggingEvent source) {
110110
.setThrown(source.getThrowableInformation().getThrowable())
111111
.setTimeMillis(source.getTimeStamp())
112112
.setNanoTime(0)
113-
.setThrownProxy(null)
114113
.build();
115114
}
116115
return new LogEventAdapter(event);

log4j-api/src/main/java/org/apache/logging/log4j/message/ReusableObjectMessage.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,11 @@ public <S> void forEachParameter(final ParameterConsumer<S> action, final S stat
128128

129129
@Override
130130
public Message memento() {
131-
return new ObjectMessage(obj);
131+
Message message = new ObjectMessage(obj);
132+
// Since `toString()` methods are not always pure functions and might depend on the thread and other context
133+
// values, we format the message and cache the result.
134+
message.getFormattedMessage();
135+
return message;
132136
}
133137

134138
/**

log4j-api/src/main/java/org/apache/logging/log4j/message/ReusableParameterizedMessage.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,11 @@ public <S> void forEachParameter(final ParameterConsumer<S> action, final S stat
114114

115115
@Override
116116
public Message memento() {
117-
return new ParameterizedMessage(messagePattern, getTrimmedParams());
117+
Message message = new ParameterizedMessage(messagePattern, getTrimmedParams());
118+
// Since `toString()` methods are not always pure functions and might depend on the thread and other context
119+
// values, we format the message and cache the result.
120+
message.getFormattedMessage();
121+
return message;
118122
}
119123

120124
private void init(final String messagePattern, final int argCount, final Object[] args) {

log4j-api/src/main/java/org/apache/logging/log4j/message/ReusableSimpleMessage.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,11 @@ public <S> void forEachParameter(final ParameterConsumer<S> action, final S stat
8585

8686
@Override
8787
public Message memento() {
88-
return new SimpleMessage(charSequence);
88+
SimpleMessage message = new SimpleMessage(charSequence);
89+
// Since `toString()` methods are not always pure functions and might depend on the thread and other context
90+
// values, we format the message and cache the result.
91+
message.getFormattedMessage();
92+
return message;
8993
}
9094

9195
// CharSequence impl

log4j-core-test/src/main/java/org/apache/logging/log4j/core/test/appender/ListAppender.java

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,6 @@
3535
import org.apache.logging.log4j.core.config.plugins.PluginBuilderFactory;
3636
import org.apache.logging.log4j.core.config.plugins.PluginElement;
3737
import org.apache.logging.log4j.core.config.plugins.validation.constraints.Required;
38-
import org.apache.logging.log4j.core.impl.MutableLogEvent;
3938
import org.apache.logging.log4j.core.layout.SerializedLayout;
4039
import org.awaitility.Awaitility;
4140

@@ -121,12 +120,7 @@ public ListAppender(
121120
public void append(final LogEvent event) {
122121
final Layout<? extends Serializable> layout = getLayout();
123122
if (layout == null) {
124-
if (event instanceof MutableLogEvent) {
125-
// must take snapshot or subsequent calls to logger.log() will modify this event
126-
events.add(((MutableLogEvent) event).createMemento());
127-
} else {
128-
events.add(event);
129-
}
123+
events.add(event.toImmutable());
130124
} else if (layout instanceof SerializedLayout) {
131125
final byte[] header = layout.getHeader();
132126
final byte[] content = layout.toByteArray(event);

log4j-core-test/src/test/java/org/apache/logging/log4j/core/async/BlockingAppender.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,6 @@
3333
import org.apache.logging.log4j.core.config.plugins.PluginElement;
3434
import org.apache.logging.log4j.core.config.plugins.PluginFactory;
3535
import org.apache.logging.log4j.core.config.plugins.validation.constraints.Required;
36-
import org.apache.logging.log4j.core.impl.Log4jLogEvent;
3736

3837
/**
3938
* Appender that can be halted and resumed, for testing queue-full scenarios.
@@ -58,7 +57,7 @@ public void append(final LogEvent event) {
5857
// may be a reusable event, make a copy, don't keep a reference to the original event
5958
final List<LogEvent> events = logEvents;
6059
if (events != null) {
61-
events.add(Log4jLogEvent.createMemento(event));
60+
events.add(event.toImmutable());
6261
}
6362

6463
if (countDownLatch == null) {

log4j-core-test/src/test/java/org/apache/logging/log4j/core/async/RingBufferLogEventTest.java

Lines changed: 26 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
import org.apache.logging.log4j.Level;
2929
import org.apache.logging.log4j.Marker;
3030
import org.apache.logging.log4j.MarkerManager;
31+
import org.apache.logging.log4j.ThreadContext;
3132
import org.apache.logging.log4j.ThreadContext.ContextStack;
3233
import org.apache.logging.log4j.core.LogEvent;
3334
import org.apache.logging.log4j.core.impl.ThrowableProxy;
@@ -201,7 +202,7 @@ void testSerializationDeserialization() {
201202
final Level level = Level.TRACE;
202203
final Message data = new SimpleMessage("message");
203204
final Throwable t = new InternalError("not a real error");
204-
final ContextStack contextStack = null;
205+
final ContextStack contextStack = ThreadContext.getImmutableStack();
205206
final String threadName = "main";
206207
final StackTraceElement location = null;
207208
evt.setValues(
@@ -222,25 +223,31 @@ void testSerializationDeserialization() {
222223
new DummyNanoClock(1));
223224
((StringMap) evt.getContextData()).putValue("key", "value");
224225

225-
final RingBufferLogEvent other = SerialUtil.deserialize(SerialUtil.serialize(evt));
226-
assertThat(other.getLoggerName()).isEqualTo(loggerName);
227-
assertThat(other.getMarker()).isEqualTo(marker);
228-
assertThat(other.getLoggerFqcn()).isEqualTo(fqcn);
229-
assertThat(other.getLevel()).isEqualTo(level);
230-
assertThat(other.getMessage()).isEqualTo(data);
231-
assertThat(other.getThrown()).isNull();
232-
assertThat(other.getThrownProxy()).isEqualTo(new ThrowableProxy(t));
233-
assertThat(other.getContextData()).isEqualTo(evt.getContextData());
234-
assertThat(other.getContextStack()).isEqualTo(contextStack);
235-
assertThat(other.getThreadName()).isEqualTo(threadName);
236-
assertThat(other.getSource()).isEqualTo(location);
237-
assertThat(other.getTimeMillis()).isEqualTo(12345);
238-
assertThat(other.getInstant().getNanoOfMillisecond()).isEqualTo(678);
226+
final LogEvent other = SerialUtil.deserialize(SerialUtil.serialize(evt));
227+
assertThat(other.getLoggerName()).as("Logger name").isEqualTo(loggerName);
228+
assertThat(other.getMarker()).as("Marker").isEqualTo(marker);
229+
assertThat(other.getLoggerFqcn())
230+
.as("Fully qualified class name of logger implementation")
231+
.isEqualTo(fqcn);
232+
assertThat(other.getLevel()).as("Log event level").isEqualTo(level);
233+
assertThat(other.getMessage()).as("Log event message").isEqualTo(data);
234+
assertThat(other.getThrown()).as("Thrown exception").isNull();
235+
assertThat(other.getThrownProxy())
236+
.as("Serialization proxy for thrown exception")
237+
.isEqualTo(new ThrowableProxy(t));
238+
assertThat(other.getContextData()).as("Context data map").isEqualTo(evt.getContextData());
239+
assertThat(other.getContextStack()).as("Context data stack").isEqualTo(contextStack);
240+
assertThat(other.getThreadName()).as("Thread name").isEqualTo(threadName);
241+
assertThat(other.getSource()).as("Log event location").isEqualTo(location);
242+
assertThat(other.getTimeMillis()).as("Log event timestamp in millis").isEqualTo(12345);
243+
assertThat(other.getInstant().getNanoOfMillisecond())
244+
.as("Log event timestamp in nanos of millis")
245+
.isEqualTo(678);
239246
}
240247

241248
@SuppressWarnings("deprecation")
242249
@Test
243-
void testCreateMementoReturnsCopy() {
250+
void testToImmutableReturnsCopy() {
244251
final RingBufferLogEvent evt = new RingBufferLogEvent();
245252
final String loggerName = "logger.name";
246253
final Marker marker = MarkerManager.getMarker("marked man");
@@ -269,7 +276,7 @@ void testCreateMementoReturnsCopy() {
269276
new DummyNanoClock(1));
270277
((StringMap) evt.getContextData()).putValue("key", "value");
271278

272-
final LogEvent actual = evt.createMemento();
279+
final LogEvent actual = evt.toImmutable();
273280
assertThat(actual.getLoggerName()).isEqualTo(evt.getLoggerName());
274281
assertThat(actual.getMarker()).isEqualTo(evt.getMarker());
275282
assertThat(actual.getLoggerFqcn()).isEqualTo(evt.getLoggerFqcn());
@@ -288,7 +295,7 @@ void testCreateMementoReturnsCopy() {
288295
}
289296

290297
@Test
291-
void testCreateMementoRetainsParametersAndFormat() {
298+
void testToImmutableRetainsParametersAndFormat() {
292299
final RingBufferLogEvent evt = new RingBufferLogEvent();
293300
// Initialize the event with parameters
294301
evt.swapParameters(new Object[10]);
@@ -321,7 +328,7 @@ void testCreateMementoRetainsParametersAndFormat() {
321328
new DummyNanoClock(1));
322329
((StringMap) evt.getContextData()).putValue("key", "value");
323330

324-
final Message actual = evt.createMemento().getMessage();
331+
final Message actual = evt.toImmutable().getMessage();
325332
assertThat(actual.getFormat()).isEqualTo("Hello {}!");
326333
assertThat(actual.getParameters()).isEqualTo(new String[] {"World"});
327334
assertThat(actual.getFormattedMessage()).isEqualTo("Hello World!");

log4j-core-test/src/test/java/org/apache/logging/log4j/core/impl/MutableLogEventTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,7 @@ void testInitFromReusableCopiesFormatString() {
137137
assertEquals("msg in a bottle", memento.getFormattedMessage(), "formatted");
138138
assertArrayEquals(new String[] {"bottle"}, memento.getParameters(), "parameters");
139139

140-
final Message eventMementoMessage = mutable.createMemento().getMessage();
140+
final Message eventMementoMessage = mutable.toImmutable().getMessage();
141141
assertEquals("msg in a {}", eventMementoMessage.getFormat(), "format");
142142
assertEquals("msg in a bottle", eventMementoMessage.getFormattedMessage(), "formatted");
143143
assertArrayEquals(new String[] {"bottle"}, eventMementoMessage.getParameters(), "parameters");

log4j-core-test/src/test/java/org/apache/logging/log4j/core/net/SmtpManagerTest.java

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -16,16 +16,13 @@
1616
*/
1717
package org.apache.logging.log4j.core.net;
1818

19-
import static org.hamcrest.CoreMatchers.instanceOf;
20-
import static org.hamcrest.CoreMatchers.is;
21-
import static org.hamcrest.MatcherAssert.assertThat;
19+
import static org.assertj.core.api.Assertions.assertThat;
2220
import static org.junit.jupiter.api.Assertions.assertEquals;
2321

2422
import org.apache.logging.log4j.core.LogEvent;
2523
import org.apache.logging.log4j.core.appender.SmtpAppender;
2624
import org.apache.logging.log4j.core.async.RingBufferLogEvent;
2725
import org.apache.logging.log4j.core.impl.Log4jLogEvent;
28-
import org.apache.logging.log4j.core.impl.MementoMessage;
2926
import org.apache.logging.log4j.core.impl.MutableLogEvent;
3027
import org.apache.logging.log4j.core.util.ClockFactory;
3128
import org.apache.logging.log4j.core.util.DummyNanoClock;
@@ -72,17 +69,14 @@ private void testAdd(final LogEvent event) {
7269
.setBufferSize(10)
7370
.build();
7471
final MailManager mailManager = appender.getManager();
75-
assertThat("is instance of SmtpManager", mailManager instanceof SmtpManager);
72+
assertThat(mailManager).isInstanceOf(SmtpManager.class);
7673
final SmtpManager smtpManager = (SmtpManager) mailManager;
7774
smtpManager.removeAllBufferedEvents(); // in case this smtpManager is reused
7875
smtpManager.add(event);
7976

8077
final LogEvent[] bufferedEvents = smtpManager.removeAllBufferedEvents();
81-
assertThat("unexpected number of buffered events", bufferedEvents.length, is(1));
82-
assertThat(
83-
"expected the immutable version of the event to be buffered",
84-
bufferedEvents[0].getMessage(),
85-
is(instanceOf(MementoMessage.class)));
78+
assertThat(bufferedEvents).as("Buffered events").hasSize(1);
79+
assertThat(bufferedEvents[0].getMessage()).as("Immutable message").isNotInstanceOf(ReusableMessage.class);
8680
}
8781

8882
// LOG4J2-3172: make sure existing protections are not violated

0 commit comments

Comments
 (0)