You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@logging.apache.org by rg...@apache.org on 2022/09/06 19:30:20 UTC

[logging-log4j2] 02/02: LOG4J2-3589 - Allow Plugins to be injected with the LoggerContext reference

This is an automated email from the ASF dual-hosted git repository.

rgoers pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/logging-log4j2.git

commit bb0c70ec1afe867a52efee873fabb3197dabeae8
Author: Ralph Goers <rg...@apache.org>
AuthorDate: Tue Sep 6 11:45:48 2022 -0700

    LOG4J2-3589 - Allow Plugins to be injected with the LoggerContext reference
---
 .../apache/logging/log4j/core/LoggerContext.java   | 11 +++-
 .../log4j/core/config/AbstractConfiguration.java   |  8 ++-
 .../log4j/core/config/LoggerContextAware.java      | 35 +++++++++++
 .../core/config/plugins/PluginLoggerContext.java   | 40 +++++++++++++
 .../plugins/visit/PluginLoggerContextVisitor.java  | 70 ++++++++++++++++++++++
 .../lookup/AbstractLoggerContextAwareLookup.java   | 35 +++++++++++
 .../logging/log4j/core/lookup/Interpolator.java    | 25 +++++++-
 .../core/selector/ClassLoaderContextSelector.java  |  1 +
 .../logging/log4j/spring/boot/SpringLookup.java    | 44 ++++++++------
 .../log4j/spring/boot/SpringProfileArbiter.java    | 39 ++++++++++--
 .../spring/boot/Log4j2SpringBootInitTest.java      |  2 +
 .../log4j/spring/boot/SpringLookupTest.java        | 12 +++-
 src/changes/changes.xml                            |  3 +
 13 files changed, 296 insertions(+), 29 deletions(-)

diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/LoggerContext.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/LoggerContext.java
index abdd68cfa6..62d30eb666 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/LoggerContext.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/LoggerContext.java
@@ -25,6 +25,7 @@ import org.apache.logging.log4j.core.config.DefaultConfiguration;
 import org.apache.logging.log4j.core.config.NullConfiguration;
 import org.apache.logging.log4j.core.config.Reconfigurable;
 import org.apache.logging.log4j.core.jmx.Server;
+import org.apache.logging.log4j.core.selector.ContextSelector;
 import org.apache.logging.log4j.core.util.Cancellable;
 import org.apache.logging.log4j.core.util.ExecutorServices;
 import org.apache.logging.log4j.core.util.NetUtils;
@@ -32,6 +33,7 @@ import org.apache.logging.log4j.core.util.ShutdownCallbackRegistry;
 import org.apache.logging.log4j.message.MessageFactory;
 import org.apache.logging.log4j.plugins.di.DI;
 import org.apache.logging.log4j.plugins.di.Injector;
+import org.apache.logging.log4j.plugins.di.Key;
 import org.apache.logging.log4j.spi.AbstractLogger;
 import org.apache.logging.log4j.spi.LoggerContextFactory;
 import org.apache.logging.log4j.spi.LoggerContextShutdownAware;
@@ -70,6 +72,7 @@ public class LoggerContext extends AbstractLifeCycle
      * Property name of the property change event fired if the configuration is changed.
      */
     public static final String PROPERTY_CONFIG = "config";
+    public  static final Key<LoggerContext> KEY = new Key<>() {};
 
     private static final Configuration NULL_CONFIGURATION = new NullConfiguration();
 
