You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@logging.apache.org by ma...@apache.org on 2022/03/28 01:32:16 UTC

[logging-log4j2] 03/03: Add lazy loading support for StrLookup plugins

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

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

commit e3a462a9be8942b4dfac21a725fb8c509794dc48
Author: Matt Sicker <ma...@apache.org>
AuthorDate: Sun Mar 27 20:29:51 2022 -0500

    Add lazy loading support for StrLookup plugins
    
    This introduces an InterpolatorFactory binding for reusing bound PluginManager instances to create Interpolator instances from a default StrLookup. This indirection allows for lazy loading of StrLookup plugins while allowing for them to be created via Injector as well.
    
    Signed-off-by: Matt Sicker <ma...@apache.org>
---
 .../log4j/core/config/AbstractConfiguration.java   | 12 ++--
 .../log4j/core/config/ConfigurationFactory.java    |  6 +-
 .../log4j/core/config/PropertiesPlugin.java        |  5 +-
 .../logging/log4j/core/impl/DefaultCallback.java   | 14 +++++
 .../logging/log4j/core/lookup/Interpolator.java    | 66 +++++++++-------------
 .../log4j/core/lookup/InterpolatorFactory.java     | 22 ++++++++
 6 files changed, 76 insertions(+), 49 deletions(-)

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 5acf726..266e7af 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
@@ -35,6 +35,7 @@ import org.apache.logging.log4j.core.filter.AbstractFilterable;
 import org.apache.logging.log4j.core.layout.PatternLayout;
 import org.apache.logging.log4j.core.lookup.ConfigurationStrSubstitutor;
 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.RuntimeStrSubstitutor;
 import org.apache.logging.log4j.core.lookup.StrLookup;
@@ -80,6 +81,7 @@ import java.util.concurrent.ConcurrentMap;
 import java.util.concurrent.CopyOnWriteArrayList;
 import java.util.concurrent.TimeUnit;
 import java.util.function.Function;
