Skip to content

Commit

Permalink
Fix handling of LoggerContextAware lookups
Browse files Browse the repository at this point in the history
Due to the changes in #2278 `LoggerContextAware` lookups stopped working
in `2.23.0`.

This PR:

 * fixes the NPE in `Interpolator` that occurs if
   `Interpolator#setLoggerContext` was **not** called after
   instantiation.
 * Calls `Interpolator#setConfiguration` and
   `Interpolator#setLoggerContext` wherever it is possible.
 * Changes the way `Interpolator` propagates `Configuration` and
   `LoggerContext` to child lookups. Previously this occurred at each
   evaluation, now it occurs only in the setters.

Closes #2309.
  • Loading branch information
ppkarwasz committed Mar 4, 2024
1 parent 28b70d1 commit 395a8f4
Show file tree
Hide file tree
Showing 7 changed files with 202 additions and 78 deletions.
Original file line number Diff line number Diff line change
@@ -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<String, String> map;

public TestConfiguration(final LoggerContext context, final Map<String, String> 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<String, String> 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));
}
}
}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -31,18 +34,24 @@
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;
import org.junit.ClassRule;
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";
Expand Down Expand Up @@ -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;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -665,6 +665,7 @@ protected void doConfigure() {
final Map<String, String> 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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -60,7 +61,7 @@ public class Interpolator extends AbstractConfigurationAwareLookup implements Lo

private final StrLookup defaultLookup;

protected WeakReference<LoggerContext> loggerContext;
protected WeakReference<LoggerContext> loggerContext = new WeakReference<>(null);

public Interpolator(final StrLookup defaultLookup) {
this(defaultLookup, null);
Expand Down Expand Up @@ -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);
Expand All @@ -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
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<entry xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://logging.apache.org/log4j/changelog"
xsi:schemaLocation="http://logging.apache.org/log4j/changelog https://logging.apache.org/log4j/changelog-0.1.3.xsd"
type="changed">
<issue id="2309" link="https://github.com/apache/logging-log4j2/pull/2309"/>
<description format="asciidoc">Fix handling of `LoggerContextAware` lookups.</description>
</entry>

0 comments on commit 395a8f4

Please sign in to comment.