Skip to content

Commit e5d1c02

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 249a4f1 commit e5d1c02

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
@@ -49,6 +49,7 @@ public class JdkMapAdapterStringMapTest {
4949
@Test
5050
public void testConstructorDisallowsNull() {
5151
assertThrows(NullPointerException.class, () -> new JdkMapAdapterStringMap(null));
52+
assertThrows(NullPointerException.class, () -> new JdkMapAdapterStringMap(null, false));
5253
}
5354

5455
@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
@@ -48,9 +48,14 @@ public class JdkMapAdapterStringMap implements StringMap {
4848
private transient String[] sortedKeys;
4949

5050
public JdkMapAdapterStringMap() {
51-
this(new HashMap<String, String>());
51+
this(new HashMap<String, String>(), false);
5252
}
5353

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

68+
/**
69+
* @param map a JDK map,
70+
* @param immutable must be {@code true} if the map is immutable or it should not be modified.
71+
*/
72+
public JdkMapAdapterStringMap(final Map<String, String> map, final boolean immutable) {
73+
this.map = Objects.requireNonNull(map, "map");
74+
this.immutable = immutable;
75+
}
76+
6377
@Override
6478
public Map<String, String> toMap() {
6579
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
@@ -133,7 +133,7 @@ public StringMap injectContextData(final List<Property> props, final StringMap i
133133
// data. Note that we cannot reuse the specified StringMap: some Loggers may have properties defined
134134
// and others not, so the LogEvent's context data may have been replaced with an immutable copy from
135135
// the ThreadContext - this will throw an UnsupportedOperationException if we try to modify it.
136-
final StringMap result = new JdkMapAdapterStringMap(new HashMap<>(copy));
136+
final StringMap result = new JdkMapAdapterStringMap(new HashMap<>(copy), false);
137137
for (int i = 0; i < props.size(); i++) {
138138
final Property prop = props.get(i);
139139
if (!copy.containsKey(prop.getName())) {
@@ -145,20 +145,20 @@ public StringMap injectContextData(final List<Property> props, final StringMap i
145145
}
146146

147147
private static JdkMapAdapterStringMap frozenStringMap(final Map<String, String> copy) {
148-
final JdkMapAdapterStringMap result = new JdkMapAdapterStringMap(copy);
149-
result.freeze();
150-
return result;
148+
return new JdkMapAdapterStringMap(copy, true);
151149
}
152150

153151
@Override
154152
public ReadOnlyStringMap rawContextData() {
155153
final ReadOnlyThreadContextMap map = ThreadContext.getThreadContextMap();
156-
if (map instanceof ReadOnlyStringMap) {
157-
return (ReadOnlyStringMap) map;
154+
if (map != null) {
155+
return map.getReadOnlyContextData();
158156
}
159157
// note: default ThreadContextMap is null
160158
final Map<String, String> copy = ThreadContext.getImmutableContext();
161-
return copy.isEmpty() ? ContextDataFactory.emptyFrozenContextData() : new JdkMapAdapterStringMap(copy);
159+
return copy.isEmpty()
160+
? ContextDataFactory.emptyFrozenContextData()
161+
: new JdkMapAdapterStringMap(copy, true);
162162
}
163163
}
164164

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
@@ -150,7 +150,7 @@ void test_writeObject_null_StringMap() {
150150

151151
@Test
152152
void test_writeObject_StringMap() {
153-
final StringMap map = new JdkMapAdapterStringMap(Collections.singletonMap("a", "b"));
153+
final StringMap map = new JdkMapAdapterStringMap(Collections.singletonMap("a", "b"), true);
154154
final String expectedJson = "{'a':'b'}".replace('\'', '"');
155155
final String actualJson = withLockedWriterReturning(writer -> writer.use(() -> writer.writeObject(map)));
156156
Assertions.assertThat(actualJson).isEqualTo(expectedJson);
@@ -225,7 +225,7 @@ public String toString() {
225225
put("foo", "bar");
226226
}
227227
}),
228-
new JdkMapAdapterStringMap(Collections.singletonMap("a", "b"))));
228+
new JdkMapAdapterStringMap(Collections.singletonMap("a", "b"), true)));
229229
put("key7", (StringBuilderFormattable) buffer -> buffer.append(7.7777777777777D));
230230
}
231231
};
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)