Skip to content

Commit 001aaad

Browse files
carterkozakrgoers
authored andcommitted
LOG4J2-3198: Log4j2 no longer formats lookups in messages by default
Lookups in messages are confusing, and muddy the line between logging APIs and implementation. Given a particular API, there's an expectation that a particular shape of call will result in specific results. However, lookups in messages can be passed into JUL and will result in resolved output in log4j formatted output, but not any other implementations despite no direct dependency on those implementations. There's also a cost to searching formatted message strings for particular escape sequences which define lookups. This feature is not used as far as we've been able to tell searching github and stackoverflow, so it's unnecessary for every log event in every application to burn several cpu cycles searching for the value.
1 parent c77b3cb commit 001aaad

11 files changed

Lines changed: 84 additions & 54 deletions

File tree

log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/MessagePatternConverter.java

Lines changed: 22 additions & 9 deletions
Original file line numberOriginal file lineDiff line numberDiff line change
@@ -16,13 +16,13 @@
16
*/
16
*/
17
package org.apache.logging.log4j.core.pattern;
17
package org.apache.logging.log4j.core.pattern;
18

18

19+
import java.util.ArrayList;
20+
import java.util.List;
19
import java.util.Locale;
21
import java.util.Locale;
20

22

21
import org.apache.logging.log4j.core.LogEvent;
23
import org.apache.logging.log4j.core.LogEvent;
22
import org.apache.logging.log4j.core.config.Configuration;
24
import org.apache.logging.log4j.core.config.Configuration;
23
import org.apache.logging.log4j.core.config.plugins.Plugin;
25
import org.apache.logging.log4j.core.config.plugins.Plugin;
24-
import org.apache.logging.log4j.core.util.ArrayUtils;
25-
import org.apache.logging.log4j.core.util.Constants;
26
import org.apache.logging.log4j.core.util.Loader;
26
import org.apache.logging.log4j.core.util.Loader;
27
import org.apache.logging.log4j.message.Message;
27
import org.apache.logging.log4j.message.Message;
28
import org.apache.logging.log4j.message.MultiformatMessage;
28
import org.apache.logging.log4j.message.MultiformatMessage;
@@ -39,17 +39,18 @@
39
@PerformanceSensitive("allocation")
39
@PerformanceSensitive("allocation")
40
public class MessagePatternConverter extends LogEventPatternConverter {
40
public class MessagePatternConverter extends LogEventPatternConverter {
41

41

42+
private static final String LOOKUPS = "lookups";
42
private static final String NOLOOKUPS = "nolookups";
43
private static final String NOLOOKUPS = "nolookups";
43

44

44
private MessagePatternConverter() {
45
private MessagePatternConverter() {
45
super("Message", "message");
46
super("Message", "message");
46
}
47
}
47

48

48-
private static int loadNoLookups(final String[] options) {
49+
private static int loadLookups(final String[] options) {
49
if (options != null) {
50
if (options != null) {
50
for (int i = 0; i < options.length; i++) {
51
for (int i = 0; i < options.length; i++) {
51
final String option = options[i];
52
final String option = options[i];
52-
if (NOLOOKUPS.equalsIgnoreCase(option)) {
53+
if (LOOKUPS.equalsIgnoreCase(option)) {
53
return i;
54
return i;
54
}
55
}
55
}
56
}
@@ -86,14 +87,13 @@ private static TextRenderer loadMessageRenderer(final String[] options) {
86
* @return instance of pattern converter.
87
* @return instance of pattern converter.
87
*/
88
*/
88
public static MessagePatternConverter newInstance(final Configuration config, final String[] options) {
89
public static MessagePatternConverter newInstance(final Configuration config, final String[] options) {
89-
int noLookupsIdx = loadNoLookups(options);
90+
boolean lookups = loadLookups(options) >= 0;
90-
boolean noLookups = Constants.FORMAT_MESSAGES_PATTERN_DISABLE_LOOKUPS || noLookupsIdx >= 0;
91+
String[] formats = withoutLookupOptions(options);
91-
String[] formats = noLookupsIdx >= 0 ? ArrayUtils.remove(options, noLookupsIdx) : options;
92+
TextRenderer textRenderer = loadMessageRenderer(formats);
92-
TextRenderer textRenderer = loadMessageRenderer(noLookupsIdx >= 0 ? ArrayUtils.remove(options, noLookupsIdx) : options);
93
MessagePatternConverter result = formats == null || formats.length == 0
93
MessagePatternConverter result = formats == null || formats.length == 0
94
? SimpleMessagePatternConverter.INSTANCE
94
? SimpleMessagePatternConverter.INSTANCE
95
: new FormattedMessagePatternConverter(formats);
95
: new FormattedMessagePatternConverter(formats);
96-
if (!noLookups && config != null) {
96+
if (lookups && config != null) {
97
result = new LookupMessagePatternConverter(result, config);
97
result = new LookupMessagePatternConverter(result, config);
98
}
98
}
99
if (textRenderer != null) {
99
if (textRenderer != null) {
@@ -102,6 +102,19 @@ public static MessagePatternConverter newInstance(final Configuration config, fi
102
return result;
102
return result;
103
}
103
}
104

104

105+
private static String[] withoutLookupOptions(final String[] options) {
106+
if (options == null || options.length == 0) {
107+
return options;
108+
}
109+
List<String> results = new ArrayList<>(options.length);
110+
for (String option : options) {
111+
if (!LOOKUPS.equalsIgnoreCase(option) && !NOLOOKUPS.equalsIgnoreCase(option)) {
112+
results.add(option);
113+
}
114+
}
115+
return results.toArray(new String[0]);
116+
}
117+
105
@Override
118
@Override
106
public void format(final LogEvent event, final StringBuilder toAppendTo) {
119
public void format(final LogEvent event, final StringBuilder toAppendTo) {
107
throw new UnsupportedOperationException();
120
throw new UnsupportedOperationException();

log4j-core/src/main/java/org/apache/logging/log4j/core/util/Constants.java

Lines changed: 7 additions & 3 deletions
Original file line numberOriginal file lineDiff line numberDiff line change
@@ -55,13 +55,17 @@ public final class Constants {
55
"log4j.format.msg.async", false);
55
"log4j.format.msg.async", false);
56

56

57
/**
57
/**
58-
* LOG4J2-2109 if {@code true}, MessagePatternConverter will always operate as though
58+
* LOG4J2-3198 property which used to globally opt out of lookups in pattern layout message text, however
59-
* <pre>%m{nolookups}</pre> is configured.
59+
* this is the default and this property is no longer read.
60+
*
61+
* Deprecated in 2.15.
60
*
62
*
61
* @since 2.10
63
* @since 2.10
64+
* @deprecated no longer used, lookups are only used when {@code %m{lookups}} is specified
62
*/
65
*/
66+
@Deprecated
63
public static final boolean FORMAT_MESSAGES_PATTERN_DISABLE_LOOKUPS = PropertiesUtil.getProperties().getBooleanProperty(
67
public static final boolean FORMAT_MESSAGES_PATTERN_DISABLE_LOOKUPS = PropertiesUtil.getProperties().getBooleanProperty(
64-
"log4j2.formatMsgNoLookups", false);
68+
"log4j2.formatMsgNoLookups", true);
65

69

66
/**
70
/**
67
* {@code true} if we think we are running in a web container, based on the boolean value of system property
71
* {@code true} if we think we are running in a web container, based on the boolean value of system property

log4j-core/src/test/java/org/apache/logging/log4j/core/layout/PatternLayoutLookupDateTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberOriginal file lineDiff line numberDiff line change
@@ -29,7 +29,7 @@
29
*
29
*
30
* This shows the behavior this user wants to disable.
30
* This shows the behavior this user wants to disable.
31
*/
31
*/
32-
@LoggerContextSource("log4j-list.xml")
32+
@LoggerContextSource("log4j-list-lookups.xml")
33
public class PatternLayoutLookupDateTest {
33
public class PatternLayoutLookupDateTest {
34

34

35
@Test
35
@Test

log4j-core/src/test/java/org/apache/logging/log4j/core/layout/PatternLayoutNoLookupDateTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberOriginal file lineDiff line numberDiff line change
@@ -26,7 +26,7 @@
26
/**
26
/**
27
* See (LOG4J2-905) Ability to disable (date) lookup completely, compatibility issues with other libraries like camel.
27
* See (LOG4J2-905) Ability to disable (date) lookup completely, compatibility issues with other libraries like camel.
28
*/
28
*/
29-
@LoggerContextSource("log4j-list-nolookups.xml")
29+
@LoggerContextSource("log4j-list.xml")
30
public class PatternLayoutNoLookupDateTest {
30
public class PatternLayoutNoLookupDateTest {
31

31

32
@Test
32
@Test

log4j-core/src/test/java/org/apache/logging/log4j/core/pattern/MessagePatternConverterTest.java

Lines changed: 19 additions & 8 deletions
Original file line numberOriginal file lineDiff line numberDiff line change
@@ -22,7 +22,6 @@
22
import org.apache.logging.log4j.core.config.DefaultConfiguration;
22
import org.apache.logging.log4j.core.config.DefaultConfiguration;
23
import org.apache.logging.log4j.core.config.builder.impl.DefaultConfigurationBuilder;
23
import org.apache.logging.log4j.core.config.builder.impl.DefaultConfigurationBuilder;
24
import org.apache.logging.log4j.core.impl.Log4jLogEvent;
24
import org.apache.logging.log4j.core.impl.Log4jLogEvent;
25-
import org.apache.logging.log4j.core.util.Constants;
26
import org.apache.logging.log4j.message.Message;
25
import org.apache.logging.log4j.message.Message;
27
import org.apache.logging.log4j.message.ParameterizedMessage;
26
import org.apache.logging.log4j.message.ParameterizedMessage;
28
import org.apache.logging.log4j.message.SimpleMessage;
27
import org.apache.logging.log4j.message.SimpleMessage;
@@ -76,12 +75,7 @@ public void testPatternAndParameterizedMessageDateLookup() {
76
}
75
}
77

76

78
@Test
77
@Test
79-
public void testLookupEnabledByDefault() {
78+
public void testDefaultDisabledLookup() {
80-
assertFalse(Constants.FORMAT_MESSAGES_PATTERN_DISABLE_LOOKUPS, "Expected lookups to be enabled");
81-
}
82-
83-
@Test
84-
public void testLookup() {
85
final Configuration config = new DefaultConfigurationBuilder()
79
final Configuration config = new DefaultConfigurationBuilder()
86
.addProperty("foo", "bar")
80
.addProperty("foo", "bar")
87
.build(true);
81
.build(true);
@@ -93,7 +87,7 @@ public void testLookup() {
93
.setMessage(msg).build();
87
.setMessage(msg).build();
94
final StringBuilder sb = new StringBuilder();
88
final StringBuilder sb = new StringBuilder();
95
converter.format(event, sb);
89
converter.format(event, sb);
96-
assertEquals("bar", sb.toString(), "Unexpected result");
90+
assertEquals("${foo}", sb.toString(), "Unexpected result");
97
}
91
}
98

92

99
@Test
93
@Test
@@ -113,6 +107,23 @@ public void testDisabledLookup() {
113
assertEquals("${foo}", sb.toString(), "Expected the raw pattern string without lookup");
107
assertEquals("${foo}", sb.toString(), "Expected the raw pattern string without lookup");
114
}
108
}
115

109

110+
@Test
111+
public void testLookup() {
112+
final Configuration config = new DefaultConfigurationBuilder()
113+
.addProperty("foo", "bar")
114+
.build(true);
115+
final MessagePatternConverter converter =
116+
MessagePatternConverter.newInstance(config, new String[] {"lookups"});
117+
final Message msg = new ParameterizedMessage("${foo}");
118+
final LogEvent event = Log4jLogEvent.newBuilder() //
119+
.setLoggerName("MyLogger") //
120+
.setLevel(Level.DEBUG) //
121+
.setMessage(msg).build();
122+
final StringBuilder sb = new StringBuilder();
123+
converter.format(event, sb);
124+
assertEquals("bar", sb.toString(), "Unexpected result");
125+
}
126+
116
@Test
127
@Test
117
public void testPatternWithConfiguration() {
128
public void testPatternWithConfiguration() {
118
final Configuration config = new DefaultConfiguration();
129
final Configuration config = new DefaultConfiguration();

log4j-core/src/test/java/org/apache/logging/log4j/core/pattern/RegexReplacementTest.java

Lines changed: 12 additions & 6 deletions
Original file line numberOriginal file lineDiff line numberDiff line change
@@ -16,6 +16,12 @@
16
*/
16
*/
17
package org.apache.logging.log4j.core.pattern;
17
package org.apache.logging.log4j.core.pattern;
18

18

19+
import static org.junit.jupiter.api.Assertions.assertEquals;
20+
import static org.junit.jupiter.api.Assertions.assertNotNull;
21+
import static org.junit.jupiter.api.Assertions.assertTrue;
22+
23+
import java.util.List;
24+
19
import org.apache.logging.log4j.ThreadContext;
25
import org.apache.logging.log4j.ThreadContext;
20
import org.apache.logging.log4j.core.LoggerContext;
26
import org.apache.logging.log4j.core.LoggerContext;
21
import org.apache.logging.log4j.junit.LoggerContextSource;
27
import org.apache.logging.log4j.junit.LoggerContextSource;
@@ -25,10 +31,6 @@
25
import org.apache.logging.log4j.util.Strings;
31
import org.apache.logging.log4j.util.Strings;
26
import org.junit.jupiter.api.Test;
32
import org.junit.jupiter.api.Test;
27

33

28-
import java.util.List;
29-
30-
import static org.junit.jupiter.api.Assertions.*;
31-
32
@LoggerContextSource("log4j-replace.xml")
34
@LoggerContextSource("log4j-replace.xml")
33
@UsingThreadContextMap
35
@UsingThreadContextMap
34
public class RegexReplacementTest {
36
public class RegexReplacementTest {
@@ -55,10 +57,14 @@ public void testReplacement() {
55
assertEquals(1, msgs.size(), "Incorrect number of messages. Should be 1 is " + msgs.size());
57
assertEquals(1, msgs.size(), "Incorrect number of messages. Should be 1 is " + msgs.size());
56
assertTrue(
58
assertTrue(
57
msgs.get(0).endsWith(EXPECTED), "Replacement failed - expected ending " + EXPECTED + " Actual " + msgs.get(0));
59
msgs.get(0).endsWith(EXPECTED), "Replacement failed - expected ending " + EXPECTED + " Actual " + msgs.get(0));
58-
app.clear();
60+
61+
}
62+
63+
@Test
64+
public void testMessageReplacement() {
59
ThreadContext.put("MyKey", "Apache");
65
ThreadContext.put("MyKey", "Apache");
60
logger.error("This is a test for ${ctx:MyKey}");
66
logger.error("This is a test for ${ctx:MyKey}");
61-
msgs = app.getMessages();
67+
List<String> msgs = app.getMessages();
62
assertNotNull(msgs);
68
assertNotNull(msgs);
63
assertEquals(1, msgs.size(), "Incorrect number of messages. Should be 1 is " + msgs.size());
69
assertEquals(1, msgs.size(), "Incorrect number of messages. Should be 1 is " + msgs.size());
64
assertEquals("LoggerTest This is a test for Apache" + Strings.LINE_SEPARATOR, msgs.get(0));
70
assertEquals("LoggerTest This is a test for Apache" + Strings.LINE_SEPARATOR, msgs.get(0));

log4j-core/src/test/resources/log4j-list-nolookups.xml renamed to log4j-core/src/test/resources/log4j-list-lookups.xml

Lines changed: 1 addition & 1 deletion
Original file line numberOriginal file lineDiff line numberDiff line change
@@ -18,7 +18,7 @@
18
<Configuration status="WARN">
18
<Configuration status="WARN">
19
<Appenders>
19
<Appenders>
20
<List name="List">
20
<List name="List">
21-
<PatternLayout pattern="[%-5level] %c{1.} %msg{ansi}{nolookups}%n" />
21+
<PatternLayout pattern="[%-5level] %c{1.} %msg{ansi}{lookups}%n" />
22
</List>
22
</List>
23
</Appenders>
23
</Appenders>
24
<Loggers>
24
<Loggers>

log4j-core/src/test/resources/log4j-replace.xml

Lines changed: 2 additions & 2 deletions
Original file line numberOriginal file lineDiff line numberDiff line change
@@ -21,12 +21,12 @@
21
<List name="List">
21
<List name="List">
22
<PatternLayout>
22
<PatternLayout>
23
<replace regex="\." replacement="/"/>
23
<replace regex="\." replacement="/"/>
24-
<Pattern>%logger %msg%n</Pattern>
24+
<Pattern>%logger %msg{lookups}%n</Pattern>
25
</PatternLayout>
25
</PatternLayout>
26
</List>
26
</List>
27
<List name="List2">
27
<List name="List2">
28
<PatternLayout>
28
<PatternLayout>
29-
<Pattern>%replace{%logger %C{1.} %msg%n}{\.}{/}</Pattern>
29+
<Pattern>%replace{%logger %C{1.} %msg{lookups}%n}{\.}{/}</Pattern>
30
</PatternLayout>
30
</PatternLayout>
31
</List>
31
</List>
32
</Appenders>
32
</Appenders>

src/changes/changes.xml

Lines changed: 6 additions & 0 deletions
Original file line numberOriginal file lineDiff line numberDiff line change
@@ -31,6 +31,12 @@
31
-->
31
-->
32
<release version="2.15.0" date="2021-MM-DD" description="GA Release 2.15.0">
32
<release version="2.15.0" date="2021-MM-DD" description="GA Release 2.15.0">
33
<!-- ADDS -->
33
<!-- ADDS -->
34+
<action issue="LOG4J2-3198" dev="ckozak" type="add">
35+
Pattern layout no longer enables lookups within message text by default for cleaner API boundaries and reduced
36+
formatting overhead. The old 'log4j2.formatMsgNoLookups' which enabled this behavior has been removed as well
37+
as the 'nolookups' message pattern converter option. The old behavior can be enabled on a per-pattern basis
38+
using '%m{lookups}'.
39+
</action>
34
<action issue="LOG4J2-3194" dev="rgoers" type="add" due-to="markuss">
40
<action issue="LOG4J2-3194" dev="rgoers" type="add" due-to="markuss">
35
Allow fractional attributes for size attribute of SizeBsaedTriggeringPolicy.
41
Allow fractional attributes for size attribute of SizeBsaedTriggeringPolicy.
36
</action>
42
</action>

src/site/xdoc/manual/configuration.xml.vm

Lines changed: 5 additions & 16 deletions
Original file line numberOriginal file lineDiff line numberDiff line change
@@ -1264,14 +1264,13 @@ rootLogger.appenderRef.stdout.ref = STDOUT
1264
<code>app.properties</code> would be used as the default value.
1264
<code>app.properties</code> would be used as the default value.
1265
</p>
1265
</p>
1266
</subsection>
1266
</subsection>
1267-
<a name="DisablingMessagePatternLookups"/>
1267+
<a name="EnablingMessagePatternLookups"/>
1268-
<subsection name="Disables Message Pattern Lookups">
1268+
<subsection name="Enabling Message Pattern Lookups">
1269
<p>
1269
<p>
1270-
A message is processed (by default) by lookups, for example if you defined
1270+
A message is processed (by default) without using lookups, for example if you defined
1271
<code> &lt;Property name="foo.bar">FOO_BAR &lt;/Property></code>, then <code>logger.info("${foo.bar}")</code>
1271
<code> &lt;Property name="foo.bar">FOO_BAR &lt;/Property></code>, then <code>logger.info("${foo.bar}")</code>
1272-
will output <code>FOO_BAR</code> instead of <code>${dollar}{foo.bar}</code>. You could disable message pattern
1272+
will output <code>${dollar}{foo.bar}</code> instead of <code>FOO_BAR</code>. You could enable message pattern
1273-
lookups globally by setting system property <code>log4j2.formatMsgNoLookups</code> to true,
1273+
lookups by defining message pattern using %m{lookups}.
1274-
or defining message pattern using %m{nolookups}.
1275
</p>
1274
</p>
1276
</subsection>
1275
</subsection>
1277
<a name="RuntimeLookup"/>
1276
<a name="RuntimeLookup"/>
@@ -2672,16 +2671,6 @@ public class AwesomeTest {
2672
<td>Prints a stacktrace to the <a href="#StatusMessages">status logger</a> at DEBUG level
2671
<td>Prints a stacktrace to the <a href="#StatusMessages">status logger</a> at DEBUG level
2673
when the LoggerContext is started. For debug purposes.</td>
2672
when the LoggerContext is started. For debug purposes.</td>
2674
</tr>
2673
</tr>
2675-
<tr>
2676-
<td><a name="formatMsgNoLookups"/>log4j2.formatMsgNoLookups
2677-
<br />
2678-
(<a name="log4j2.formatMsgNoLookups" />log4j2.formatMsgNoLookups)
2679-
</td>
2680-
<td>FORMAT_MESSAGES_PATTERN_DISABLE_LOOKUPS</td>
2681-
<td>false</td>
2682-
<td>Disables message pattern lookups globally when set to <tt>true</tt>.
2683-
This is equivalent to defining all message patterns using <tt>%m{nolookups}</tt>.</td>
2684-
</tr>
2685
<tr>
2674
<tr>
2686
<td><a name="log4j2.trustStoreLocation "/>log4j2.trustStoreLocation</td>
2675
<td><a name="log4j2.trustStoreLocation "/>log4j2.trustStoreLocation</td>
2687
<td>LOG4J_TRUST_STORE_LOCATION</td>
2676
<td>LOG4J_TRUST_STORE_LOCATION</td>

0 commit comments

Comments
 (0)