diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/ContextDataInjector.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/ContextDataInjector.java index 6d386fe139f..e61bfe57440 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/ContextDataInjector.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/ContextDataInjector.java @@ -105,6 +105,19 @@ public interface ContextDataInjector { * the implementation of this method. It is not safe to pass the returned object to another thread. *

* @return a {@code ReadOnlyStringMap} object reflecting the current state of the context, may not return {@code null} + * @deprecated Since 2.24.0 use {@link #getValue} instead. */ + @Deprecated ReadOnlyStringMap rawContextData(); + + /** + * Retrieves a single context data value. + * + * @param key The context data key of the value to retrieve. + * @return A context data value. + * @since 2.24.0 + */ + default Object getValue(final String key) { + return rawContextData().getValue(key); + } } diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/filter/DynamicThresholdFilter.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/filter/DynamicThresholdFilter.java index 25187b0f171..3bd2cf14f45 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/filter/DynamicThresholdFilter.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/filter/DynamicThresholdFilter.java @@ -36,7 +36,6 @@ import org.apache.logging.log4j.core.util.KeyValuePair; import org.apache.logging.log4j.message.Message; import org.apache.logging.log4j.util.PerformanceSensitive; -import org.apache.logging.log4j.util.ReadOnlyStringMap; import org.apache.logging.log4j.util.StringMap; /** @@ -125,10 +124,9 @@ public boolean equals(final Object obj) { return true; } - private Result filter(final Level level, final ReadOnlyStringMap contextMap) { - final String value = contextMap.getValue(key); + private Result filter(final Level level, final Object value) { if (value != null) { - Level ctxLevel = levelMap.get(value); + Level ctxLevel = levelMap.get(Objects.toString(value, null)); if (ctxLevel == null) { ctxLevel = defaultThreshold; } @@ -139,35 +137,31 @@ private Result filter(final Level level, final ReadOnlyStringMap contextMap) { @Override public Result filter(final LogEvent event) { - return filter(event.getLevel(), event.getContextData()); + return filter(event.getLevel(), event.getContextData().getValue(key)); } @Override public Result filter( final Logger logger, final Level level, final Marker marker, final Message msg, final Throwable t) { - return filter(level, currentContextData()); + return filter(level, injector.getValue(key)); } @Override public Result filter( final Logger logger, final Level level, final Marker marker, final Object msg, final Throwable t) { - return filter(level, currentContextData()); + return filter(level, injector.getValue(key)); } @Override public Result filter( final Logger logger, final Level level, final Marker marker, final String msg, final Object... params) { - return filter(level, currentContextData()); - } - - private ReadOnlyStringMap currentContextData() { - return injector.rawContextData(); + return filter(level, injector.getValue(key)); } @Override public Result filter( final Logger logger, final Level level, final Marker marker, final String msg, final Object p0) { - return filter(level, currentContextData()); + return filter(level, injector.getValue(key)); } @Override @@ -178,7 +172,7 @@ public Result filter( final String msg, final Object p0, final Object p1) { - return filter(level, currentContextData()); + return filter(level, injector.getValue(key)); } @Override @@ -190,7 +184,7 @@ public Result filter( final Object p0, final Object p1, final Object p2) { - return filter(level, currentContextData()); + return filter(level, injector.getValue(key)); } @Override @@ -203,7 +197,7 @@ public Result filter( final Object p1, final Object p2, final Object p3) { - return filter(level, currentContextData()); + return filter(level, injector.getValue(key)); } @Override @@ -217,7 +211,7 @@ public Result filter( final Object p2, final Object p3, final Object p4) { - return filter(level, currentContextData()); + return filter(level, injector.getValue(key)); } @Override @@ -232,7 +226,7 @@ public Result filter( final Object p3, final Object p4, final Object p5) { - return filter(level, currentContextData()); + return filter(level, injector.getValue(key)); } @Override @@ -248,7 +242,7 @@ public Result filter( final Object p4, final Object p5, final Object p6) { - return filter(level, currentContextData()); + return filter(level, injector.getValue(key)); } @Override @@ -265,7 +259,7 @@ public Result filter( final Object p5, final Object p6, final Object p7) { - return filter(level, currentContextData()); + return filter(level, injector.getValue(key)); } @Override @@ -283,7 +277,7 @@ public Result filter( final Object p6, final Object p7, final Object p8) { - return filter(level, currentContextData()); + return filter(level, injector.getValue(key)); } @Override @@ -302,7 +296,7 @@ public Result filter( final Object p7, final Object p8, final Object p9) { - return filter(level, currentContextData()); + return filter(level, injector.getValue(key)); } public String getKey() { diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/filter/ThreadContextMapFilter.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/filter/ThreadContextMapFilter.java index 21e6e1da1ba..a224bd4e39a 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/filter/ThreadContextMapFilter.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/filter/ThreadContextMapFilter.java @@ -21,6 +21,7 @@ import java.util.Iterator; import java.util.List; import java.util.Map; +import java.util.Objects; import org.apache.logging.log4j.Level; import org.apache.logging.log4j.Marker; import org.apache.logging.log4j.core.ContextDataInjector; @@ -39,7 +40,6 @@ import org.apache.logging.log4j.message.Message; import org.apache.logging.log4j.util.IndexedReadOnlyStringMap; import org.apache.logging.log4j.util.PerformanceSensitive; -import org.apache.logging.log4j.util.ReadOnlyStringMap; import org.apache.logging.log4j.util.StringMap; /** @@ -109,26 +109,22 @@ public Result filter( private Result filter() { boolean match = false; if (useMap) { - ReadOnlyStringMap currentContextData = null; final IndexedReadOnlyStringMap map = getStringMap(); for (int i = 0; i < map.size(); i++) { - if (currentContextData == null) { - currentContextData = currentContextData(); - } - final String toMatch = currentContextData.getValue(map.getKeyAt(i)); - match = toMatch != null && ((List) map.getValueAt(i)).contains(toMatch); + final String toMatch = getContextValue(map.getKeyAt(i)); + match = toMatch != null && map.>getValueAt(i).contains(toMatch); if ((!isAnd() && match) || (isAnd() && !match)) { break; } } } else { - match = value.equals(currentContextData().getValue(key)); + match = value.equals(getContextValue(key)); } return match ? onMatch : onMismatch; } - private ReadOnlyStringMap currentContextData() { - return injector.rawContextData(); + private String getContextValue(final String key) { + return Objects.toString(injector.getValue(key), null); } @Override diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/ThreadContextDataInjector.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/ThreadContextDataInjector.java index 332b877a51f..1bf0f5b29d4 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/ThreadContextDataInjector.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/ThreadContextDataInjector.java @@ -87,19 +87,33 @@ private static List getServiceProviders() { return Collections.unmodifiableList(providers); } + private abstract static class AbstractContextDataInjector implements ContextDataInjector { + + final List providers; + + AbstractContextDataInjector() { + this.providers = getProviders(); + } + + @Override + public Object getValue(String key) { + for (final ContextDataProvider provider : providers) { + final Object value = provider.getValue(key); + if (value != null) { + return value; + } + } + return null; + } + } + /** * Default {@code ContextDataInjector} for the legacy {@code Map}-based ThreadContext (which is * also the ThreadContext implementation used for web applications). *

* This injector always puts key-value pairs into the specified reusable StringMap. */ - public static class ForDefaultThreadContextMap implements ContextDataInjector { - - private final List providers; - - public ForDefaultThreadContextMap() { - providers = getProviders(); - } + public static class ForDefaultThreadContextMap extends AbstractContextDataInjector { /** * 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() { *

* This injector always puts key-value pairs into the specified reusable StringMap. */ - public static class ForGarbageFreeThreadContextMap implements ContextDataInjector { - private final List providers; - - public ForGarbageFreeThreadContextMap() { - this.providers = getProviders(); - } + public static class ForGarbageFreeThreadContextMap extends AbstractContextDataInjector { /** * 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() { * structure. Otherwise the configuration properties are combined with the thread context key-value pairs into the * specified reusable StringMap. */ - public static class ForCopyOnWriteThreadContextMap implements ContextDataInjector { - private final List providers; + public static class ForCopyOnWriteThreadContextMap extends AbstractContextDataInjector { - public ForCopyOnWriteThreadContextMap() { - this.providers = getProviders(); - } /** * If there are no configuration properties, this injector will return the thread context's internal data * structure. Otherwise the configuration properties are combined with the thread context key-value pairs into the diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/package-info.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/package-info.java index c50504a8726..0c3b08f43a7 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/package-info.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/package-info.java @@ -18,7 +18,7 @@ * Log4j 2 private implementation classes. */ @Export -@Version("2.23.0") +@Version("2.24.0") package org.apache.logging.log4j.core.impl; import org.osgi.annotation.bundle.Export; diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/lookup/ContextMapLookup.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/lookup/ContextMapLookup.java index 86778d2bd7f..587fac33154 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/lookup/ContextMapLookup.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/lookup/ContextMapLookup.java @@ -16,12 +16,12 @@ */ package org.apache.logging.log4j.core.lookup; +import java.util.Objects; import org.apache.logging.log4j.ThreadContext; import org.apache.logging.log4j.core.ContextDataInjector; import org.apache.logging.log4j.core.LogEvent; import org.apache.logging.log4j.core.config.plugins.Plugin; import org.apache.logging.log4j.core.impl.ContextDataInjectorFactory; -import org.apache.logging.log4j.util.ReadOnlyStringMap; /** * 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 { */ @Override public String lookup(final String key) { - return currentContextData().getValue(key); - } - - private ReadOnlyStringMap currentContextData() { - return injector.rawContextData(); + return Objects.toString(injector.getValue(key), null); } /** diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/util/ContextDataProvider.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/util/ContextDataProvider.java index 8ac63b6858d..b06e5d73bfe 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/util/ContextDataProvider.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/util/ContextDataProvider.java @@ -46,4 +46,17 @@ public interface ContextDataProvider { default StringMap supplyStringMap() { return new JdkMapAdapterStringMap(supplyContextData(), true); } + + /** + * Retrieves a single context data value. + *

+ * This method avoids the overhead of copying the entire context data, when only a single value is needed. + *

+ * @param key The context data key of the value to retrieve. + * @return A context data value. + * @since 2.24.0 + */ + default Object getValue(final String key) { + return supplyContextData().get(key); + } } diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/util/package-info.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/util/package-info.java index 753b7c456bd..6c602546054 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/util/package-info.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/util/package-info.java @@ -18,7 +18,7 @@ * Log4j 2 helper classes. */ @Export -@Version("2.20.2") +@Version("2.24.0") package org.apache.logging.log4j.core.util; import org.osgi.annotation.bundle.Export; diff --git a/src/changelog/.2.x.x/2331_custom_context_data_in_lookup.xml b/src/changelog/.2.x.x/2331_custom_context_data_in_lookup.xml new file mode 100644 index 00000000000..f075fb3919f --- /dev/null +++ b/src/changelog/.2.x.x/2331_custom_context_data_in_lookup.xml @@ -0,0 +1,8 @@ + + + + Fix custom thread-context data provider handling in lookups and filters. +