@@ -120,6 +123,7 @@ public class LoggerContext extends AbstractLifeCycle
     public LoggerContext(final String name, final Object externalContext, final URI configLocn) {
         this(name, externalContext, configLocn, DI.createInjector());
         injector.init();
+        injector.registerBindingIfAbsent(KEY, () -> this);
     }
 
     /**
@@ -136,7 +140,8 @@ public class LoggerContext extends AbstractLifeCycle
             externalMap.put(EXTERNAL_CONTEXT_KEY, externalContext);
         }
         this.configLocation = configLocn;
-        this.injector = injector;
+        this.injector = injector.copy();
+        injector.registerBindingIfAbsent(KEY, () -> this);
     }
 
     /**
@@ -150,6 +155,7 @@ public class LoggerContext extends AbstractLifeCycle
     public LoggerContext(final String name, final Object externalContext, final String configLocn) {
         this(name, externalContext, configLocn, DI.createInjector());
         injector.init();
+        injector.registerBindingIfAbsent(KEY, () -> this);
     }
 
     /**
@@ -177,7 +183,8 @@ public class LoggerContext extends AbstractLifeCycle
         } else {
             configLocation = null;
         }
-        this.injector = injector;
+        this.injector = injector.copy();
+        this.injector.registerBindingIfAbsent(KEY, () -> this);
     }
 
     public void addShutdownListener(final LoggerContextShutdownAware listener) {
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 8c3adced80..a42cf31263 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
@@ -138,7 +138,7 @@ public abstract class AbstractConfiguration extends AbstractFilterable implement
     private List<CustomLevelConfig> customLevels = List.of();
     private final ConcurrentMap<String, String> properties = new ConcurrentHashMap<>();
     private final InterpolatorFactory interpolatorFactory;
-    private final StrLookup tempLookup;
+    private final Interpolator tempLookup;
     private final StrSubstitutor runtimeStrSubstitutor;
     private final StrSubstitutor configurationStrSubstitutor;
     private LoggerConfig root = new LoggerConfig();
@@ -168,6 +168,7 @@ public abstract class AbstractConfiguration extends AbstractFilterable implement
         componentMap.put(Configuration.CONTEXT_PROPERTIES, properties);
         interpolatorFactory = injector.getInstance(InterpolatorFactory.class);
         tempLookup = interpolatorFactory.newInterpolator(new PropertiesLookup(properties));
+        tempLookup.setLoggerContext(loggerContext);
         runtimeStrSubstitutor = new RuntimeStrSubstitutor(tempLookup);
         configurationStrSubstitutor = new ConfigurationStrSubstitutor(runtimeStrSubstitutor);
         configurationScheduler = injector.getInstance(ConfigurationScheduler.class);
@@ -641,6 +642,7 @@ public abstract class AbstractConfiguration extends AbstractFilterable implement
 
     protected void doConfigure() {
         injector.registerBinding(Keys.SUBSTITUTOR_KEY, () -> configurationStrSubstitutor::replace);
+        injector.registerBinding(LoggerContext.KEY, () -> loggerContext.get());
         processConditionals(rootNode);
         preConfigure(rootNode);
         configurationScheduler.start();
@@ -649,6 +651,9 @@ public abstract class AbstractConfiguration extends AbstractFilterable implement
             createConfiguration(first, null);
             if (first.getObject() != null) {
                 StrLookup lookup = first.getObject();
+                if (lookup instanceof LoggerContextAware) {
+                    ((LoggerContextAware) lookup).setLoggerContext(loggerContext.get());
+                }
                 runtimeStrSubstitutor.setVariableResolver(lookup);
                 configurationStrSubstitutor.setVariableResolver(lookup);
             }
@@ -656,6 +661,7 @@ public abstract class AbstractConfiguration extends AbstractFilterable implement
             final Map<String, String> map = this.getComponent(CONTEXT_PROPERTIES);
             final StrLookup lookup = map == null ? null : new PropertiesLookup(map);
             Interpolator interpolator = interpolatorFactory.newInterpolator(lookup);
+            interpolator.setLoggerContext(loggerContext.get());
             runtimeStrSubstitutor.setVariableResolver(interpolator);
             configurationStrSubstitutor.setVariableResolver(interpolator);
         }
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/LoggerContextAware.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/LoggerContextAware.java
new file mode 100644
index 0000000000..e68af4c277
--- /dev/null
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/LoggerContextAware.java
@@ -0,0 +1,35 @@
+/*
+ * 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 org.apache.logging.log4j.core.LoggerContext;
+
+/**
+ * Indicates that a class requests the current LoggerContext to be injected.
+ *
+ * @since 2.19.0
+ */
+public interface LoggerContextAware {
+
+    /**
+     * Injects the current LoggerContext into this object.
+     *
+     * @param loggerContext the current LoggerContext
+     */
+    void setLoggerContext(LoggerContext loggerContext);
+}
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/PluginLoggerContext.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/PluginLoggerContext.java
new file mode 100644
index 0000000000..2e68001705
--- /dev/null
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/PluginLoggerContext.java
@@ -0,0 +1,40 @@
+/*
+ * 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.plugins;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+import org.apache.logging.log4j.core.config.plugins.visit.PluginLoggerContextVisitor;
+import org.apache.logging.log4j.plugins.QualifierType;
+import org.apache.logging.log4j.plugins.visit.NodeVisitor;
+
+/**
+ * Identifies a parameter or field as a LoggerContext.
+ * @see org.apache.logging.log4j.core.LoggerContext
+ */
+@Documented
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.PARAMETER, ElementType.FIELD, ElementType.METHOD})
+@NodeVisitor.Kind(PluginLoggerContextVisitor.class)
+@QualifierType
+public @interface PluginLoggerContext {
+    // empty
+}
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/visit/PluginLoggerContextVisitor.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/visit/PluginLoggerContextVisitor.java
new file mode 100644
index 0000000000..3306544328
--- /dev/null
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/visit/PluginLoggerContextVisitor.java
@@ -0,0 +1,70 @@
+/*
+ * 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.plugins.visit;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Parameter;
+
+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.plugins.PluginConfiguration;
+import org.apache.logging.log4j.plugins.Inject;
+import org.apache.logging.log4j.plugins.Node;
+import org.apache.logging.log4j.plugins.util.TypeUtil;
+import org.apache.logging.log4j.plugins.visit.NodeVisitor;
+import org.apache.logging.log4j.status.StatusLogger;
+import org.apache.logging.log4j.util.StringBuilders;
+
+/**
+ * PluginVisitor implementation for {@link PluginConfiguration}.
+ */
+public class PluginLoggerContextVisitor implements NodeVisitor {
+    private static final Logger LOGGER = StatusLogger.getLogger();
+    private LoggerContext loggerContext;
+
+    @Inject
+    public PluginLoggerContextVisitor(final LoggerContext loggerContext) {
+        this.loggerContext = loggerContext;
+    }
+
+    @Override
+    public Object visitField(final Field field, final Node node, final StringBuilder debugLog) {
+        if (TypeUtil.isAssignable(field.getGenericType(), LoggerContext.class)) {
+            StringBuilders.appendKeyDqValueWithJoiner(debugLog, "loggerContext", loggerContext, ", ");
+            return TypeUtil.cast(loggerContext);
+        } else {
+            LOGGER.error("Field {} annotated with @PluginLoggerContext is not compatible with type {}", field,
+                    loggerContext.getClass());
+            return null;
+        }
+    }
+
+    @Override
+    public Object visitParameter(final Parameter parameter, final Node node, final StringBuilder debugLog) {
+        if (TypeUtil.isAssignable(parameter.getParameterizedType(), loggerContext.getClass())) {
+            StringBuilders.appendKeyDqValueWithJoiner(debugLog, "loggerContext", loggerContext, ", ");
+            return TypeUtil.cast(loggerContext);
+        } else {
+            LOGGER.error("Parameter {} annotated with @PluginLoggerContext is not compatible with type {}",
+                    parameter, loggerContext.getClass());
+            return null;
+        }
+    }
+}
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/lookup/AbstractLoggerContextAwareLookup.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/lookup/AbstractLoggerContextAwareLookup.java
new file mode 100644
index 0000000000..bec6b4cc9f
--- /dev/null
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/lookup/AbstractLoggerContextAwareLookup.java
@@ -0,0 +1,35 @@
+/*
+ * 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.lookup;
+
+import org.apache.logging.log4j.core.LoggerContext;
+import org.apache.logging.log4j.core.config.LoggerContextAware;
+
+/**
+ * StrLookup that is ConfigurationAware. Handles saving the Configuration.
+ *
+ * @since 2.6
+ */
+public abstract class AbstractLoggerContextAwareLookup extends AbstractLookup implements LoggerContextAware {
+    protected LoggerContext loggerContext;
+
+    @Override
+    public void setLoggerContext(final LoggerContext loggerContext) {
+        this.loggerContext = loggerContext;
+    }
+}
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 8ccdc51f62..647b6ecb40 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
@@ -18,7 +18,10 @@ package org.apache.logging.log4j.core.lookup;
 
 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.ConfigurationAware;
