Skip to content

Commit a50fe37

Browse files
committed
Fix regression in JdkMapAdapterStringMap performance
We introduce a new constructor for `JdkMapAdapterStringMap` to prevent the performance penalty of an exception if the map is immutable.
1 parent 0540a02 commit a50fe37

File tree

6 files changed

+36
-11
lines changed

6 files changed

+36
-11
lines changed

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ public class JdkMapAdapterStringMapTest {
4444
@Test
4545
public void testConstructorDisallowsNull() {
4646
assertThrows(NullPointerException.class, () -> new JdkMapAdapterStringMap(null));
47+
assertThrows(NullPointerException.class, () -> new JdkMapAdapterStringMap(null, false));
4748
}
4849

4950
@Test

log4j-core/src/main/java/org/apache/logging/log4j/core/impl/JdkMapAdapterStringMap.java

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,9 +47,14 @@ public class JdkMapAdapterStringMap implements StringMap {
4747
private transient String[] sortedKeys;
4848

4949
public JdkMapAdapterStringMap() {
50-
this(new HashMap<String, String>());
50+
this(new HashMap<String, String>(), false);
5151
}
5252

53+
/**
54+
* @deprecated for performance reasons since 2.23.
55+
* Use {@link #JdkMapAdapterStringMap(Map, boolean)} instead.
56+
*/
57+
@Deprecated
5358
public JdkMapAdapterStringMap(final Map<String, String> map) {
5459
this.map = Objects.requireNonNull(map, "map");
5560
try {
@@ -59,6 +64,15 @@ public JdkMapAdapterStringMap(final Map<String, String> map) {
5964
}
6065
}
6166

67+
/**
68+
* @param map a JDK map,
69+
* @param immutable must be {@code true} if the map is immutable or it should not be modified.
70+
*/
71+
public JdkMapAdapterStringMap(final Map<String, String> map, final boolean immutable) {
72+
this.map = Objects.requireNonNull(map, "map");
73+
this.immutable = immutable;
74+
}
75+
6276
@Override
6377
public Map<String, String> toMap() {
6478
return map;

log4j-core/src/main/java/org/apache/logging/log4j/core/impl/ThreadContextDataInjector.java

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@ public StringMap injectContextData(final List<Property> props, final StringMap i
121121
// data. Note that we cannot reuse the specified StringMap: some Loggers may have properties defined
122122
// and others not, so the LogEvent's context data may have been replaced with an immutable copy from
123123
// the ThreadContext - this will throw an UnsupportedOperationException if we try to modify it.
124-
final StringMap result = new JdkMapAdapterStringMap(new HashMap<>(copy));
124+
final StringMap result = new JdkMapAdapterStringMap(new HashMap<>(copy), false);
125125
for (int i = 0; i < props.size(); i++) {
126126
final Property prop = props.get(i);
127127
if (!copy.containsKey(prop.getName())) {
@@ -133,20 +133,20 @@ public StringMap injectContextData(final List<Property> props, final StringMap i
133133
}
134134

135135
private static JdkMapAdapterStringMap frozenStringMap(final Map<String, String> copy) {
136-
final JdkMapAdapterStringMap result = new JdkMapAdapterStringMap(copy);
137-
result.freeze();
138-
return result;
136+
return new JdkMapAdapterStringMap(copy, true);
139137
}
140138

141139
@Override
142140
public ReadOnlyStringMap rawContextData() {
143141
final ReadOnlyThreadContextMap map = ThreadContext.getThreadContextMap();
144-
if (map instanceof ReadOnlyStringMap) {
145-
return (ReadOnlyStringMap) map;
142+
if (map != null) {
143+
return map.getReadOnlyContextData();
146144
}
147145
// note: default ThreadContextMap is null
148146
final Map<String, String> copy = ThreadContext.getImmutableContext();
149-
return copy.isEmpty() ? ContextDataFactory.emptyFrozenContextData() : new JdkMapAdapterStringMap(copy);
147+
return copy.isEmpty()
148+
? ContextDataFactory.emptyFrozenContextData()
149+
: new JdkMapAdapterStringMap(copy, true);
150150
}
151151
}
152152

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,6 @@ public interface ContextDataProvider {
3636
* @return the context data in a StringMap.
3737
*/
3838
default StringMap supplyStringMap() {
39-
return new JdkMapAdapterStringMap(supplyContextData());
39+
return new JdkMapAdapterStringMap(supplyContextData(), true);
4040
}
4141
}

log4j-layout-template-json-test/src/test/java/org/apache/logging/log4j/layout/template/json/util/JsonWriterTest.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,7 @@ void test_writeObject_null_StringMap() {
149149

150150
@Test
151151
void test_writeObject_StringMap() {
152-
final StringMap map = new JdkMapAdapterStringMap(Collections.singletonMap("a", "b"));
152+
final StringMap map = new JdkMapAdapterStringMap(Collections.singletonMap("a", "b"), true);
153153
final String expectedJson = "{'a':'b'}".replace('\'', '"');
154154
final String actualJson = withLockedWriterReturning(writer -> writer.use(() -> writer.writeObject(map)));
155155
Assertions.assertThat(actualJson).isEqualTo(expectedJson);
@@ -224,7 +224,7 @@ public String toString() {
224224
put("foo", "bar");
225225
}
226226
}),
227-
new JdkMapAdapterStringMap(Collections.singletonMap("a", "b"))));
227+
new JdkMapAdapterStringMap(Collections.singletonMap("a", "b"), true)));
228228
put("key7", (StringBuilderFormattable) buffer -> buffer.append(7.7777777777777D));
229229
}
230230
};
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<entry xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
3+
xmlns="http://logging.apache.org/log4j/changelog"
4+
xsi:schemaLocation="http://logging.apache.org/log4j/changelog https://logging.apache.org/log4j/changelog-0.1.2.xsd"
5+
type="fixed">
6+
<issue id="2238" link="https://github.com/apache/logging-log4j2/issues/2238"/>
7+
<description format="asciidoc">
8+
Fix regression in `JdkMapAdapterStringMap` performance.
9+
</description>
10+
</entry>

0 commit comments

Comments
 (0)