+import java.util.function.Supplier;
 
 /**
  * The base Configuration. Many configuration implementations will extend this class.
@@ -133,6 +135,7 @@ public abstract class AbstractConfiguration extends AbstractFilterable implement
     private ConcurrentMap<String, LoggerConfig> loggerConfigs = new ConcurrentHashMap<>();
     private List<CustomLevelConfig> customLevels = List.of();
     private final ConcurrentMap<String, String> properties = new ConcurrentHashMap<>();
+    private final InterpolatorFactory interpolatorFactory;
     private final StrLookup tempLookup;
     private final StrSubstitutor runtimeStrSubstitutor;
     private final StrSubstitutor configurationStrSubstitutor;
@@ -160,7 +163,8 @@ public abstract class AbstractConfiguration extends AbstractFilterable implement
             injector.init();
         }
         componentMap.put(Configuration.CONTEXT_PROPERTIES, properties);
-        tempLookup = new Interpolator(new PropertiesLookup(properties), this);
+        interpolatorFactory = injector.getInstance(InterpolatorFactory.class);
+        tempLookup = interpolatorFactory.newInterpolator(new PropertiesLookup(properties));
         runtimeStrSubstitutor = new RuntimeStrSubstitutor(tempLookup);
         configurationStrSubstitutor = new ConfigurationStrSubstitutor(runtimeStrSubstitutor);
         pluginManager = injector.getInstance(Core.PLUGIN_MANAGER_KEY);
@@ -519,8 +523,8 @@ public abstract class AbstractConfiguration extends AbstractFilterable implement
     }
 
     @Override
-    public <T> T getComponent(final Key<T> key) {
-        return injector.getInstance(key);
+    public <T> Supplier<T> getFactory(final Key<T> key) {
+        return injector.getFactory(key);
     }
 
     @Override
@@ -652,7 +656,7 @@ public abstract class AbstractConfiguration extends AbstractFilterable implement
         } else {
             final Map<String, String> map = this.getComponent(CONTEXT_PROPERTIES);
             final StrLookup lookup = map == null ? null : new PropertiesLookup(map);
-            Interpolator interpolator = new Interpolator(lookup, this);
+            Interpolator interpolator = interpolatorFactory.newInterpolator(lookup);
             runtimeStrSubstitutor.setVariableResolver(interpolator);
             configurationStrSubstitutor.setVariableResolver(interpolator);
         }
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/ConfigurationFactory.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/ConfigurationFactory.java
index 37fd8f1..fc204fa 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/ConfigurationFactory.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/ConfigurationFactory.java
@@ -20,13 +20,12 @@ import org.apache.logging.log4j.Level;
 import org.apache.logging.log4j.Logger;
 import org.apache.logging.log4j.core.LoggerContext;
 import org.apache.logging.log4j.core.config.builder.api.ConfigurationBuilderFactory;
-import org.apache.logging.log4j.core.lookup.ConfigurationStrSubstitutor;
-import org.apache.logging.log4j.core.lookup.Interpolator;
 import org.apache.logging.log4j.core.lookup.StrSubstitutor;
 import org.apache.logging.log4j.core.net.UrlConnectionFactory;
 import org.apache.logging.log4j.core.util.AuthorizationProvider;
 import org.apache.logging.log4j.core.util.BasicAuthorizationProvider;
 import org.apache.logging.log4j.core.util.FileUtils;
+import org.apache.logging.log4j.plugins.Inject;
 import org.apache.logging.log4j.plugins.di.Injector;
 import org.apache.logging.log4j.plugins.di.Key;
 import org.apache.logging.log4j.status.StatusLogger;
@@ -147,7 +146,8 @@ public abstract class ConfigurationFactory extends ConfigurationBuilderFactory {
         return provider;
     }
 
-    protected final StrSubstitutor substitutor = new ConfigurationStrSubstitutor(new Interpolator());
+    @Inject
+    protected StrSubstitutor substitutor;
 
     protected abstract String[] getSupportedTypes();
 
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 94fede4..52c7904 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
@@ -17,13 +17,14 @@
 package org.apache.logging.log4j.core.config;
 
 import org.apache.logging.log4j.core.config.plugins.PluginConfiguration;
-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.apache.logging.log4j.plugins.Node;
 import org.apache.logging.log4j.plugins.Plugin;
 import org.apache.logging.log4j.plugins.PluginElement;
 import org.apache.logging.log4j.plugins.PluginFactory;
+import org.apache.logging.log4j.plugins.di.Key;
 
 import java.util.HashMap;
 import java.util.Map;
@@ -56,6 +57,6 @@ public final class PropertiesPlugin {
                 map.put(prop.getName(), prop.getValue());
             }
         }
-        return new Interpolator(new PropertiesLookup(map), config);
+        return config.getComponent(Key.forClass(InterpolatorFactory.class)).newInterpolator(new PropertiesLookup(map));
     }
 }
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/DefaultCallback.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/DefaultCallback.java
index 4264355..590222f 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/DefaultCallback.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/DefaultCallback.java
@@ -23,6 +23,10 @@ import org.apache.logging.log4j.ThreadContext;
 import org.apache.logging.log4j.core.ContextDataInjector;
 import org.apache.logging.log4j.core.config.ConfigurationFactory;
 import org.apache.logging.log4j.core.config.DefaultConfigurationFactory;
+import org.apache.logging.log4j.core.lookup.Interpolator;
+import org.apache.logging.log4j.core.lookup.InterpolatorFactory;
+import org.apache.logging.log4j.core.lookup.StrLookup;
+import org.apache.logging.log4j.core.lookup.StrSubstitutor;
 import org.apache.logging.log4j.core.selector.ClassLoaderContextSelector;
 import org.apache.logging.log4j.core.selector.ContextSelector;
 import org.apache.logging.log4j.core.time.Clock;
@@ -41,6 +45,8 @@ import org.apache.logging.log4j.core.util.ShutdownCallbackRegistry;
 import org.apache.logging.log4j.plugins.PluginException;
 import org.apache.logging.log4j.plugins.di.Injector;
 import org.apache.logging.log4j.plugins.di.InjectorCallback;
+import org.apache.logging.log4j.plugins.di.Key;
+import org.apache.logging.log4j.plugins.util.PluginManager;
 import org.apache.logging.log4j.spi.CopyOnWrite;
 import org.apache.logging.log4j.spi.DefaultThreadContextMap;
 import org.apache.logging.log4j.spi.ReadOnlyThreadContextMap;
@@ -119,6 +125,14 @@ public class DefaultCallback implements InjectorCallback {
                         () -> loader.getInstance(Constants.LOG4J_LOG_EVENT_FACTORY, LogEventFactory.class,
                                 () -> Constants.ENABLE_THREADLOCALS ? ReusableLogEventFactory.class :
                                         DefaultLogEventFactory.class))
+                .registerBindingIfAbsent(Key.forClass(InterpolatorFactory.class),
+                        () -> defaultLookup -> {
+                            final PluginManager pluginManager = injector.getInstance(StrLookup.PLUGIN_MANAGER_KEY);
+                            pluginManager.collectPlugins();
+                            return new Interpolator(defaultLookup, pluginManager.getPlugins(), injector::getInstance);
+                        })
+                .registerBindingIfAbsent(Key.forClass(StrSubstitutor.class),
+                        () -> new StrSubstitutor(injector.getInstance(InterpolatorFactory.class).newInterpolator(null)))
                 .registerBindingIfAbsent(ConfigurationFactory.KEY, injector.getFactory(DefaultConfigurationFactory.class))
                 .registerBindingIfAbsent(Constants.DEFAULT_STATUS_LEVEL_KEY, () -> {
                     final String statusLevel =
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 ca84caf..e238826 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,20 +18,20 @@ 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.config.Configuration;
 import org.apache.logging.log4j.core.config.ConfigurationAware;
-import org.apache.logging.log4j.core.util.Constants;
-import org.apache.logging.log4j.plugins.di.Key;
-import org.apache.logging.log4j.plugins.util.PluginManager;
 import org.apache.logging.log4j.plugins.util.PluginType;
 import org.apache.logging.log4j.plugins.util.PluginUtil;
 import org.apache.logging.log4j.status.StatusLogger;
+import org.apache.logging.log4j.util.LazyValue;
 import org.apache.logging.log4j.util.ReflectionUtil;
 
-import java.util.HashMap;
 import java.util.List;
 import java.util.Locale;
 import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.function.Function;
+import java.util.function.Supplier;
+import java.util.stream.Collectors;
 
 /**
  * Proxies other {@link StrLookup}s using a keys within ${} markers.
@@ -55,9 +55,7 @@ public class Interpolator extends AbstractConfigurationAwareLookup {
 
     private static final Logger LOGGER = StatusLogger.getLogger();
 
-    private static final String JNDI_LOOKUP = "org.apache.logging.log4j.jndi.lookup.JndiLookup";
-
-    private final Map<String, StrLookup> strLookupMap = new HashMap<>();
+    private final Map<String, Supplier<StrLookup>> strLookups = new ConcurrentHashMap<>();
 
     private final StrLookup defaultLookup;
 
@@ -73,37 +71,22 @@ public class Interpolator extends AbstractConfigurationAwareLookup {
      * @since 2.1
      */
     public Interpolator(final StrLookup defaultLookup, final List<String> pluginPackages) {
-        this.defaultLookup = defaultLookup == null ? new PropertiesLookup(Map.of()) : defaultLookup;
-        final Map<String, PluginType<?>> plugins =
-                PluginUtil.collectPluginsByCategoryAndPackage(CATEGORY, pluginPackages);
-
-        for (final Map.Entry<String, PluginType<?>> entry : plugins.entrySet()) {
-            try {
-                final Class<? extends StrLookup> clazz = entry.getValue().getPluginClass().asSubclass(StrLookup.class);
-                if (!clazz.getName().equals(JNDI_LOOKUP) || Constants.JNDI_LOOKUP_ENABLED) {
-                    strLookupMap.put(entry.getKey().toLowerCase(), ReflectionUtil.instantiate(clazz));
-                }
-            } catch (final Throwable t) {
-                handleError(entry.getKey(), t);
-            }
-        }
+        this(defaultLookup, PluginUtil.collectPluginsByCategoryAndPackage(CATEGORY, pluginPackages), ReflectionUtil::instantiate);
     }
 
