Skip to content

Commit 9c3e24a

Browse files
committed
Add support for custom context data in ctx lookup
This backports (from the `2.25.x` branch), the support for custom context data providers in the `ctx` lookup. The feature was first introduced in #2438. Includes #2846 review.
1 parent 78af316 commit 9c3e24a

File tree

9 files changed

+83
-58
lines changed

9 files changed

+83
-58
lines changed

log4j-core/src/main/java/org/apache/logging/log4j/core/ContextDataInjector.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,19 @@ public interface ContextDataInjector {
105105
* the implementation of this method. It is not safe to pass the returned object to another thread.
106106
* </p>
107107
* @return a {@code ReadOnlyStringMap} object reflecting the current state of the context, may not return {@code null}
108+
* @deprecated Since 2.24.0 use {@link #getValue} instead.
108109
*/
110+
@Deprecated
109111
ReadOnlyStringMap rawContextData();
112+
113+
/**
114+
* Retrieves a single context data value.
115+
*
116+
* @param key The context data key of the value to retrieve.
117+
* @return A context data value.
118+
* @since 2.24.0
119+
*/
120+
default Object getValue(final String key) {
121+
return rawContextData().getValue(key);
122+
}
110123
}

log4j-core/src/main/java/org/apache/logging/log4j/core/filter/DynamicThresholdFilter.java

Lines changed: 16 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,6 @@
3636
import org.apache.logging.log4j.core.util.KeyValuePair;
3737
import org.apache.logging.log4j.message.Message;
3838
import org.apache.logging.log4j.util.PerformanceSensitive;
39-
import org.apache.logging.log4j.util.ReadOnlyStringMap;
4039
import org.apache.logging.log4j.util.StringMap;
4140

4241
/**
@@ -125,10 +124,9 @@ public boolean equals(final Object obj) {
125124
return true;
126125
}
127126

128-
private Result filter(final Level level, final ReadOnlyStringMap contextMap) {
129-
final String value = contextMap.getValue(key);
127+
private Result filter(final Level level, final Object value) {
130128
if (value != null) {
131-
Level ctxLevel = levelMap.get(value);
129+
Level ctxLevel = levelMap.get(Objects.toString(value, null));
132130
if (ctxLevel == null) {
133131
ctxLevel = defaultThreshold;
134132
}
@@ -139,35 +137,31 @@ private Result filter(final Level level, final ReadOnlyStringMap contextMap) {
139137

140138
@Override
141139
public Result filter(final LogEvent event) {
142-
return filter(event.getLevel(), event.getContextData());
140+
return filter(event.getLevel(), event.getContextData().getValue(key));
143141
}
144142

145143
@Override
146144
public Result filter(
147145
final Logger logger, final Level level, final Marker marker, final Message msg, final Throwable t) {
148-
return filter(level, currentContextData());
146+
return filter(level, injector.getValue(key));
149147
}
150148

151149
@Override
152150
public Result filter(
153151
final Logger logger, final Level level, final Marker marker, final Object msg, final Throwable t) {
154-
return filter(level, currentContextData());
152+
return filter(level, injector.getValue(key));
155153
}
156154

157155
@Override
158156
public Result filter(
159157
final Logger logger, final Level level, final Marker marker, final String msg, final Object... params) {
160-
return filter(level, currentContextData());
161-
}
162-
163-
private ReadOnlyStringMap currentContextData() {
164-
return injector.rawContextData();
158+
return filter(level, injector.getValue(key));
165159
}
166160

167161
@Override
168162
public Result filter(
169163
final Logger logger, final Level level, final Marker marker, final String msg, final Object p0) {
170-
return filter(level, currentContextData());
164+
return filter(level, injector.getValue(key));
171165
}
172166

173167
@Override
@@ -178,7 +172,7 @@ public Result filter(
178172
final String msg,
179173
final Object p0,
180174
final Object p1) {
181-
return filter(level, currentContextData());
175+
return filter(level, injector.getValue(key));
182176
}
183177

184178
@Override
@@ -190,7 +184,7 @@ public Result filter(
190184
final Object p0,
191185
final Object p1,
192186
final Object p2) {
193-
return filter(level, currentContextData());
187+
return filter(level, injector.getValue(key));
194188
}
195189

196190
@Override
@@ -203,7 +197,7 @@ public Result filter(
203197
final Object p1,
204198
final Object p2,
205199
final Object p3) {
206-
return filter(level, currentContextData());
200+
return filter(level, injector.getValue(key));
207201
}
208202

209203
@Override
@@ -217,7 +211,7 @@ public Result filter(
217211
final Object p2,
218212
final Object p3,
219213
final Object p4) {
220-
return filter(level, currentContextData());
214+
return filter(level, injector.getValue(key));
221215
}
222216

223217
@Override
@@ -232,7 +226,7 @@ public Result filter(
232226
final Object p3,
233227
final Object p4,
234228
final Object p5) {
235-
return filter(level, currentContextData());
229+
return filter(level, injector.getValue(key));
236230
}
237231

238232
@Override
@@ -248,7 +242,7 @@ public Result filter(
248242
final Object p4,
249243
final Object p5,
250244
final Object p6) {
251-
return filter(level, currentContextData());
245+
return filter(level, injector.getValue(key));
252246
}
253247

254248
@Override
@@ -265,7 +259,7 @@ public Result filter(
265259
final Object p5,
266260
final Object p6,
267261
final Object p7) {
268-
return filter(level, currentContextData());
262+
return filter(level, injector.getValue(key));
269263
}
270264

271265
@Override
@@ -283,7 +277,7 @@ public Result filter(
283277
final Object p6,
284278
final Object p7,
285279
final Object p8) {
286-
return filter(level, currentContextData());
280+
return filter(level, injector.getValue(key));
287281
}
288282

289283
@Override
@@ -302,7 +296,7 @@ public Result filter(
302296
final Object p7,
303297
final Object p8,
304298
final Object p9) {
305-
return filter(level, currentContextData());
299+
return filter(level, injector.getValue(key));
306300
}
307301

308302
public String getKey() {

log4j-core/src/main/java/org/apache/logging/log4j/core/filter/ThreadContextMapFilter.java

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import java.util.Iterator;
2222
import java.util.List;
2323
import java.util.Map;
24+
import java.util.Objects;
2425
import org.apache.logging.log4j.Level;
2526
import org.apache.logging.log4j.Marker;
2627
import org.apache.logging.log4j.core.ContextDataInjector;
@@ -39,7 +40,6 @@
3940
import org.apache.logging.log4j.message.Message;
4041
import org.apache.logging.log4j.util.IndexedReadOnlyStringMap;
4142
import org.apache.logging.log4j.util.PerformanceSensitive;
42-
import org.apache.logging.log4j.util.ReadOnlyStringMap;
4343
import org.apache.logging.log4j.util.StringMap;
4444

4545
/**
@@ -109,26 +109,22 @@ public Result filter(
109109
private Result filter() {
110110
boolean match = false;
111111
if (useMap) {
112-
ReadOnlyStringMap currentContextData = null;
113112
final IndexedReadOnlyStringMap map = getStringMap();
114113
for (int i = 0; i < map.size(); i++) {
115-
if (currentContextData == null) {
116-
currentContextData = currentContextData();
117-
}
118-
final String toMatch = currentContextData.getValue(map.getKeyAt(i));
119-
match = toMatch != null && ((List<String>) map.getValueAt(i)).contains(toMatch);
114+
final String toMatch = getContextValue(map.getKeyAt(i));
115+
match = toMatch != null && map.<List<String>>getValueAt(i).contains(toMatch);
120116
if ((!isAnd() && match) || (isAnd() && !match)) {
121117
break;
122118
}
123119
}
124120
} else {
125-
match = value.equals(currentContextData().getValue(key));
121+
match = value.equals(getContextValue(key));
126122
}
127123
return match ? onMatch : onMismatch;
128124
}
129125

130-
private ReadOnlyStringMap currentContextData() {
131-
return injector.rawContextData();
126+
private String getContextValue(final String key) {
127+
return Objects.toString(injector.getValue(key), null);
132128
}
133129

134130
@Override

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

Lines changed: 23 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -87,19 +87,33 @@ private static List<ContextDataProvider> getServiceProviders() {
8787
return Collections.unmodifiableList(providers);
8888
}
8989

90+
private abstract static class AbstractContextDataInjector implements ContextDataInjector {
91+
92+
final List<ContextDataProvider> providers;
93+
94+
AbstractContextDataInjector() {
95+
this.providers = getProviders();
96+
}
97+
98+
@Override
99+
public Object getValue(String key) {
100+
for (final ContextDataProvider provider : providers) {
101+
final Object value = provider.getValue(key);
102+
if (value != null) {
103+
return value;
104+
}
105+
}
106+
return null;
107+
}
108+
}
109+
90110
/**
91111
* Default {@code ContextDataInjector} for the legacy {@code Map<String, String>}-based ThreadContext (which is
92112
* also the ThreadContext implementation used for web applications).
93113
* <p>
94114
* This injector always puts key-value pairs into the specified reusable StringMap.
95115
*/
96-
public static class ForDefaultThreadContextMap implements ContextDataInjector {
97-
98-
private final List<ContextDataProvider> providers;
99-
100-
public ForDefaultThreadContextMap() {
101-
providers = getProviders();
102-
}
116+
public static class ForDefaultThreadContextMap extends AbstractContextDataInjector {
103117

104118
/**
105119
* Puts key-value pairs from both the specified list of properties as well as the thread context into the
@@ -171,12 +185,7 @@ public ReadOnlyStringMap rawContextData() {
171185
* <p>
172186
* This injector always puts key-value pairs into the specified reusable StringMap.
173187
*/
174-
public static class ForGarbageFreeThreadContextMap implements ContextDataInjector {
175-
private final List<ContextDataProvider> providers;
176-
177-
public ForGarbageFreeThreadContextMap() {
178-
this.providers = getProviders();
179-
}
188+
public static class ForGarbageFreeThreadContextMap extends AbstractContextDataInjector {
180189

181190
/**
182191
* Puts key-value pairs from both the specified list of properties as well as the thread context into the
@@ -212,12 +221,8 @@ public ReadOnlyStringMap rawContextData() {
212221
* structure. Otherwise the configuration properties are combined with the thread context key-value pairs into the
213222
* specified reusable StringMap.
214223
*/
215-
public static class ForCopyOnWriteThreadContextMap implements ContextDataInjector {
216-
private final List<ContextDataProvider> providers;
224+
public static class ForCopyOnWriteThreadContextMap extends AbstractContextDataInjector {
217225

218-
public ForCopyOnWriteThreadContextMap() {
219-
this.providers = getProviders();
220-
}
221226
/**
222227
* If there are no configuration properties, this injector will return the thread context's internal data
223228
* structure. Otherwise the configuration properties are combined with the thread context key-value pairs into the

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
* Log4j 2 private implementation classes.
1919
*/
2020
@Export
21-
@Version("2.23.0")
21+
@Version("2.24.0")
2222
package org.apache.logging.log4j.core.impl;
2323

2424
import org.osgi.annotation.bundle.Export;

log4j-core/src/main/java/org/apache/logging/log4j/core/lookup/ContextMapLookup.java

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,12 @@
1616
*/
1717
package org.apache.logging.log4j.core.lookup;
1818

19+
import java.util.Objects;
1920
import org.apache.logging.log4j.ThreadContext;
2021
import org.apache.logging.log4j.core.ContextDataInjector;
2122
import org.apache.logging.log4j.core.LogEvent;
2223
import org.apache.logging.log4j.core.config.plugins.Plugin;
2324
import org.apache.logging.log4j.core.impl.ContextDataInjectorFactory;
24-
import org.apache.logging.log4j.util.ReadOnlyStringMap;
2525

2626
/**
2727
* Looks up keys from the context. By default this is the {@link ThreadContext}, but users may
@@ -40,11 +40,7 @@ public class ContextMapLookup implements StrLookup {
4040
*/
4141
@Override
4242
public String lookup(final String key) {
43-
return currentContextData().getValue(key);
44-
}
45-
46-
private ReadOnlyStringMap currentContextData() {
47-
return injector.rawContextData();
43+
return Objects.toString(injector.getValue(key), null);
4844
}
4945

5046
/**

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

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,4 +46,17 @@ public interface ContextDataProvider {
4646
default StringMap supplyStringMap() {
4747
return new JdkMapAdapterStringMap(supplyContextData(), true);
4848
}
49+
50+
/**
51+
* Retrieves a single context data value.
52+
* <p>
53+
* This method avoids the overhead of copying the entire context data, when only a single value is needed.
54+
* </p>
55+
* @param key The context data key of the value to retrieve.
56+
* @return A context data value.
57+
* @since 2.24.0
58+
*/
59+
default Object getValue(final String key) {
60+
return supplyContextData().get(key);
61+
}
4962
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
* Log4j 2 helper classes.
1919
*/
2020
@Export
21-
@Version("2.20.2")
21+
@Version("2.24.0")
2222
package org.apache.logging.log4j.core.util;
2323

2424
import org.osgi.annotation.bundle.Export;
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<entry xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
3+
xmlns="https://logging.apache.org/xml/ns"
4+
xsi:schemaLocation="https://logging.apache.org/xml/ns https://logging.apache.org/xml/ns/log4j-changelog-0.xsd"
5+
type="fixed">
6+
<issue id="2331" link="https://github.com/apache/logging-log4j2/issues/2331"/>
7+
<description format="asciidoc">Fix custom thread-context data provider handling in lookups and filters.</description>
8+
</entry>

0 commit comments

Comments
 (0)