Skip to content

Commit f94d243

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 e6accaa commit f94d243

File tree

7 files changed

+81
-56
lines changed

7 files changed

+81
-56
lines changed

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

+13
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,19 @@ public interface ContextDataInjector {
107107
* the implementation of this method. It is not safe to pass the returned object to another thread.
108108
* </p>
109109
* @return a {@code ReadOnlyStringMap} object reflecting the current state of the context, may not return {@code null}
110+
* @deprecated Since 2.24.0 use {@link #getValue} instead.
110111
*/
112+
@Deprecated
111113
ReadOnlyStringMap rawContextData();
114+
115+
/**
116+
* Retrieves a single context data value.
117+
*
118+
* @param key The context data key of the value to retrieve.
119+
* @return A context data value.
120+
* @since 2.24.0
121+
*/
122+
default Object getValue(final String key) {
123+
return rawContextData().getValue(key);
124+
}
112125
}

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

+16-22
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,6 @@
3939
import org.apache.logging.log4j.plugins.PluginElement;
4040
import org.apache.logging.log4j.plugins.PluginFactory;
4141
import org.apache.logging.log4j.util.PerformanceSensitive;
42-
import org.apache.logging.log4j.util.ReadOnlyStringMap;
4342
import org.apache.logging.log4j.util.StringMap;
4443

4544
/**
@@ -162,10 +161,9 @@ public boolean equals(final Object obj) {
162161
return true;
163162
}
164163

165-
private Result filter(final Level level, final ReadOnlyStringMap contextMap) {
166-
final String value = contextMap.getValue(key);
164+
private Result filter(final Level level, final Object value) {
167165
if (value != null) {
168-
Level ctxLevel = levelMap.get(value);
166+
Level ctxLevel = levelMap.get(Objects.toString(value, null));
169167
if (ctxLevel == null) {
170168
ctxLevel = defaultThreshold;
171169
}
@@ -176,35 +174,31 @@ private Result filter(final Level level, final ReadOnlyStringMap contextMap) {
176174

177175
@Override
178176
public Result filter(final LogEvent event) {
179-
return filter(event.getLevel(), event.getContextData());
177+
return filter(event.getLevel(), event.getContextData().getValue(key));
180178
}
181179

182180
@Override
183181
public Result filter(
184182
final Logger logger, final Level level, final Marker marker, final Message msg, final Throwable t) {
185-
return filter(level, currentContextData());
183+
return filter(level, injector.getValue(key));
186184
}
187185

188186
@Override
189187
public Result filter(
190188
final Logger logger, final Level level, final Marker marker, final Object msg, final Throwable t) {
191-
return filter(level, currentContextData());
189+
return filter(level, injector.getValue(key));
192190
}
193191

194192
@Override
195193
public Result filter(
196194
final Logger logger, final Level level, final Marker marker, final String msg, final Object... params) {
197-
return filter(level, currentContextData());
198-
}
199-
200-
private ReadOnlyStringMap currentContextData() {
201-
return injector.rawContextData();
195+
return filter(level, injector.getValue(key));
202196
}
203197

204198
@Override
205199
public Result filter(
206200
final Logger logger, final Level level, final Marker marker, final String msg, final Object p0) {
207-
return filter(level, currentContextData());
201+
return filter(level, injector.getValue(key));
208202
}
209203

210204
@Override
@@ -215,7 +209,7 @@ public Result filter(
215209
final String msg,
216210
final Object p0,
217211
final Object p1) {
218-
return filter(level, currentContextData());
212+
return filter(level, injector.getValue(key));
219213
}
220214

221215
@Override
@@ -227,7 +221,7 @@ public Result filter(
227221
final Object p0,
228222
final Object p1,
229223
final Object p2) {
230-
return filter(level, currentContextData());
224+
return filter(level, injector.getValue(key));
231225
}
232226

233227
@Override
@@ -240,7 +234,7 @@ public Result filter(
240234
final Object p1,
241235
final Object p2,
242236
final Object p3) {
243-
return filter(level, currentContextData());
237+
return filter(level, injector.getValue(key));
244238
}
245239

246240
@Override
@@ -254,7 +248,7 @@ public Result filter(
254248
final Object p2,
255249
final Object p3,
256250
final Object p4) {
257-
return filter(level, currentContextData());
251+
return filter(level, injector.getValue(key));
258252
}
259253

260254
@Override
@@ -269,7 +263,7 @@ public Result filter(
269263
final Object p3,
270264
final Object p4,
271265
final Object p5) {
272-
return filter(level, currentContextData());
266+
return filter(level, injector.getValue(key));
273267
}
274268

275269
@Override
@@ -285,7 +279,7 @@ public Result filter(
285279
final Object p4,
286280
final Object p5,
287281
final Object p6) {
288-
return filter(level, currentContextData());
282+
return filter(level, injector.getValue(key));
289283
}
290284

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

308302
@Override
@@ -320,7 +314,7 @@ public Result filter(
320314
final Object p6,
321315
final Object p7,
322316
final Object p8) {
323-
return filter(level, currentContextData());
317+
return filter(level, injector.getValue(key));
324318
}
325319

326320
@Override
@@ -339,7 +333,7 @@ public Result filter(
339333
final Object p7,
340334
final Object p8,
341335
final Object p9) {
342-
return filter(level, currentContextData());
336+
return filter(level, injector.getValue(key));
343337
}
344338

345339
public String getKey() {

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

+6-10
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 java.util.function.Supplier;
2526
import org.apache.logging.log4j.Level;
2627
import org.apache.logging.log4j.Marker;
@@ -42,7 +43,6 @@
4243
import org.apache.logging.log4j.plugins.validation.constraints.Required;
4344
import org.apache.logging.log4j.util.IndexedReadOnlyStringMap;
4445
import org.apache.logging.log4j.util.PerformanceSensitive;
45-
import org.apache.logging.log4j.util.ReadOnlyStringMap;
4646
import org.apache.logging.log4j.util.StringMap;
4747

4848
/**
@@ -115,26 +115,22 @@ public Result filter(
115115
private Result filter() {
116116
boolean match = false;
117117
if (useMap) {
118-
ReadOnlyStringMap currentContextData = null;
119118
final IndexedReadOnlyStringMap map = getStringMap();
120119
for (int i = 0; i < map.size(); i++) {
121-
if (currentContextData == null) {
122-
currentContextData = currentContextData();
123-
}
124-
final String toMatch = currentContextData.getValue(map.getKeyAt(i));
125-
match = toMatch != null && ((List<String>) map.getValueAt(i)).contains(toMatch);
120+
final String toMatch = getContextValue(map.getKeyAt(i));
121+
match = toMatch != null && map.<List<String>>getValueAt(i).contains(toMatch);
126122
if ((!isAnd() && match) || (isAnd() && !match)) {
127123
break;
128124
}
129125
}
130126
} else {
131-
match = value.equals(currentContextData().getValue(key));
127+
match = value.equals(getContextValue(key));
132128
}
133129
return match ? onMatch : onMismatch;
134130
}
135131

136-
private ReadOnlyStringMap currentContextData() {
137-
return injector.rawContextData();
132+
private String getContextValue(final String key) {
133+
return Objects.toString(injector.getValue(key), null);
138134
}
139135

140136
@Override

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

+23-18
Original file line numberDiff line numberDiff line change
@@ -74,19 +74,33 @@ private static List<ContextDataProvider> getServiceProviders() {
7474
return Collections.unmodifiableList(providers);
7575
}
7676

77+
private abstract static class AbstractContextDataInjector implements ContextDataInjector {
78+
79+
final List<ContextDataProvider> providers;
80+
81+
AbstractContextDataInjector() {
82+
this.providers = getProviders();
83+
}
84+
85+
@Override
86+
public Object getValue(String key) {
87+
for (final ContextDataProvider provider : providers) {
88+
final Object value = provider.getValue(key);
89+
if (value != null) {
90+
return value;
91+
}
92+
}
93+
return null;
94+
}
95+
}
96+
7797
/**
7898
* Default {@code ContextDataInjector} for the legacy {@code Map<String, String>}-based ThreadContext (which is
7999
* also the ThreadContext implementation used for web applications).
80100
* <p>
81101
* This injector always puts key-value pairs into the specified reusable StringMap.
82102
*/
83-
public static class ForDefaultThreadContextMap implements ContextDataInjector {
84-
85-
private final List<ContextDataProvider> providers;
86-
87-
public ForDefaultThreadContextMap() {
88-
providers = getProviders();
89-
}
103+
public static class ForDefaultThreadContextMap extends AbstractContextDataInjector {
90104

91105
/**
92106
* Puts key-value pairs from both the specified list of properties as well as the thread context into the
@@ -158,12 +172,7 @@ public ReadOnlyStringMap rawContextData() {
158172
* <p>
159173
* This injector always puts key-value pairs into the specified reusable StringMap.
160174
*/
161-
public static class ForGarbageFreeThreadContextMap implements ContextDataInjector {
162-
private final List<ContextDataProvider> providers;
163-
164-
public ForGarbageFreeThreadContextMap() {
165-
this.providers = getProviders();
166-
}
175+
public static class ForGarbageFreeThreadContextMap extends AbstractContextDataInjector {
167176

168177
/**
169178
* Puts key-value pairs from both the specified list of properties as well as the thread context into the
@@ -199,12 +208,8 @@ public ReadOnlyStringMap rawContextData() {
199208
* structure. Otherwise the configuration properties are combined with the thread context key-value pairs into the
200209
* specified reusable StringMap.
201210
*/
202-
public static class ForCopyOnWriteThreadContextMap implements ContextDataInjector {
203-
private final List<ContextDataProvider> providers;
211+
public static class ForCopyOnWriteThreadContextMap extends AbstractContextDataInjector {
204212

205-
public ForCopyOnWriteThreadContextMap() {
206-
this.providers = getProviders();
207-
}
208213
/**
209214
* If there are no configuration properties, this injector will return the thread context's internal data
210215
* 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/lookup/ContextMapLookup.java

+2-6
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.impl.ContextDataInjectorFactory;
2324
import org.apache.logging.log4j.plugins.Plugin;
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
@@ -41,11 +41,7 @@ public class ContextMapLookup implements StrLookup {
4141
*/
4242
@Override
4343
public String lookup(final String key) {
44-
return currentContextData().getValue(key);
45-
}
46-
47-
private ReadOnlyStringMap currentContextData() {
48-
return injector.rawContextData();
44+
return Objects.toString(injector.getValue(key), null);
4945
}
5046

5147
/**

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

+13
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
}
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)