-    public Interpolator(final StrLookup defaultLookup, final Configuration configuration) {
+    public Interpolator(
+            final StrLookup defaultLookup, final Map<String, PluginType<?>> strLookupPlugins,
+            final Function<Class<? extends StrLookup>, StrLookup> pluginLoader) {
         this.defaultLookup = defaultLookup == null ? new PropertiesLookup(Map.of()) : defaultLookup;
-        final PluginManager pluginManager = configuration.getComponent(StrLookup.PLUGIN_MANAGER_KEY);
-        pluginManager.collectPlugins(configuration.getPluginPackages());
-        for (final Map.Entry<String, PluginType<?>> entry : pluginManager.getPlugins().entrySet()) {
+        strLookupPlugins.forEach((key, value) -> {
             try {
-                final Class<? extends StrLookup> strLookupClass = entry.getValue().getPluginClass().asSubclass(StrLookup.class);
-                // TODO: this could use @RequiredProperty on JndiLookup instead
-                if (!strLookupClass.getName().equals(JNDI_LOOKUP) || Constants.JNDI_LOOKUP_ENABLED) {
-                    strLookupMap.put(entry.getKey().toLowerCase(Locale.ROOT), configuration.getComponent(Key.forClass(strLookupClass)));
-                }
+                final Class<? extends StrLookup> strLookupClass = value.getPluginClass().asSubclass(StrLookup.class);
+                final Supplier<StrLookup> strLookupSupplier = LazyValue.from(() -> pluginLoader.apply(strLookupClass));
+                strLookups.put(key.toLowerCase(Locale.ROOT), strLookupSupplier);
             } catch (final Throwable t) {
-                handleError(entry.getKey(), t);
+                handleError(key, t);
             }
-        }
+        });
     }
 
     /**
@@ -125,7 +108,9 @@ public class Interpolator extends AbstractConfigurationAwareLookup {
     }
 
     public Map<String, StrLookup> getStrLookupMap() {
-        return strLookupMap;
+        return strLookups.entrySet()
+                .stream()
+                .collect(Collectors.toMap(Map.Entry::getKey, e -> e.getValue().get()));
     }
 
     private void handleError(final String lookupKey, final Throwable t) {
@@ -182,12 +167,13 @@ public class Interpolator extends AbstractConfigurationAwareLookup {
         if (prefixPos >= 0) {
             final String prefix = var.substring(0, prefixPos).toLowerCase(Locale.US);
             final String name = var.substring(prefixPos + 1);
-            final StrLookup lookup = strLookupMap.get(prefix);
-            if (lookup instanceof ConfigurationAware) {
-                ((ConfigurationAware) lookup).setConfiguration(configuration);
-            }
+            final Supplier<StrLookup> lookupSupplier = strLookups.get(prefix);
             String value = null;
-            if (lookup != null) {
+            if (lookupSupplier != null) {
+                final StrLookup lookup = lookupSupplier.get();
+                if (lookup instanceof ConfigurationAware) {
+                    ((ConfigurationAware) lookup).setConfiguration(configuration);
+                }
                 value = event == null ? lookup.lookup(name) : lookup.lookup(event, name);
             }
 
@@ -205,7 +191,7 @@ public class Interpolator extends AbstractConfigurationAwareLookup {
     @Override
     public String toString() {
         final StringBuilder sb = new StringBuilder();
-        for (final String name : strLookupMap.keySet()) {
+        for (final String name : strLookups.keySet()) {
             if (sb.length() == 0) {
                 sb.append('{');
             } else {
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/lookup/InterpolatorFactory.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/lookup/InterpolatorFactory.java
new file mode 100644
index 0000000..3700fe1
--- /dev/null
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/lookup/InterpolatorFactory.java
@@ -0,0 +1,22 @@
+/*
+ * 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;
+
+public interface InterpolatorFactory {
+    Interpolator newInterpolator(final StrLookup defaultLookup);
+}