+import org.apache.logging.log4j.core.config.LoggerContextAware;
+import org.apache.logging.log4j.plugins.Inject;
 import org.apache.logging.log4j.plugins.di.DI;
 import org.apache.logging.log4j.plugins.di.Injector;
 import org.apache.logging.log4j.plugins.di.Keys;
@@ -34,7 +37,7 @@ import java.util.stream.Collectors;
 /**
  * Proxies other {@link StrLookup}s using a keys within ${} markers.
  */
-public class Interpolator extends AbstractConfigurationAwareLookup {
+public class Interpolator extends AbstractConfigurationAwareLookup implements LoggerContextAware {
 
     /** Constant for the prefix separator. */
     public static final char PREFIX_SEPARATOR = ':';
@@ -57,12 +60,16 @@ public class Interpolator extends AbstractConfigurationAwareLookup {
 
     private final StrLookup defaultLookup;
 
+    private LoggerContext loggerContext = null;
+
+    // Used by tests
     public Interpolator(final StrLookup defaultLookup) {
         this(defaultLookup, List.of());
     }
 
     /**
      * Constructs an Interpolator using a given StrLookup and a list of packages to find Lookup plugins in.
+     * Only used in the Interpolator.
      *
      * @param defaultLookup  the default StrLookup to use as a fallback
      * @param pluginPackages a list of packages to scan for Lookup plugins
@@ -82,6 +89,12 @@ public class Interpolator extends AbstractConfigurationAwareLookup {
                 });
     }
 
+    /**
+     * Used by interpolatrorFactory.
+     *
+     * @param defaultLookup The default Lookup.
+     * @param strLookupPlugins The Lookup Plugins.
+     */
     public Interpolator(final StrLookup defaultLookup, final Map<String, Supplier<StrLookup>> strLookupPlugins) {
         this.defaultLookup = defaultLookup;
         strLookups.putAll(strLookupPlugins);
@@ -172,6 +185,9 @@ public class Interpolator extends AbstractConfigurationAwareLookup {
                 if (lookup instanceof ConfigurationAware) {
                     ((ConfigurationAware) lookup).setConfiguration(configuration);
                 }
+                if (lookup instanceof LoggerContextAware) {
+                    ((LoggerContextAware) lookup).setLoggerContext(loggerContext);
+                }
                 value = event == null ? lookup.lookup(name) : lookup.lookup(event, name);
             }
 
@@ -186,6 +202,13 @@ public class Interpolator extends AbstractConfigurationAwareLookup {
         return null;
     }
 
+    public void setLoggerContext(final LoggerContext loggerContext) {
+        if (loggerContext == null) {
+            return;
+        }
+        this.loggerContext = loggerContext;
+    }
+
     @Override
     public String toString() {
         final StringBuilder sb = new StringBuilder();
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/selector/ClassLoaderContextSelector.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/selector/ClassLoaderContextSelector.java
index 5cc1d36544..372a6103ee 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/selector/ClassLoaderContextSelector.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/selector/ClassLoaderContextSelector.java
@@ -282,6 +282,7 @@ public class ClassLoaderContextSelector implements ContextSelector, LoggerContex
     }
 
     protected LoggerContext createContext(final String name, final URI configLocation, final Injector injector) {
+
         return new LoggerContext(name, null, configLocation, injector);
     }
 
diff --git a/log4j-spring-boot/src/main/java/org/apache/logging/log4j/spring/boot/SpringLookup.java b/log4j-spring-boot/src/main/java/org/apache/logging/log4j/spring/boot/SpringLookup.java
index 6243d111d3..69e9f41cfb 100644
--- a/log4j-spring-boot/src/main/java/org/apache/logging/log4j/spring/boot/SpringLookup.java
+++ b/log4j-spring-boot/src/main/java/org/apache/logging/log4j/spring/boot/SpringLookup.java
@@ -18,8 +18,11 @@ package org.apache.logging.log4j.spring.boot;
 
 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.lookup.AbstractLoggerContextAwareLookup;
 import org.apache.logging.log4j.core.lookup.StrLookup;
 import org.apache.logging.log4j.core.lookup.Lookup;
+import org.apache.logging.log4j.plugins.Inject;
 import org.apache.logging.log4j.plugins.Plugin;
 import org.apache.logging.log4j.status.StatusLogger;
 import org.springframework.core.env.Environment;
@@ -32,7 +35,7 @@ import java.util.regex.Pattern;
  */
 @Lookup
 @Plugin("spring")
-public class SpringLookup extends SpringEnvironmentHolder implements StrLookup {
+public class SpringLookup extends AbstractLoggerContextAwareLookup {
 
     private static final Logger LOGGER = StatusLogger.getLogger();
     private static final String ACTIVE = "profiles.active";
@@ -41,30 +44,27 @@ public class SpringLookup extends SpringEnvironmentHolder implements StrLookup {
     private static final Pattern ACTIVE_PATTERN = Pattern.compile(ACTIVE + PATTERN);
     private static final Pattern DEFAULT_PATTERN = Pattern.compile(DEFAULT + PATTERN);
 
-    public SpringLookup() {
-        getEnvironment();
-    }
+    private Environment environment = null;
 
     @Override
     public String lookup(String key) {
-        Environment env = getEnvironment();
-        if (env != null) {
+        if (environment != null) {
             String lowerKey = key.toLowerCase();
             if (lowerKey.startsWith(ACTIVE)) {
-                switch (env.getActiveProfiles().length) {
+                switch (environment.getActiveProfiles().length) {
                     case 0: {
                         return null;
                     }
                     case 1: {
-                        return env.getActiveProfiles()[0];
+                        return environment.getActiveProfiles()[0];
                     }
                     default: {
                         Matcher matcher = ACTIVE_PATTERN.matcher(key);
                         if (matcher.matches()) {
                             try {
                                 int index = Integer.parseInt(matcher.group(1));
-                                if (index < env.getActiveProfiles().length) {
-                                    return env.getActiveProfiles()[index];
+                                if (index < environment.getActiveProfiles().length) {
+                                    return environment.getActiveProfiles()[index];
                                 } else {
                                     LOGGER.warn("Index out of bounds for Spring active profiles: {}", index);
                                     return null;
@@ -76,7 +76,7 @@ public class SpringLookup extends SpringEnvironmentHolder implements StrLookup {
 
                         } else {
                             StringBuilder sb = new StringBuilder();
-                            for (String profile : env.getActiveProfiles()) {
+                            for (String profile : environment.getActiveProfiles()) {
                                 if (sb.length() > 0) {
                                     sb.append(",");
                                 }
@@ -87,20 +87,20 @@ public class SpringLookup extends SpringEnvironmentHolder implements StrLookup {
                     }
                 }
             } else if (lowerKey.startsWith(DEFAULT)) {
-                switch (env.getDefaultProfiles().length) {
+                switch (environment.getDefaultProfiles().length) {
                     case 0: {
                         return null;
                     }
                     case 1: {
-                        return env.getDefaultProfiles()[0];
+                        return environment.getDefaultProfiles()[0];
                     }
                     default: {
                         Matcher matcher = DEFAULT_PATTERN.matcher(key);
                         if (matcher.matches()) {
                             try {
                                 int index = Integer.parseInt(matcher.group(1));
-                                if (index < env.getDefaultProfiles().length) {
-                                    return env.getDefaultProfiles()[index];
+                                if (index < environment.getDefaultProfiles().length) {
+                                    return environment.getDefaultProfiles()[index];
                                 } else {
                                     LOGGER.warn("Index out of bounds for Spring default profiles: {}", index);
                                     return null;
@@ -112,7 +112,7 @@ public class SpringLookup extends SpringEnvironmentHolder implements StrLookup {
 
                         } else {
                             StringBuilder sb = new StringBuilder();
-                            for (String profile : env.getDefaultProfiles()) {
+                            for (String profile : environment.getDefaultProfiles()) {
                                 if (sb.length() > 0) {
                                     sb.append(",");
                                 }
@@ -124,7 +124,7 @@ public class SpringLookup extends SpringEnvironmentHolder implements StrLookup {
                 }
             }
 
-            return env.getProperty(key);
+            return environment.getProperty(key);
 
         }
         return null;
@@ -134,4 +134,14 @@ public class SpringLookup extends SpringEnvironmentHolder implements StrLookup {
     public String lookup(LogEvent event, String key) {
         return lookup((key));
     }
+
+    @Override
+    public void setLoggerContext(final LoggerContext loggerContext) {
+        if (loggerContext != null) {
+            super.setLoggerContext(loggerContext);
+            environment = (Environment) loggerContext.getObject(Log4j2SpringBootLoggingSystem.ENVIRONMENT_KEY);
+        } else {
+            LOGGER.warn("Attempt to set LoggerContext reference to null in SpringLookup");
+        }
+    }
 }
diff --git a/log4j-spring-boot/src/main/java/org/apache/logging/log4j/spring/boot/SpringProfileArbiter.java b/log4j-spring-boot/src/main/java/org/apache/logging/log4j/spring/boot/SpringProfileArbiter.java
index 8c7a513790..32afc36714 100644
--- a/log4j-spring-boot/src/main/java/org/apache/logging/log4j/spring/boot/SpringProfileArbiter.java
+++ b/log4j-spring-boot/src/main/java/org/apache/logging/log4j/spring/boot/SpringProfileArbiter.java
@@ -16,13 +16,18 @@
  */
 package org.apache.logging.log4j.spring.boot;
 
+import org.apache.logging.log4j.Logger;
+import org.apache.logging.log4j.core.LoggerContext;
 import org.apache.logging.log4j.core.config.Configuration;
 import org.apache.logging.log4j.core.config.arbiters.Arbiter;
 import org.apache.logging.log4j.core.config.plugins.PluginBuilderAttribute;
 import org.apache.logging.log4j.core.config.plugins.PluginBuilderFactory;
 import org.apache.logging.log4j.core.config.plugins.PluginConfiguration;
+import org.apache.logging.log4j.core.config.plugins.PluginLoggerContext;
 import org.apache.logging.log4j.plugins.Configurable;
+import org.apache.logging.log4j.plugins.Inject;
 import org.apache.logging.log4j.plugins.Plugin;
+import org.apache.logging.log4j.status.StatusLogger;
 import org.springframework.core.env.Environment;
 import org.springframework.core.env.Profiles;
 import org.springframework.util.StringUtils;
@@ -32,18 +37,18 @@ import org.springframework.util.StringUtils;
  */
 @Configurable(elementType = Arbiter.ELEMENT_TYPE, deferChildren = true, printObject = true)
 @Plugin("SpringProfile")
-public class SpringProfileArbiter extends SpringEnvironmentHolder implements Arbiter {
+public class SpringProfileArbiter implements Arbiter {
 
     private final String[] profileNames;
+    private Environment environment;
 
-    private SpringProfileArbiter(final String[] profiles) {
+    private SpringProfileArbiter(final String[] profiles, Environment environment) {
         this.profileNames = profiles;
-
+        this.environment = environment;
     }
 
     @Override
     public boolean isCondition() {
-        Environment environment = getEnvironment();
         if (environment == null) {
             return false;
         }
@@ -61,13 +66,18 @@ public class SpringProfileArbiter extends SpringEnvironmentHolder implements Arb
 
     public static class Builder implements org.apache.logging.log4j.core.util.Builder<SpringProfileArbiter> {
 
+        private final Logger LOGGER = StatusLogger.getLogger();
         public static final String ATTR_NAME = "name";
 
         @PluginBuilderAttribute(ATTR_NAME)
         private String name;
 
+        @PluginLoggerContext
+        private LoggerContext loggerContext;
+
         @PluginConfiguration
-        private Configuration configuration;;
+        private Configuration configuration;
+
 
         /**
          * Sets the Profile Name or Names.
@@ -84,6 +94,11 @@ public class SpringProfileArbiter extends SpringEnvironmentHolder implements Arb
             return asBuilder();
         }
 
+        public Builder setLoggerContext(final LoggerContext loggerContext) {
+            this.loggerContext = loggerContext;
+            return asBuilder();
+        }
+
         private Builder asBuilder() {
             return this;
         }
@@ -91,7 +106,19 @@ public class SpringProfileArbiter extends SpringEnvironmentHolder implements Arb
         public SpringProfileArbiter build() {
             String[] profileNames = StringUtils.trimArrayElements(
                     StringUtils.commaDelimitedListToStringArray(configuration.getStrSubstitutor().replace(name)));
-            return new SpringProfileArbiter(profileNames);
+            Environment environment = null;
+            if (loggerContext != null) {
+                environment = (Environment) loggerContext.getObject(Log4j2SpringBootLoggingSystem.ENVIRONMENT_KEY);
+                if (environment == null) {
+                    LOGGER.warn("Cannot create Arbiter, no Spring Environment provided");
+                    return null;
+                }
+
+                return new SpringProfileArbiter(profileNames, environment);
+            } else {
+                LOGGER.warn("Cannot create Arbiter, LoggerContext is not available");
+            }
+            return null;
         }
     }
 }
diff --git a/log4j-spring-boot/src/test/java/org/apache/logging/log4j/spring/boot/Log4j2SpringBootInitTest.java b/log4j-spring-boot/src/test/java/org/apache/logging/log4j/spring/boot/Log4j2SpringBootInitTest.java
index d909c672db..93077df3dc 100644
--- a/log4j-spring-boot/src/test/java/org/apache/logging/log4j/spring/boot/Log4j2SpringBootInitTest.java
+++ b/log4j-spring-boot/src/test/java/org/apache/logging/log4j/spring/boot/Log4j2SpringBootInitTest.java
@@ -54,7 +54,9 @@ public class Log4j2SpringBootInitTest {
 
         @Override
         public void run(ApplicationArguments args) throws Exception {
+            LoggerContext loggerContext = ((org.apache.logging.log4j.core.Logger) LOGGER).getContext();
             SpringLookup lookup = new SpringLookup();
+            lookup.setLoggerContext(loggerContext);
             LOGGER.info("Started: {}", lookup.lookup("spring.application.name"));
         }
     }
diff --git a/log4j-spring-boot/src/test/java/org/apache/logging/log4j/spring/boot/SpringLookupTest.java b/log4j-spring-boot/src/test/java/org/apache/logging/log4j/spring/boot/SpringLookupTest.java
index a10e6d9f0e..fba952d793 100644
--- a/log4j-spring-boot/src/test/java/org/apache/logging/log4j/spring/boot/SpringLookupTest.java
+++ b/log4j-spring-boot/src/test/java/org/apache/logging/log4j/spring/boot/SpringLookupTest.java
@@ -16,9 +16,14 @@
  */
 package org.apache.logging.log4j.spring.boot;
 
+import java.util.HashMap;
+import java.util.Map;
+
 import org.apache.logging.log4j.LogManager;
 import org.apache.logging.log4j.core.LoggerContext;
 import org.apache.logging.log4j.core.lookup.Interpolator;
+import org.apache.logging.log4j.core.lookup.InterpolatorFactory;
+import org.apache.logging.log4j.core.lookup.PropertiesLookup;
 import org.apache.logging.log4j.core.lookup.StrLookup;
 import org.junit.Test;
 import org.springframework.mock.env.MockEnvironment;
@@ -41,6 +46,7 @@ public class SpringLookupTest {
         LoggerContext context = (LoggerContext) LogManager.getContext(false);
         context.putObject(Log4j2SpringBootLoggingSystem.ENVIRONMENT_KEY, env);
         SpringLookup lookup = new SpringLookup();
+        lookup.setLoggerContext(context);
         String result = lookup.lookup("profiles.active");
         assertNotNull("No active profiles", result);
         assertEquals("Incorrect active profile", "test", result);
@@ -67,8 +73,10 @@ public class SpringLookupTest {
         env.setProperty("app.property", "test");
         LoggerContext context = (LoggerContext) LogManager.getContext(false);
         context.putObject(Log4j2SpringBootLoggingSystem.ENVIRONMENT_KEY, env);
-
-        StrLookup lookup = new Interpolator();
+        InterpolatorFactory interpolatorFactory = context.getInjector().getInstance(InterpolatorFactory.class);
+        Map<String, String> properties = new HashMap<>();
+        Interpolator lookup = interpolatorFactory.newInterpolator(new PropertiesLookup(properties));
+        lookup.setLoggerContext(context);
         String result = lookup.lookup("spring:profiles.active");
         assertNotNull("No active profiles", result);
         assertEquals("Incorrect active profile", "test", result);
diff --git a/src/changes/changes.xml b/src/changes/changes.xml
index a2a0066824..b76a274924 100644
--- a/src/changes/changes.xml
+++ b/src/changes/changes.xml
@@ -202,6 +202,9 @@
       </action>
     </release>
     <release version="2.19.0" date="2022-MM-DD" description="GA Release 2.19.0">
+      <action issue="LOG4J2-3589" dev="rgoers" type="update">
+        Allow Plugins to be injected with the LoggerContext reference.
+      </action>
       <action issue="LOG4J2-3588" dev="rgoers" type="update">
         Allow PropertySources to be added.
       </action>