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 <[email protected]> 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 <[email protected]> --- .../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); +}
