diff --git a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/config/AbstractConfigurationTest.java b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/config/AbstractConfigurationTest.java new file mode 100644 index 00000000000..91d4405edf2 --- /dev/null +++ b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/config/AbstractConfigurationTest.java @@ -0,0 +1,99 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.logging.log4j.core.config; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; + +import java.util.Arrays; +import java.util.Collections; +import java.util.Map; +import java.util.Map.Entry; +import org.apache.logging.log4j.core.LoggerContext; +import org.apache.logging.log4j.core.lookup.Interpolator; +import org.apache.logging.log4j.core.lookup.InterpolatorTest; +import org.apache.logging.log4j.core.lookup.StrSubstitutor; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; +import org.junitpioneer.jupiter.Issue; + +class AbstractConfigurationTest { + + @Test + void propertiesCanComeLast() { + final Configuration config = new TestConfiguration(null, Collections.singletonMap("console.name", "CONSOLE")); + config.initialize(); + final StrSubstitutor substitutor = config.getStrSubstitutor(); + assertThat(substitutor.replace("${console.name}")) + .as("No interpolation for '${console.name}'") + .isEqualTo("CONSOLE"); + } + + @ParameterizedTest + @ValueSource(booleans = {false, true}) + @Issue("https://github.com/apache/logging-log4j2/issues/2309") + void substitutorHasConfigurationAndLoggerContext(final boolean hasProperties) { + final LoggerContext context = mock(LoggerContext.class); + final Configuration config = new TestConfiguration(context, hasProperties ? Collections.emptyMap() : null); + config.initialize(); + final Interpolator runtime = (Interpolator) config.getStrSubstitutor().getVariableResolver(); + final Interpolator configTime = + (Interpolator) config.getConfigurationStrSubstitutor().getVariableResolver(); + for (final Interpolator interpolator : Arrays.asList(runtime, configTime)) { + assertThat(InterpolatorTest.getConfiguration(interpolator)).isEqualTo(config); + assertThat(InterpolatorTest.getLoggerContext(interpolator)).isEqualTo(context); + } + } + + private static class TestConfiguration extends AbstractConfiguration { + + private final Map map; + + public TestConfiguration(final LoggerContext context, final Map map) { + super(context, ConfigurationSource.NULL_SOURCE); + this.map = map; + } + + @Override + public void setup() { + // Nodes + final Node loggers = newNode(rootNode, "Loggers"); + rootNode.getChildren().add(loggers); + + final Node rootLogger = newNode(loggers, "Root"); + rootLogger.getAttributes().put("level", "INFO"); + loggers.getChildren().add(rootLogger); + + if (map != null) { + final Node properties = newNode(rootNode, "Properties"); + rootNode.getChildren().add(properties); + + for (final Entry entry : map.entrySet()) { + final Node property = newNode(properties, "Property"); + property.getAttributes().put("name", entry.getKey()); + property.getAttributes().put("value", entry.getValue()); + properties.getChildren().add(property); + } + } + } + + private Node newNode(final Node parent, final String name) { + return new Node(parent, name, pluginManager.getPluginType(name)); + } + } +} diff --git a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/config/ConfigurationPropertiesOrderTest.java b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/config/ConfigurationPropertiesOrderTest.java deleted file mode 100644 index a725283da93..00000000000 --- a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/config/ConfigurationPropertiesOrderTest.java +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to you under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.logging.log4j.core.config; - -import static org.assertj.core.api.Assertions.assertThat; - -import org.apache.logging.log4j.core.Appender; -import org.apache.logging.log4j.core.appender.ConsoleAppender; -import org.junit.jupiter.api.Test; - -public class ConfigurationPropertiesOrderTest { - - @Test - void propertiesCanComeLast() { - final Configuration config = new AbstractConfiguration(null, ConfigurationSource.NULL_SOURCE) { - @Override - public void setup() { - // Nodes - final Node appenders = newNode(rootNode, "Appenders"); - rootNode.getChildren().add(appenders); - - final Node console = newNode(appenders, "Console"); - console.getAttributes().put("name", "${console.name}"); - appenders.getChildren().add(console); - - final Node loggers = newNode(rootNode, "Loggers"); - rootNode.getChildren().add(loggers); - - final Node rootLogger = newNode(loggers, "Root"); - rootLogger.getAttributes().put("level", "INFO"); - loggers.getChildren().add(rootLogger); - - final Node properties = newNode(rootNode, "Properties"); - rootNode.getChildren().add(properties); - - final Node property = newNode(properties, "Property"); - property.getAttributes().put("name", "console.name"); - property.getAttributes().put("value", "CONSOLE"); - properties.getChildren().add(property); - } - - private Node newNode(final Node parent, final String name) { - return new Node(rootNode, name, pluginManager.getPluginType(name)); - } - }; - config.initialize(); - - final Appender noInterpolation = config.getAppender("${console.name}"); - assertThat(noInterpolation).as("No interpolation for '${console.name}'").isNull(); - final Appender console = config.getAppender("CONSOLE"); - assertThat(console).isInstanceOf(ConsoleAppender.class); - } -} diff --git a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/lookup/InterpolatorTest.java b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/lookup/InterpolatorTest.java index c3ad8203f25..38fcf77ee63 100644 --- a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/lookup/InterpolatorTest.java +++ b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/lookup/InterpolatorTest.java @@ -16,11 +16,14 @@ */ package org.apache.logging.log4j.core.lookup; +import static org.assertj.core.api.Assertions.assertThat; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertSame; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; +import static org.mockito.Mockito.mock; import java.text.SimpleDateFormat; import java.util.Collections; @@ -31,6 +34,10 @@ import org.apache.logging.log4j.ThreadContext; import org.apache.logging.log4j.core.LogEvent; import org.apache.logging.log4j.core.Logger; +import org.apache.logging.log4j.core.LoggerContext; +import org.apache.logging.log4j.core.config.Configuration; +import org.apache.logging.log4j.core.config.LoggerContextAware; +import org.apache.logging.log4j.core.config.plugins.Plugin; import org.apache.logging.log4j.core.impl.Log4jLogEvent; import org.apache.logging.log4j.core.test.junit.JndiRule; import org.apache.logging.log4j.message.StringMapMessage; @@ -38,11 +45,13 @@ import org.junit.Test; import org.junit.rules.ExternalResource; import org.junit.rules.RuleChain; +import org.junitpioneer.jupiter.Issue; /** * Tests {@link Interpolator}. */ public class InterpolatorTest { + public static final String TEST_LOOKUP = "interpolator_test"; private static final String TESTKEY = "TestKey"; private static final String TESTKEY2 = "TestKey2"; @@ -177,4 +186,66 @@ public void testInterpolatorMapMessageWithMapPrefix() { .build(); assertEquals("mapMessage", interpolator.lookup(event, "map:key")); } + + @Test + @Issue("https://github.com/apache/logging-log4j2/issues/2309") + public void testContextAndConfigurationPropagation() { + final Interpolator interpolator = new Interpolator(); + assertThat(getConfiguration(interpolator)).isNull(); + assertThat(getLoggerContext(interpolator)).isNull(); + + final Lookup lookup = (Lookup) interpolator.getStrLookupMap().get(TEST_LOOKUP); + assertThat(lookup) + .isNotNull() + .as("Configuration and logger context are null") + .extracting(Lookup::getConfiguration, Lookup::getLoggerContext) + .containsExactly(null, null); + + // Evaluation does not throw, even if config and context are null. + assertDoesNotThrow(() -> interpolator.evaluate(null, TEST_LOOKUP + ":any_key")); + + final Configuration config = mock(Configuration.class); + interpolator.setConfiguration(config); + assertThat(getConfiguration(interpolator)).isEqualTo(config); + assertThat(lookup.getConfiguration()).as("Configuration propagates").isEqualTo(config); + + final LoggerContext context = mock(LoggerContext.class); + interpolator.setLoggerContext(context); + assertThat(getLoggerContext(interpolator)).isEqualTo(context); + assertThat(lookup.getLoggerContext()).as("Logger context propagates").isEqualTo(context); + } + + // Used in tests from other packages + public static Configuration getConfiguration(final Interpolator interpolator) { + return interpolator.configuration; + } + + // Used in tests from other packages + public static LoggerContext getLoggerContext(final Interpolator interpolator) { + return interpolator.loggerContext.get(); + } + + @Plugin(name = TEST_LOOKUP, category = StrLookup.CATEGORY) + public static class Lookup extends AbstractConfigurationAwareLookup implements LoggerContextAware { + + private LoggerContext loggerContext; + + public Configuration getConfiguration() { + return configuration; + } + + public LoggerContext getLoggerContext() { + return loggerContext; + } + + @Override + public void setLoggerContext(final LoggerContext loggerContext) { + this.loggerContext = loggerContext; + } + + @Override + public String lookup(final LogEvent event, final String key) { + return null; + } + } } diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/AbstractConfiguration.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/AbstractConfiguration.java index 10a2bcfab47..014ebcca013 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/AbstractConfiguration.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/AbstractConfiguration.java @@ -665,6 +665,7 @@ protected void doConfigure() { final Map map = this.getComponent(CONTEXT_PROPERTIES); final StrLookup lookup = map == null ? null : new PropertiesLookup(map); final Interpolator interpolator = new Interpolator(lookup, pluginPackages); + interpolator.setConfiguration(this); interpolator.setLoggerContext(loggerContext.get()); runtimeStrSubstitutor.setVariableResolver(interpolator); configurationStrSubstitutor.setVariableResolver(interpolator); diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/PropertiesPlugin.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/PropertiesPlugin.java index aa1fd310676..d944b5aa90f 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/PropertiesPlugin.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/PropertiesPlugin.java @@ -52,8 +52,11 @@ public static StrLookup configureSubstitutor( for (int i = 0; i < unescapedProperties.length; i++) { unescapedProperties[i] = unescape(properties[i]); } - return new Interpolator( + final Interpolator interpolator = new Interpolator( new PropertiesLookup(unescapedProperties, config.getProperties()), config.getPluginPackages()); + interpolator.setConfiguration(config); + interpolator.setLoggerContext(config.getLoggerContext()); + return interpolator; } private static Property unescape(final Property input) { diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/lookup/Interpolator.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/lookup/Interpolator.java index bf6066c0c1a..41f270aac6b 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/lookup/Interpolator.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/lookup/Interpolator.java @@ -26,6 +26,7 @@ import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.core.LogEvent; import org.apache.logging.log4j.core.LoggerContext; +import org.apache.logging.log4j.core.config.Configuration; import org.apache.logging.log4j.core.config.ConfigurationAware; import org.apache.logging.log4j.core.config.LoggerContextAware; import org.apache.logging.log4j.core.config.plugins.util.PluginManager; @@ -60,7 +61,7 @@ public class Interpolator extends AbstractConfigurationAwareLookup implements Lo private final StrLookup defaultLookup; - protected WeakReference loggerContext; + protected WeakReference loggerContext = new WeakReference<>(null); public Interpolator(final StrLookup defaultLookup) { this(defaultLookup, null); @@ -191,12 +192,6 @@ public LookupResult evaluate(final LogEvent event, String var) { final String prefix = toRootLowerCase(var.substring(0, prefixPos)); final String name = var.substring(prefixPos + 1); final StrLookup lookup = strLookupMap.get(prefix); - if (lookup instanceof ConfigurationAware) { - ((ConfigurationAware) lookup).setConfiguration(configuration); - } - if (lookup instanceof LoggerContextAware) { - ((LoggerContextAware) lookup).setLoggerContext(loggerContext.get()); - } LookupResult value = null; if (lookup != null) { value = event == null ? lookup.evaluate(name) : lookup.evaluate(event, name); @@ -214,11 +209,25 @@ public LookupResult evaluate(final LogEvent event, String var) { } @Override - public void setLoggerContext(final LoggerContext loggerContext) { - if (loggerContext == null) { - return; + public void setConfiguration(final Configuration configuration) { + super.setConfiguration(configuration); + // Propagate + for (final StrLookup lookup : strLookupMap.values()) { + if (lookup instanceof ConfigurationAware) { + ((ConfigurationAware) lookup).setConfiguration(configuration); + } } + } + + @Override + public void setLoggerContext(final LoggerContext loggerContext) { this.loggerContext = new WeakReference<>(loggerContext); + // Propagate + for (final StrLookup lookup : strLookupMap.values()) { + if (lookup instanceof LoggerContextAware) { + ((LoggerContextAware) lookup).setLoggerContext(loggerContext); + } + } } @Override diff --git a/log4j-parent/pom.xml b/log4j-parent/pom.xml index 5517c43c0c9..801cf871118 100644 --- a/log4j-parent/pom.xml +++ b/log4j-parent/pom.xml @@ -71,7 +71,7 @@ 1.16.1 1.26.0 1.10.0 - 2.11.0 + 2.12.0 2.15.1 3.14.0 1.3.0 diff --git a/src/changelog/.2.x.x/2309_fix_logger_context_aware_lookups.xml b/src/changelog/.2.x.x/2309_fix_logger_context_aware_lookups.xml new file mode 100644 index 00000000000..1d30fbf238e --- /dev/null +++ b/src/changelog/.2.x.x/2309_fix_logger_context_aware_lookups.xml @@ -0,0 +1,8 @@ + + + + Fix handling of `LoggerContextAware` lookups. + diff --git a/src/changelog/.2.x.x/update_org_apache_commons_commons_dbcp2.xml b/src/changelog/.2.x.x/update_org_apache_commons_commons_dbcp2.xml new file mode 100644 index 00000000000..381c541d8cb --- /dev/null +++ b/src/changelog/.2.x.x/update_org_apache_commons_commons_dbcp2.xml @@ -0,0 +1,8 @@ + + + + Update `org.apache.commons:commons-dbcp2` to version `2.12.0` + diff --git a/src/site/_release-notes/_2.x.x.adoc b/src/site/_release-notes/_2.x.x.adoc index d7acc079e28..38d4c14552a 100644 --- a/src/site/_release-notes/_2.x.x.adoc +++ b/src/site/_release-notes/_2.x.x.adoc @@ -26,13 +26,15 @@ This releases contains ... [#release-notes-2-x-x-changed] === Changed +* Fix handling of `LoggerContextAware` lookups. (https://github.com/apache/logging-log4j2/pull/2309[2309]) * Improve performance of `CloseableThreadContext#closeMap()` (https://github.com/apache/logging-log4j2/pull/2296[2296]) [#release-notes-2-x-x-fixed] === Fixed -* Add `log4j2.StatusLogger.DateFormatZone` system property to set the time-zone `StatusLogger` uses to format `java.time.Instant`. Without this formatting patterns accessing to time-zone-specific fields (e.g., year-of-era) cause failures. -* Fix stack overflow in `StatusLogger` +* Fix `StatusLogger` log level filtering when debug mode is enabled (https://github.com/apache/logging-log4j2/issues/2337[2337]) +* Add `log4j2.StatusLogger.DateFormatZone` system property to set the time-zone `StatusLogger` uses to format `java.time.Instant`. Without this formatting patterns accessing to time-zone-specific fields (e.g., year-of-era) cause failures. (https://github.com/apache/logging-log4j2/pull/2322[2322]) +* Fix stack overflow in `StatusLogger` (https://github.com/apache/logging-log4j2/pull/2322[2322]) [#release-notes-2-x-x-updated] === Updated @@ -44,4 +46,5 @@ This releases contains ... * Update `io.fabric8:docker-maven-plugin` to version `0.44.0` (https://github.com/apache/logging-log4j2/pull/2299[2299]) * Update `jakarta.activation:jakarta.activation-api` to version `2.1.3` (https://github.com/apache/logging-log4j2/pull/2335[2335]) * Update `org.apache.commons:commons-compress` to version `1.26.0` (https://github.com/apache/logging-log4j2/pull/2304[2304]) +* Update `org.apache.commons:commons-dbcp2` to version `2.12.0` (https://github.com/apache/logging-log4j2/pull/2344[2344]) * Update `org.apache.kafka:kafka-clients` to version `3.7.0` (https://github.com/apache/logging-log4j2/pull/2326[2326])