diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/PropertyConfigurator.java b/log4j-1.2-api/src/main/java/org/apache/log4j/PropertyConfigurator.java index 5d1e63ccdb8..ca88c35d038 100644 --- a/log4j-1.2-api/src/main/java/org/apache/log4j/PropertyConfigurator.java +++ b/log4j-1.2-api/src/main/java/org/apache/log4j/PropertyConfigurator.java @@ -47,6 +47,7 @@ import org.apache.log4j.spi.RendererSupport; import org.apache.log4j.spi.ThrowableRenderer; import org.apache.log4j.spi.ThrowableRendererSupport; +import org.apache.logging.log4j.core.LoggerContext; import org.apache.logging.log4j.core.config.Configuration; import org.apache.logging.log4j.core.net.UrlConnectionFactory; import org.apache.logging.log4j.util.StackLocatorUtil; @@ -307,7 +308,7 @@ public void doConfigure(final Properties properties, final LoggerRepository logg Configuration doConfigure( final Properties properties, final LoggerRepository loggerRepository, final ClassLoader classLoader) { final PropertiesConfiguration configuration = - new PropertiesConfiguration(LogManager.getContext(classLoader), properties); + new PropertiesConfiguration((LoggerContext) LogManager.getContext(classLoader), properties); configuration.doConfigure(); repository = loggerRepository; diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/config/Log4j1Configuration.java b/log4j-1.2-api/src/main/java/org/apache/log4j/config/Log4j1Configuration.java index 26edfbc33d8..2eec639e843 100644 --- a/log4j-1.2-api/src/main/java/org/apache/log4j/config/Log4j1Configuration.java +++ b/log4j-1.2-api/src/main/java/org/apache/log4j/config/Log4j1Configuration.java @@ -16,6 +16,7 @@ */ package org.apache.log4j.config; +import java.util.Optional; import org.apache.log4j.Level; import org.apache.log4j.builders.BuilderManager; import org.apache.logging.log4j.core.LoggerContext; @@ -23,6 +24,9 @@ import org.apache.logging.log4j.core.config.Configuration; import org.apache.logging.log4j.core.config.ConfigurationSource; import org.apache.logging.log4j.core.config.Reconfigurable; +import org.apache.logging.log4j.plugins.di.ConfigurableInstanceFactory; +import org.apache.logging.log4j.plugins.di.DI; +import org.apache.logging.log4j.util.PropertiesUtil; /** * Base Configuration for Log4j 1. @@ -49,7 +53,15 @@ public Log4j1Configuration( final LoggerContext loggerContext, final ConfigurationSource configurationSource, final int monitorIntervalSeconds) { - super(loggerContext, configurationSource); + super( + loggerContext, + configurationSource, + Optional.ofNullable(loggerContext) + .map(LoggerContext::getEnvironment) + .orElseGet(PropertiesUtil::getProperties), + Optional.ofNullable(loggerContext) + .map(ctx -> (ConfigurableInstanceFactory) ctx.getInstanceFactory()) + .orElseGet(DI::createInitializedFactory)); initializeWatchers(this, configurationSource, monitorIntervalSeconds); manager = instanceFactory.getInstance(BuilderManager.class); } diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/config/PropertiesConfiguration.java b/log4j-1.2-api/src/main/java/org/apache/log4j/config/PropertiesConfiguration.java index 5ee7bc06fa1..3bc29f95870 100644 --- a/log4j-1.2-api/src/main/java/org/apache/log4j/config/PropertiesConfiguration.java +++ b/log4j-1.2-api/src/main/java/org/apache/log4j/config/PropertiesConfiguration.java @@ -81,12 +81,14 @@ public class PropertiesConfiguration extends Log4j1Configuration { * Constructs a new instance. * * @param loggerContext The LoggerContext. - * @param source The ConfigurationSource. + * @param configurationSource The ConfigurationSource. * @param monitorIntervalSeconds The monitoring interval in seconds. */ public PropertiesConfiguration( - final LoggerContext loggerContext, final ConfigurationSource source, final int monitorIntervalSeconds) { - super(loggerContext, source, monitorIntervalSeconds); + final LoggerContext loggerContext, + final ConfigurationSource configurationSource, + final int monitorIntervalSeconds) { + this(loggerContext, configurationSource, monitorIntervalSeconds, null); } /** @@ -96,18 +98,16 @@ public PropertiesConfiguration( * @param properties The ConfigurationSource, may be null. */ public PropertiesConfiguration(final LoggerContext loggerContext, final Properties properties) { - super(loggerContext, ConfigurationSource.NULL_SOURCE, 0); - this.properties = properties; + this(loggerContext, ConfigurationSource.NULL_SOURCE, 0, properties); } - /** - * Constructs a new instance. - * - * @param loggerContext The LoggerContext. - * @param properties The ConfigurationSource. - */ - public PropertiesConfiguration(org.apache.logging.log4j.spi.LoggerContext loggerContext, Properties properties) { - this((LoggerContext) loggerContext, properties); + private PropertiesConfiguration( + final LoggerContext loggerContext, + final ConfigurationSource configurationSource, + final int monitorIntervalSeconds, + final Properties properties) { + super(loggerContext, configurationSource, monitorIntervalSeconds); + this.properties = properties; } @Override @@ -149,7 +149,7 @@ public Configuration reconfigure() { /** * Reads a configuration from a file. The existing configuration is not cleared nor reset. If you require a - * different behavior, then call {@link LogManager#resetConfiguration resetConfiguration} method before calling + * different behavior, then call {@link LogManager#resetConfiguration()} resetConfiguration} method before calling * doConfigure. *

* The configuration file consists of statements in the format key=value. The syntax of different @@ -373,7 +373,7 @@ private void parseLoggers(final Properties props) { LoggerConfig loggerConfig = getLogger(loggerName); if (loggerConfig == null) { final boolean additivity = getAdditivityForLogger(props, loggerName); - loggerConfig = new LoggerConfig(loggerName, org.apache.logging.log4j.Level.ERROR, additivity); + loggerConfig = new LoggerConfig(loggerName, Level.ERROR, additivity, this); addLogger(loggerName, loggerConfig); } parseLogger(props, loggerConfig, key, loggerName, value); diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/config/PropertiesConfigurationFactory.java b/log4j-1.2-api/src/main/java/org/apache/log4j/config/PropertiesConfigurationFactory.java index 8f3949b173a..7ca6314c66d 100644 --- a/log4j-1.2-api/src/main/java/org/apache/log4j/config/PropertiesConfigurationFactory.java +++ b/log4j-1.2-api/src/main/java/org/apache/log4j/config/PropertiesConfigurationFactory.java @@ -61,17 +61,17 @@ public Configuration getConfiguration(final LoggerContext loggerContext, final C } @Override - protected String getTestPrefix() { + public String getTestPrefix() { return TEST_PREFIX; } @Override - protected String getDefaultPrefix() { + public String getDefaultPrefix() { return DEFAULT_PREFIX; } @Override - protected String getVersion() { + public String getVersion() { return LOG4J1_VERSION; } } diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/xml/XmlConfiguration.java b/log4j-1.2-api/src/main/java/org/apache/log4j/xml/XmlConfiguration.java index daa0903ba03..efe481e4d6f 100644 --- a/log4j-1.2-api/src/main/java/org/apache/log4j/xml/XmlConfiguration.java +++ b/log4j-1.2-api/src/main/java/org/apache/log4j/xml/XmlConfiguration.java @@ -112,8 +112,10 @@ public class XmlConfiguration extends Log4j1Configuration { private final Properties props = null; public XmlConfiguration( - final LoggerContext loggerContext, final ConfigurationSource source, final int monitorIntervalSeconds) { - super(loggerContext, source, monitorIntervalSeconds); + final LoggerContext loggerContext, + final ConfigurationSource configurationSource, + final int monitorIntervalSeconds) { + super(loggerContext, configurationSource, monitorIntervalSeconds); appenderMap = new HashMap<>(); } @@ -563,7 +565,7 @@ private void parseCategory(final Element loggerElement) { final boolean additivity = OptionConverter.toBoolean(subst(loggerElement.getAttribute(ADDITIVITY_ATTR)), true); LoggerConfig loggerConfig = getLogger(catName); if (loggerConfig == null) { - loggerConfig = new LoggerConfig(catName, org.apache.logging.log4j.Level.ERROR, additivity); + loggerConfig = new LoggerConfig(catName, org.apache.logging.log4j.Level.ERROR, additivity, this); addLogger(catName, loggerConfig); } else { loggerConfig.setAdditive(additivity); diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/xml/XmlConfigurationFactory.java b/log4j-1.2-api/src/main/java/org/apache/log4j/xml/XmlConfigurationFactory.java index d7822928d7c..78d1cae4639 100644 --- a/log4j-1.2-api/src/main/java/org/apache/log4j/xml/XmlConfigurationFactory.java +++ b/log4j-1.2-api/src/main/java/org/apache/log4j/xml/XmlConfigurationFactory.java @@ -62,17 +62,17 @@ public Configuration getConfiguration(final LoggerContext loggerContext, final C } @Override - protected String getTestPrefix() { + public String getTestPrefix() { return TEST_PREFIX; } @Override - protected String getDefaultPrefix() { + public String getDefaultPrefix() { return DEFAULT_PREFIX; } @Override - protected String getVersion() { + public String getVersion() { return LOG4J1_VERSION; } } diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/BasicConfigurationFactory.java b/log4j-1.2-api/src/test/java/org/apache/log4j/BasicConfigurationFactory.java index 16b14d84ee2..fbf5ba1d5fb 100644 --- a/log4j-1.2-api/src/test/java/org/apache/log4j/BasicConfigurationFactory.java +++ b/log4j-1.2-api/src/test/java/org/apache/log4j/BasicConfigurationFactory.java @@ -31,7 +31,7 @@ public class BasicConfigurationFactory extends ConfigurationFactory { @Override - public String[] getSupportedTypes() { + protected String[] getSupportedTypes() { return new String[] {"*"}; } diff --git a/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/StatusLoggerExtension.java b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/StatusLoggerExtension.java index 8795d5bb174..7737023bda0 100644 --- a/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/StatusLoggerExtension.java +++ b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/StatusLoggerExtension.java @@ -147,7 +147,10 @@ public void handleException(final ExtensionContext context, final Throwable thro logger.atLevel(data.getLevel()) .withThrowable(data.getThrowable()) .withLocation(data.getStackTraceElement()) - .log("{} {}", formatter.format(Instant.ofEpochMilli(data.getTimestamp())), data.getMessage()); + .log( + "{} {}", + formatter.format(Instant.ofEpochMilli(data.getTimestamp())), + data.getMessage().getFormattedMessage()); }); } } diff --git a/log4j-api/src/main/java/org/apache/logging/log4j/LogManager.java b/log4j-api/src/main/java/org/apache/logging/log4j/LogManager.java index aa16353407c..4f98d99c9f4 100644 --- a/log4j-api/src/main/java/org/apache/logging/log4j/LogManager.java +++ b/log4j-api/src/main/java/org/apache/logging/log4j/LogManager.java @@ -23,8 +23,11 @@ import org.apache.logging.log4j.spi.LoggerContext; import org.apache.logging.log4j.spi.LoggerContextFactory; import org.apache.logging.log4j.spi.LoggingSystem; +import org.apache.logging.log4j.spi.LoggingSystemProperty; import org.apache.logging.log4j.spi.Terminable; import org.apache.logging.log4j.status.StatusLogger; +import org.apache.logging.log4j.util.LoaderUtil; +import org.apache.logging.log4j.util.PropertiesUtil; import org.apache.logging.log4j.util.StackLocatorUtil; import org.apache.logging.log4j.util.Strings; @@ -55,6 +58,28 @@ public class LogManager { // for convenience private static final String FQCN = LogManager.class.getName(); + private static volatile LoggerContextFactory factory; + + /* + * Scans the classpath to find all logging implementation. Currently, only one will be used but this could be + * extended to allow multiple implementations to be used. + */ + static { + // Shortcut binding to force a specific logging implementation. + final PropertiesUtil managerProps = PropertiesUtil.getProperties(); + final String factoryClassName = + managerProps.getStringProperty(LoggingSystemProperty.LOGGER_CONTEXT_FACTORY_CLASS); + if (factoryClassName != null) { + try { + factory = LoaderUtil.newCheckedInstanceOf(factoryClassName, LoggerContextFactory.class); + } catch (final ClassNotFoundException cnfe) { + LOGGER.error("Unable to locate configured LoggerContextFactory {}", factoryClassName); + } catch (final Exception ex) { + LOGGER.error("Unable to create configured LoggerContextFactory {}", factoryClassName, ex); + } + } + } + /** * Prevents instantiation */ @@ -387,7 +412,7 @@ public static void shutdown(final LoggerContext context) { * @return The LoggerContextFactory. */ public static LoggerContextFactory getFactory() { - return LoggingSystem.getLoggerContextFactory(); + return factory != null ? factory : LoggingSystem.getProvider().getLoggerContextFactory(); } /** @@ -405,7 +430,7 @@ public static LoggerContextFactory getFactory() { * @see LoggingSystem */ public static void setFactory(final LoggerContextFactory factory) { - LoggingSystem.getInstance().setLoggerContextFactory(factory); + LogManager.factory = factory; } /** diff --git a/log4j-api/src/main/java/org/apache/logging/log4j/ThreadContext.java b/log4j-api/src/main/java/org/apache/logging/log4j/ThreadContext.java index 8a5a6a6b183..61a4750ded6 100644 --- a/log4j-api/src/main/java/org/apache/logging/log4j/ThreadContext.java +++ b/log4j-api/src/main/java/org/apache/logging/log4j/ThreadContext.java @@ -42,7 +42,7 @@ *

* @see Thread Context Manual */ -public final class ThreadContext { +public class ThreadContext { /** * An empty read-only ThreadContextStack. @@ -165,17 +165,15 @@ public ContextStack getImmutableStackOrNull() { init(); } - private ThreadContext() { - // empty - } + protected ThreadContext() {} /** * Consider private, used for testing. */ @InternalApi public static void init() { - contextMap = LoggingSystem.createContextMap(); - contextStack = LoggingSystem.createContextStack(); + contextMap = LoggingSystem.getProvider().getThreadContextMapFactory(); + contextStack = LoggingSystem.getProvider().getThreadContextStack(); if (contextMap instanceof ReadOnlyThreadContextMap) { readOnlyContextMap = (ReadOnlyThreadContextMap) contextMap; } else { @@ -496,6 +494,13 @@ public static void trim(final int depth) { contextStack.trim(depth); } + /** + * @return The underlying context map implementation. + */ + protected static ThreadContextMap getContextMap() { + return contextMap; + } + /** * The ThreadContext Stack interface. */ diff --git a/log4j-api/src/main/java/org/apache/logging/log4j/spi/LoggerContext.java b/log4j-api/src/main/java/org/apache/logging/log4j/spi/LoggerContext.java index 01cd966c084..34e57212ce6 100644 --- a/log4j-api/src/main/java/org/apache/logging/log4j/spi/LoggerContext.java +++ b/log4j-api/src/main/java/org/apache/logging/log4j/spi/LoggerContext.java @@ -55,7 +55,7 @@ default String getName() { * @return the PropertyEnvironment. * @since 3.0 */ - default PropertyEnvironment getProperties() { + default PropertyEnvironment getEnvironment() { return PropertiesUtil.getContextProperties(LoggerContext.class.getClassLoader(), getName()); } diff --git a/log4j-api/src/main/java/org/apache/logging/log4j/spi/LoggingSystem.java b/log4j-api/src/main/java/org/apache/logging/log4j/spi/LoggingSystem.java index 02ad1242e77..33e4af2ec4d 100644 --- a/log4j-api/src/main/java/org/apache/logging/log4j/spi/LoggingSystem.java +++ b/log4j-api/src/main/java/org/apache/logging/log4j/spi/LoggingSystem.java @@ -18,12 +18,6 @@ import static org.apache.logging.log4j.spi.LoggingSystemProperty.LOGGER_FLOW_MESSAGE_FACTORY_CLASS; import static org.apache.logging.log4j.spi.LoggingSystemProperty.LOGGER_MESSAGE_FACTORY_CLASS; -import static org.apache.logging.log4j.spi.LoggingSystemProperty.THREAD_CONTEXT_ENABLE; -import static org.apache.logging.log4j.spi.LoggingSystemProperty.THREAD_CONTEXT_GARBAGE_FREE_ENABLED; -import static org.apache.logging.log4j.spi.LoggingSystemProperty.THREAD_CONTEXT_INITIAL_CAPACITY; -import static org.apache.logging.log4j.spi.LoggingSystemProperty.THREAD_CONTEXT_MAP_CLASS; -import static org.apache.logging.log4j.spi.LoggingSystemProperty.THREAD_CONTEXT_MAP_INHERITABLE; -import static org.apache.logging.log4j.spi.LoggingSystemProperty.THREAD_CONTEXT_STACK_ENABLED; import aQute.bnd.annotation.spi.ServiceConsumer; import java.io.IOException; @@ -76,10 +70,8 @@ public class LoggingSystem { private static final Lazy SYSTEM = Lazy.relaxed(LoggingSystem::new); - private final Lazy providerLazy = Lazy.relaxed(this::findProvider); + private final Lazy providerLazy = Lazy.relaxed(this::findProvider); private final Lazy environmentLazy = Lazy.relaxed(PropertiesUtil::getProperties); - private final Lazy loggerContextFactoryLazy = - environmentLazy.map(environment -> getProvider().createLoggerContextFactory(environment)); private final Lazy messageFactoryLazy = environmentLazy.map(environment -> { final String className = environment.getStringProperty(LOGGER_MESSAGE_FACTORY_CLASS); if (className != null) { @@ -101,24 +93,22 @@ public class LoggingSystem { return new DefaultFlowMessageFactory(); }); private final Lazy> threadContextMapFactoryLazy = - environmentLazy.map(environment -> () -> getProvider().createContextMap(environment)); - private final Lazy> threadContextStackFactoryLazy = - environmentLazy.map(environment -> () -> getProvider().createContextStack(environment)); + environmentLazy.map(environment -> () -> getProvider().getThreadContextMapFactory()); private final Lazy recyclerFactoryLazy = environmentLazy.map(RecyclerFactoryRegistry::findRecyclerFactory); public LoggingSystem() {} - private SystemProvider getProvider() { - return providerLazy.get(); + public static Provider getProvider() { + return getInstance().providerLazy.get(); } - private SystemProvider findProvider() { + private Provider findProvider() { final SortedMap providers = new TreeMap<>(); loadDefaultProviders().forEach(p -> providers.put(p.getPriority(), p)); loadLegacyProviders().forEach(p -> providers.put(p.getPriority(), p)); if (providers.isEmpty()) { - return new SystemProvider(); + return new SimpleProvider(); } final Provider provider = providers.get(providers.lastKey()); if (providers.size() > 1) { @@ -127,29 +117,17 @@ private SystemProvider findProvider() { sb.append("Using ").append(provider); LowLevelLogUtil.log(sb.toString()); } - return new SystemProvider(provider); - } - - public void setLoggerContextFactory(final LoggerContextFactory loggerContextFactory) { - loggerContextFactoryLazy.set(loggerContextFactory); + return provider; } public void setMessageFactory(final MessageFactory messageFactory) { messageFactoryLazy.set(messageFactory); } - public void setFlowMessageFactory(final FlowMessageFactory flowMessageFactory) { - flowMessageFactoryLazy.set(flowMessageFactory); - } - public void setThreadContextMapFactory(final Supplier threadContextMapFactory) { threadContextMapFactoryLazy.set(threadContextMapFactory); } - public void setThreadContextStackFactory(final Supplier threadContextStackFactory) { - threadContextStackFactoryLazy.set(threadContextStackFactory); - } - public void setRecyclerFactory(final RecyclerFactory factory) { recyclerFactoryLazy.set(factory); } @@ -161,14 +139,6 @@ public static LoggingSystem getInstance() { return SYSTEM.get(); } - /** - * Gets the current LoggerContextFactory. This may initialize the instance if this is the first time it was - * requested. - */ - public static LoggerContextFactory getLoggerContextFactory() { - return getInstance().loggerContextFactoryLazy.get(); - } - public static MessageFactory getMessageFactory() { return getInstance().messageFactoryLazy.get(); } @@ -177,22 +147,15 @@ public static FlowMessageFactory getFlowMessageFactory() { return getInstance().flowMessageFactoryLazy.get(); } - /** - * Creates a new ThreadContextMap. - */ - public static ThreadContextMap createContextMap() { - return getInstance().threadContextMapFactoryLazy.get().get(); + public static RecyclerFactory getRecyclerFactory() { + return getInstance().recyclerFactoryLazy.get(); } /** - * Creates a new ThreadContextStack. + * Temporarily for tests */ - public static ThreadContextStack createContextStack() { - return getInstance().threadContextStackFactoryLazy.get().get(); - } - - public static RecyclerFactory getRecyclerFactory() { - return getInstance().recyclerFactoryLazy.get(); + public static void reset() { + getProvider().reset(); } private static List loadDefaultProviders() { @@ -231,7 +194,7 @@ private static boolean validVersion(final String version) { return false; } - private static T tryInstantiate(final Class clazz) { + static T tryInstantiate(final Class clazz) { Constructor constructor; try { constructor = clazz.getConstructor(); @@ -259,7 +222,7 @@ private static T tryInstantiate(final Class clazz) { return null; } - private static T createInstance(final String className, final Class type) { + static T createInstance(final String className, final Class type) { try { final Class loadedClass = LoaderUtil.loadClass(className); final Class typedClass = loadedClass.asSubclass(type); @@ -271,98 +234,9 @@ private static T createInstance(final String className, final Class type) } } - private static final class SystemProvider { - private final Provider provider; - - private SystemProvider() { - this(null); - } - - private SystemProvider(final Provider provider) { - this.provider = provider; - } - - public LoggerContextFactory createLoggerContextFactory(final PropertyEnvironment environment) { - final String customFactoryClass = - environment.getStringProperty(LoggingSystemProperty.LOGGER_CONTEXT_FACTORY_CLASS); - if (customFactoryClass != null) { - final LoggerContextFactory customFactory = - createInstance(customFactoryClass, LoggerContextFactory.class); - if (customFactory != null) { - return customFactory; - } - } - if (provider != null) { - final Class factoryClass = provider.loadLoggerContextFactory(); - if (factoryClass != null) { - final LoggerContextFactory factory = tryInstantiate(factoryClass); - if (factory != null) { - return factory; - } - } - } - LowLevelLogUtil.log("Log4j could not find a logging implementation. " - + "Please add log4j-core dependencies to classpath or module path. " - + "Using SimpleLogger to log to the console."); - return SimpleLoggerContextFactory.INSTANCE; - } - - /** - * Creates the ThreadContextMap instance used by the ThreadContext. - *

- * A garbage-free StringMap-based context map can - * be installed by setting system property {@link LoggingSystemProperty#THREAD_CONTEXT_GARBAGE_FREE_ENABLED} to {@code true}. - *

- * Furthermore, any custom {@code ThreadContextMap} can be installed by setting system property - * {@link LoggingSystemProperty#THREAD_CONTEXT_MAP_CLASS} to the fully qualified class name of the class implementing the - * {@code ThreadContextMap} interface. (Also implement the {@code ReadOnlyThreadContextMap} interface if your custom - * {@code ThreadContextMap} implementation should be accessible to applications via the - * {@link ThreadContext#getThreadContextMap()} method.) - *

- * Instead of system properties, the above can also be specified in a properties file named - * {@code log4j2.component.properties} in the classpath. - *

- * - * @see ThreadContextMap - * @see ReadOnlyThreadContextMap - * @see org.apache.logging.log4j.ThreadContext - */ - public ThreadContextMap createContextMap(final PropertyEnvironment environment) { - final String customThreadContextMap = environment.getStringProperty(THREAD_CONTEXT_MAP_CLASS); - if (customThreadContextMap != null) { - final ThreadContextMap customContextMap = - createInstance(customThreadContextMap, ThreadContextMap.class); - if (customContextMap != null) { - return customContextMap; - } - } - final boolean enableMap = environment.getBooleanProperty( - LoggingSystemProperty.THREAD_CONTEXT_MAP_ENABLED, - environment.getBooleanProperty(LoggingSystemProperty.THREAD_CONTEXT_ENABLE, true)); - if (!enableMap) { - return new NoOpThreadContextMap(); - } - final Class mapClass = provider.loadThreadContextMap(); - if (mapClass != null) { - final ThreadContextMap map = tryInstantiate(mapClass); - if (map != null) { - return map; - } - } - final boolean garbageFreeEnabled = environment.getBooleanProperty(THREAD_CONTEXT_GARBAGE_FREE_ENABLED); - final boolean inheritableMap = environment.getBooleanProperty(THREAD_CONTEXT_MAP_INHERITABLE); - final int initialCapacity = environment.getIntegerProperty( - THREAD_CONTEXT_INITIAL_CAPACITY, THREAD_CONTEXT_DEFAULT_INITIAL_CAPACITY); - if (garbageFreeEnabled) { - return new GarbageFreeSortedArrayThreadContextMap(inheritableMap, initialCapacity); - } - return new CopyOnWriteSortedArrayThreadContextMap(inheritableMap, initialCapacity); - } - - public ThreadContextStack createContextStack(final PropertyEnvironment environment) { - final boolean enableStack = environment.getBooleanProperty( - THREAD_CONTEXT_STACK_ENABLED, environment.getBooleanProperty(THREAD_CONTEXT_ENABLE, true)); - return new DefaultThreadContextStack(enableStack); + private static class SimpleProvider extends Provider { + public SimpleProvider() { + super(0, "3.0.0", SimpleLoggerContextFactory.class, NoOpThreadContextMap.class); } } } diff --git a/log4j-api/src/main/java/org/apache/logging/log4j/spi/Provider.java b/log4j-api/src/main/java/org/apache/logging/log4j/spi/Provider.java index 94c4b2c82f8..451d7811d2e 100644 --- a/log4j-api/src/main/java/org/apache/logging/log4j/spi/Provider.java +++ b/log4j-api/src/main/java/org/apache/logging/log4j/spi/Provider.java @@ -16,12 +16,25 @@ */ package org.apache.logging.log4j.spi; +import static org.apache.logging.log4j.spi.LoggingSystemProperty.THREAD_CONTEXT_ENABLE; +import static org.apache.logging.log4j.spi.LoggingSystemProperty.THREAD_CONTEXT_GARBAGE_FREE_ENABLED; +import static org.apache.logging.log4j.spi.LoggingSystemProperty.THREAD_CONTEXT_INITIAL_CAPACITY; +import static org.apache.logging.log4j.spi.LoggingSystemProperty.THREAD_CONTEXT_MAP_CLASS; +import static org.apache.logging.log4j.spi.LoggingSystemProperty.THREAD_CONTEXT_MAP_INHERITABLE; +import static org.apache.logging.log4j.spi.LoggingSystemProperty.THREAD_CONTEXT_STACK_ENABLED; + import java.lang.ref.WeakReference; import java.net.URL; import java.util.Objects; import java.util.Properties; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; import org.apache.logging.log4j.Logger; +import org.apache.logging.log4j.simple.SimpleLoggerContextFactory; import org.apache.logging.log4j.status.StatusLogger; +import org.apache.logging.log4j.util.Lazy; +import org.apache.logging.log4j.util.LowLevelLogUtil; +import org.apache.logging.log4j.util.PropertiesUtil; /** * Model class for a Log4j 2 provider. The properties in this class correspond to the properties used in a @@ -42,6 +55,8 @@ public class Provider { */ public static final String LOGGER_CONTEXT_FACTORY = "LoggerContextFactory"; + public static final int THREAD_CONTEXT_DEFAULT_INITIAL_CAPACITY = 16; + private static final Integer DEFAULT_PRIORITY = -1; private static final Logger LOGGER = StatusLogger.getLogger(); @@ -54,6 +69,12 @@ public class Provider { private final URL url; private final WeakReference classLoader; + // Temporary fields until we revert to Log4j API 2.x + private final Lazy loggerContextFactory = Lazy.lazy(this::createLoggerContextFactory); + private final Lazy threadContextMapFactory = Lazy.lazy(this::createThreadContextMap); + private final Lazy threadContextStack = Lazy.lazy(this::createThreadContextStack); + private final Lock lock = new ReentrantLock(); + public Provider(final Properties props, final URL url, final ClassLoader classLoader) { this.url = url; this.classLoader = new WeakReference<>(classLoader); @@ -193,6 +214,91 @@ public URL getUrl() { return url; } + public LoggerContextFactory getLoggerContextFactory() { + return loggerContextFactory.get(); + } + + protected LoggerContextFactory createLoggerContextFactory() { + final String customFactoryClass = + PropertiesUtil.getProperties().getStringProperty(LoggingSystemProperty.LOGGER_CONTEXT_FACTORY_CLASS); + if (customFactoryClass != null) { + final LoggerContextFactory customFactory = + LoggingSystem.createInstance(customFactoryClass, LoggerContextFactory.class); + if (customFactory != null) { + return customFactory; + } + } + final Class factoryClass = loadLoggerContextFactory(); + if (factoryClass != null) { + final LoggerContextFactory factory = LoggingSystem.tryInstantiate(factoryClass); + if (factory != null) { + return factory; + } + } + LowLevelLogUtil.log("Log4j could not find a logging implementation. " + + "Please add log4j-core dependencies to classpath or module path. " + + "Using SimpleLogger to log to the console."); + return SimpleLoggerContextFactory.INSTANCE; + } + + public ThreadContextMap getThreadContextMapFactory() { + return threadContextMapFactory.get(); + } + + protected ThreadContextMap createThreadContextMap() { + final PropertiesUtil environment = PropertiesUtil.getProperties(); + final String customThreadContextMap = environment.getStringProperty(THREAD_CONTEXT_MAP_CLASS); + if (customThreadContextMap != null) { + final ThreadContextMap customContextMap = + LoggingSystem.createInstance(customThreadContextMap, ThreadContextMap.class); + if (customContextMap != null) { + return customContextMap; + } + } + final boolean enableMap = environment.getBooleanProperty( + LoggingSystemProperty.THREAD_CONTEXT_MAP_ENABLED, + environment.getBooleanProperty(LoggingSystemProperty.THREAD_CONTEXT_ENABLE, true)); + if (!enableMap) { + return new NoOpThreadContextMap(); + } + final Class mapClass = loadThreadContextMap(); + if (mapClass != null) { + final ThreadContextMap map = LoggingSystem.tryInstantiate(mapClass); + if (map != null) { + return map; + } + } + final boolean garbageFreeEnabled = environment.getBooleanProperty(THREAD_CONTEXT_GARBAGE_FREE_ENABLED); + final boolean inheritableMap = environment.getBooleanProperty(THREAD_CONTEXT_MAP_INHERITABLE); + final int initialCapacity = environment.getIntegerProperty( + THREAD_CONTEXT_INITIAL_CAPACITY, THREAD_CONTEXT_DEFAULT_INITIAL_CAPACITY); + if (garbageFreeEnabled) { + return new GarbageFreeSortedArrayThreadContextMap(inheritableMap, initialCapacity); + } + return new CopyOnWriteSortedArrayThreadContextMap(inheritableMap, initialCapacity); + } + + public ThreadContextStack getThreadContextStack() { + return threadContextStack.get(); + } + + protected ThreadContextStack createThreadContextStack() { + final PropertiesUtil environment = PropertiesUtil.getProperties(); + final boolean enableStack = environment.getBooleanProperty( + THREAD_CONTEXT_STACK_ENABLED, environment.getBooleanProperty(THREAD_CONTEXT_ENABLE, true)); + return new DefaultThreadContextStack(enableStack); + } + + public synchronized void setThreadContextMapFactory(final ThreadContextMap threadContextMapFactory) { + this.threadContextMapFactory.set(threadContextMapFactory); + } + + void reset() { + loggerContextFactory.set(null); + threadContextMapFactory.set(null); + threadContextStack.set(null); + } + @Override public String toString() { final StringBuilder result = new StringBuilder("Provider["); diff --git a/log4j-async-logger/src/main/java/org/apache/logging/log4j/async/logger/AsyncLogger.java b/log4j-async-logger/src/main/java/org/apache/logging/log4j/async/logger/AsyncLogger.java index 7143a5b7b76..80578ee369a 100644 --- a/log4j-async-logger/src/main/java/org/apache/logging/log4j/async/logger/AsyncLogger.java +++ b/log4j-async-logger/src/main/java/org/apache/logging/log4j/async/logger/AsyncLogger.java @@ -37,6 +37,8 @@ import org.apache.logging.log4j.message.FlowMessageFactory; import org.apache.logging.log4j.message.Message; import org.apache.logging.log4j.message.MessageFactory; +import org.apache.logging.log4j.plugins.Inject; +import org.apache.logging.log4j.plugins.Named; import org.apache.logging.log4j.spi.recycler.Recycler; import org.apache.logging.log4j.spi.recycler.RecyclerFactory; import org.apache.logging.log4j.util.StringMap; @@ -281,4 +283,33 @@ private static StringMap getContextData(final ReusableLogEvent event) { AsyncLoggerDisruptor getAsyncLoggerDisruptor() { return loggerDisruptor; } + + public static class Builder extends Logger.Builder { + + private final AsyncLoggerDisruptor disruptor; + + @Inject + public Builder( + final LoggerContext context, + final MessageFactory messageFactory, + final FlowMessageFactory flowMessageFactory, + final RecyclerFactory recyclerFactory, + final @Named("StatusLogger") org.apache.logging.log4j.Logger statusLogger, + final AsyncLoggerDisruptor disruptor) { + super(context, messageFactory, flowMessageFactory, recyclerFactory, statusLogger); + this.disruptor = disruptor; + } + + @Override + public Logger build() { + return new AsyncLogger( + getContext(), + getName(), + getActualMessageFactory(), + getFlowMessageFactory(), + getRecyclerFactory(), + getStatusLogger(), + disruptor); + } + } } diff --git a/log4j-async-logger/src/main/java/org/apache/logging/log4j/async/logger/AsyncLoggerConfig.java b/log4j-async-logger/src/main/java/org/apache/logging/log4j/async/logger/AsyncLoggerConfig.java index b8591342bb6..690a67e5933 100644 --- a/log4j-async-logger/src/main/java/org/apache/logging/log4j/async/logger/AsyncLoggerConfig.java +++ b/log4j-async-logger/src/main/java/org/apache/logging/log4j/async/logger/AsyncLoggerConfig.java @@ -29,7 +29,6 @@ import org.apache.logging.log4j.core.config.Configuration; import org.apache.logging.log4j.core.config.LoggerConfig; import org.apache.logging.log4j.core.config.Property; -import org.apache.logging.log4j.core.impl.LogEventFactory; import org.apache.logging.log4j.kit.logger.AbstractLogger; import org.apache.logging.log4j.plugins.Configurable; import org.apache.logging.log4j.plugins.Plugin; @@ -65,10 +64,10 @@ */ @Configurable(printObject = true) @Plugin("asyncLogger") -public class AsyncLoggerConfig extends LoggerConfig { +public final class AsyncLoggerConfig extends LoggerConfig { private static final ThreadLocal ASYNC_LOGGER_ENTERED = ThreadLocal.withInitial(() -> Boolean.FALSE); - private AsyncLoggerConfigDelegate delegate; + private AsyncLoggerConfigDisruptor disruptor; @PluginFactory public static > B newAsyncBuilder() { @@ -80,9 +79,10 @@ public static class Builder> extends LoggerConfig.Builder appenders, final Filter filter, @@ -104,22 +103,23 @@ protected AsyncLoggerConfig( final boolean additive, final Property[] properties, final Configuration config, - final boolean includeLocation, - final LogEventFactory logEventFactory) { - super(name, appenders, filter, level, additive, properties, config, includeLocation, logEventFactory); + final boolean includeLocation) { + super(name, appenders, filter, level, additive, properties, config, includeLocation); } @Override public void initialize() { final Configuration configuration = getConfiguration(); - final DisruptorConfiguration disruptorConfig = configuration.addExtensionIfAbsent( - DisruptorConfiguration.class, - () -> DisruptorConfiguration.newBuilder().build()); - delegate = disruptorConfig.getAsyncLoggerConfigDelegate(); - delegate.setLogEventFactory(getLogEventFactory()); + // Retrieve or create the common holder for the disruptor. + final DisruptorConfiguration disruptorConfiguration = configuration.addExtensionIfAbsent( + DisruptorConfiguration.class, () -> DisruptorConfiguration.newBuilder() + .setConfiguration(configuration) + .build()); + this.disruptor = disruptorConfiguration.getLoggerConfigDisruptor(); super.initialize(); } + @Override protected void log(final LogEvent event, final Predicate predicate) { // See LOG4J2-2301 if (predicate == null @@ -153,8 +153,8 @@ protected void log(final LogEvent event, final Predicate predicate } // package-protected for testing - AsyncLoggerConfigDelegate getAsyncLoggerConfigDelegate() { - return delegate; + AsyncLoggerConfigDisruptor getAsyncLoggerConfigDisruptor() { + return disruptor; } @Override @@ -165,7 +165,7 @@ protected void callAppenders(final LogEvent event) { private void logToAsyncDelegate(final LogEvent event) { // Passes on the event to a separate thread that will call // asyncCallAppenders(LogEvent). - if (!delegate.tryEnqueue(event, this)) { + if (!disruptor.tryEnqueue(event, this)) { handleQueueFull(event); } } @@ -177,7 +177,7 @@ private void handleQueueFull(final LogEvent event) { logToAsyncLoggerConfigsOnCurrentThread(event); } else { // otherwise, we leave it to the user preference - final EventRoute eventRoute = delegate.getEventRoute(event.getLevel()); + final EventRoute eventRoute = disruptor.getEventRoute(event.getLevel()); switch (eventRoute) { case DISCARD: break; @@ -193,14 +193,15 @@ private void handleQueueFull(final LogEvent event) { } void logInBackgroundThread(final LogEvent event) { - delegate.enqueueEvent(event, this); + disruptor.enqueueEvent(event, this); } /** * Called by AsyncLoggerConfigHelper.RingBufferLog4jEventHandler. - * + *

* This method will log the provided event to only configs of type {@link AsyncLoggerConfig} (not * default {@link LoggerConfig} definitions), which will be invoked on the calling thread. + *

*/ void logToAsyncLoggerConfigsOnCurrentThread(final LogEvent event) { // skip the filter, which was already called on the logging thread @@ -231,30 +232,33 @@ public boolean stop(final long timeout, final TimeUnit timeUnit) { */ @Configurable(printObject = true) @Plugin("asyncRoot") - public static class RootLogger extends LoggerConfig { + public static final class RootLogger extends LoggerConfig { @PluginFactory - public static > B newAsyncRootBuilder() { - return new Builder().asBuilder(); + public static Builder newAsyncRootBuilder() { + return new Builder(); + } + + private RootLogger() { + super(Strings.EMPTY, Level.ERROR, false, null); } - public static class Builder> extends RootLogger.Builder { + public static class Builder extends RootLogger.Builder { @Override public LoggerConfig build() { final LevelAndRefs container = LoggerConfig.getLevelAndRefs(getLevel(), getRefs(), getLevelAndRefs(), getConfig()); - final String includeLocationConfigValue = getIncludeLocation(); + final boolean includeLocation = Boolean.TRUE.equals(getIncludeLocation()); return new AsyncLoggerConfig( LogManager.ROOT_LOGGER_NAME, container.refs, getFilter(), container.level, - isAdditivity(), + ADDITIVITY, getProperties(), getConfig(), - Boolean.parseBoolean(includeLocationConfigValue), - getLogEventFactory()); + includeLocation); } } } diff --git a/log4j-async-logger/src/main/java/org/apache/logging/log4j/async/logger/AsyncLoggerConfigDelegate.java b/log4j-async-logger/src/main/java/org/apache/logging/log4j/async/logger/AsyncLoggerConfigDelegate.java index e3997404c42..f58ce5d2975 100644 --- a/log4j-async-logger/src/main/java/org/apache/logging/log4j/async/logger/AsyncLoggerConfigDelegate.java +++ b/log4j-async-logger/src/main/java/org/apache/logging/log4j/async/logger/AsyncLoggerConfigDelegate.java @@ -19,7 +19,6 @@ import org.apache.logging.log4j.Level; import org.apache.logging.log4j.core.LogEvent; import org.apache.logging.log4j.core.async.EventRoute; -import org.apache.logging.log4j.core.impl.LogEventFactory; /** * Encapsulates the mechanism used to log asynchronously. There is one delegate per configuration, which is shared by @@ -43,13 +42,4 @@ public interface AsyncLoggerConfigDelegate { void enqueueEvent(LogEvent event, AsyncLoggerConfig asyncLoggerConfig); boolean tryEnqueue(LogEvent event, AsyncLoggerConfig asyncLoggerConfig); - - /** - * Notifies the delegate what LogEventFactory an AsyncLoggerConfig is using, so the delegate can determine - * whether to populate the ring buffer with mutable log events or not. This method may be invoked multiple times - * for all AsyncLoggerConfigs that use this delegate. - * - * @param logEventFactory the factory used - */ - void setLogEventFactory(LogEventFactory logEventFactory); } diff --git a/log4j-async-logger/src/main/java/org/apache/logging/log4j/async/logger/AsyncLoggerConfigDisruptor.java b/log4j-async-logger/src/main/java/org/apache/logging/log4j/async/logger/AsyncLoggerConfigDisruptor.java index a17aa977603..1faed03aaa4 100644 --- a/log4j-async-logger/src/main/java/org/apache/logging/log4j/async/logger/AsyncLoggerConfigDisruptor.java +++ b/log4j-async-logger/src/main/java/org/apache/logging/log4j/async/logger/AsyncLoggerConfigDisruptor.java @@ -33,6 +33,7 @@ import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; import org.apache.logging.log4j.Level; +import org.apache.logging.log4j.async.logger.internal.DisruptorUtil; import org.apache.logging.log4j.core.AbstractLifeCycle; import org.apache.logging.log4j.core.LogEvent; import org.apache.logging.log4j.core.ReusableLogEvent; @@ -49,6 +50,7 @@ import org.apache.logging.log4j.core.util.Log4jThreadFactory; import org.apache.logging.log4j.core.util.Throwables; import org.apache.logging.log4j.message.ReusableMessage; +import org.apache.logging.log4j.plugins.Inject; /** * Helper class decoupling the {@code AsyncLoggerConfig} class from the LMAX Disruptor library. @@ -62,7 +64,7 @@ * This class serves to make the dependency on the Disruptor optional, so that these classes are only loaded when the * {@code AsyncLoggerConfig} is actually used. */ -public class AsyncLoggerConfigDisruptor extends AbstractLifeCycle implements AsyncLoggerConfigDelegate { +public class AsyncLoggerConfigDisruptor extends AbstractLifeCycle { private static final int MAX_DRAIN_ATTEMPTS_BEFORE_SHUTDOWN = 200; private static final int SLEEP_MILLIS_BETWEEN_DRAIN_ATTEMPTS = 50; @@ -166,20 +168,21 @@ private void notifyIntermediateProgress(final long sequence) { }; private AsyncQueueFullPolicy asyncQueueFullPolicy; - private Boolean mutable = Boolean.FALSE; private volatile Disruptor disruptor; private long backgroundThreadId; // LOG4J2-471 private EventTranslatorTwoArg translator; private static volatile boolean alreadyLoggedWarning; - private final AsyncWaitStrategyFactory asyncWaitStrategyFactory; - private WaitStrategy waitStrategy; + private final WaitStrategy waitStrategy; + private final boolean mutable; private final Lock startLock = new ReentrantLock(); private final Lock queueFullEnqueueLock = new ReentrantLock(); - public AsyncLoggerConfigDisruptor(final AsyncWaitStrategyFactory asyncWaitStrategyFactory) { - this.asyncWaitStrategyFactory = asyncWaitStrategyFactory; // may be null + @Inject + public AsyncLoggerConfigDisruptor(final WaitStrategy waitStrategy, final LogEventFactory logEventFactory) { + this.waitStrategy = waitStrategy; + this.mutable = logEventFactory instanceof ReusableLogEventFactory; } // package-protected for testing @@ -187,14 +190,6 @@ WaitStrategy getWaitStrategy() { return waitStrategy; } - // called from AsyncLoggerConfig constructor - @Override - public void setLogEventFactory(final LogEventFactory logEventFactory) { - // if any AsyncLoggerConfig uses a ReusableLogEventFactory - // then we need to populate our ringbuffer with MutableLogEvents - this.mutable = mutable || (logEventFactory instanceof ReusableLogEventFactory); - } - /** * Increases the reference count and creates and starts a new Disruptor and associated thread if none currently * exists. @@ -213,8 +208,6 @@ public void start() { LOGGER.trace("AsyncLoggerConfigDisruptor creating new disruptor for this configuration."); final int ringBufferSize = DisruptorUtil.calculateRingBufferSize(Log4jPropertyKey.ASYNC_CONFIG_RING_BUFFER_SIZE); - waitStrategy = DisruptorUtil.createWaitStrategy( - Log4jPropertyKey.ASYNC_CONFIG_WAIT_STRATEGY, asyncWaitStrategyFactory); final ThreadFactory threadFactory = new Log4jThreadFactory("AsyncLoggerConfig", true, Thread.NORM_PRIORITY) { @@ -304,7 +297,6 @@ private static boolean hasBacklog(final Disruptor theDisruptor) { return !ringBuffer.hasAvailableCapacity(ringBuffer.getBufferSize()); } - @Override public EventRoute getEventRoute(final Level logLevel) { final int remainingCapacity = remainingDisruptorCapacity(); if (remainingCapacity < 0) { @@ -332,7 +324,6 @@ private boolean hasLog4jBeenShutDown(final Disruptor aDisrupt return false; } - @Override public void enqueueEvent(final LogEvent event, final AsyncLoggerConfig asyncLoggerConfig) { // LOG4J2-639: catch NPE if disruptor field was set to null after our check above try { @@ -416,7 +407,6 @@ private boolean synchronizeEnqueueWhenQueueFull() { && !(Thread.currentThread() instanceof Log4jThread); } - @Override public boolean tryEnqueue(final LogEvent event, final AsyncLoggerConfig asyncLoggerConfig) { return disruptor.getRingBuffer().tryPublishEvent(translator, event, asyncLoggerConfig); } diff --git a/log4j-async-logger/src/main/java/org/apache/logging/log4j/async/logger/AsyncLoggerContext.java b/log4j-async-logger/src/main/java/org/apache/logging/log4j/async/logger/AsyncLoggerContext.java index bc96643c349..772afe9c617 100644 --- a/log4j-async-logger/src/main/java/org/apache/logging/log4j/async/logger/AsyncLoggerContext.java +++ b/log4j-async-logger/src/main/java/org/apache/logging/log4j/async/logger/AsyncLoggerContext.java @@ -18,123 +18,74 @@ import java.net.URI; import java.util.concurrent.TimeUnit; +import org.apache.logging.log4j.async.logger.internal.DefaultBundle; 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.DefaultConfiguration; -import org.apache.logging.log4j.message.FlowMessageFactory; -import org.apache.logging.log4j.message.MessageFactory; +import org.apache.logging.log4j.plugins.Inject; import org.apache.logging.log4j.plugins.di.ConfigurableInstanceFactory; -import org.apache.logging.log4j.spi.recycler.RecyclerFactory; +import org.apache.logging.log4j.plugins.di.Key; import org.apache.logging.log4j.status.StatusLogger; +import org.apache.logging.log4j.util.Lazy; /** * {@code LoggerContext} that creates {@code AsyncLogger} objects. */ public class AsyncLoggerContext extends LoggerContext { - private final AsyncLoggerDisruptor loggerDisruptor; - - public AsyncLoggerContext(final String name) { - super(name); - loggerDisruptor = new AsyncLoggerDisruptor(name, this::createAsyncWaitStrategyFactory); - } - - public AsyncLoggerContext(final String name, final Object externalContext) { - super(name, externalContext); - loggerDisruptor = new AsyncLoggerDisruptor(name, this::createAsyncWaitStrategyFactory); - } - - public AsyncLoggerContext(final String name, final Object externalContext, final URI configLocn) { - super(name, externalContext, configLocn); - loggerDisruptor = new AsyncLoggerDisruptor(name, this::createAsyncWaitStrategyFactory); - } + private final Lazy loggerDisruptor = Lazy.lazy(() -> + getInstanceFactory().getInstance(AsyncLoggerDisruptor.Factory.class).createAsyncLoggerDisruptor(getName())); public AsyncLoggerContext( final String name, final Object externalContext, - final URI configLocn, + final URI configLocation, final ConfigurableInstanceFactory instanceFactory) { - super(name, externalContext, configLocn, instanceFactory); - loggerDisruptor = new AsyncLoggerDisruptor(name, this::createAsyncWaitStrategyFactory); - } - - public AsyncLoggerContext(final String name, final Object externalContext, final String configLocn) { - super(name, externalContext, configLocn); - loggerDisruptor = new AsyncLoggerDisruptor(name, this::createAsyncWaitStrategyFactory); - } - - public AsyncLoggerContext( - final String name, - final Object externalContext, - final String configLocn, - final ConfigurableInstanceFactory instanceFactory) { - super(name, externalContext, configLocn, instanceFactory); - loggerDisruptor = new AsyncLoggerDisruptor(name, this::createAsyncWaitStrategyFactory); - } - - private AsyncWaitStrategyFactory createAsyncWaitStrategyFactory() { - final DisruptorConfiguration disruptorConfiguration = - getConfiguration().getExtension(DisruptorConfiguration.class); - return disruptorConfiguration != null ? disruptorConfiguration.getWaitStrategyFactory() : null; + super(name, externalContext, configLocation, instanceFactory); + instanceFactory.registerBundle(DefaultBundle.class); + instanceFactory.registerBinding(Key.forClass(AsyncLoggerDisruptor.class), loggerDisruptor); } @Override - protected Logger newInstance( - final LoggerContext ctx, - final String name, - final MessageFactory messageFactory, - final FlowMessageFactory flowMessageFactory, - final RecyclerFactory recyclerFactory, - final org.apache.logging.log4j.Logger statusLogger) { - return new AsyncLogger( - ctx, name, messageFactory, flowMessageFactory, recyclerFactory, statusLogger, loggerDisruptor); + protected Class getLoggerBuilderClass() { + return AsyncLogger.Builder.class; } @Override public void setName(final String name) { super.setName("AsyncContext[" + name + "]"); - loggerDisruptor.setContextName(name); + if (loggerDisruptor.isInitialized()) { + loggerDisruptor.get().setContextName(name); + } } - /* - * (non-Javadoc) - * - * @see org.apache.logging.log4j.core.LoggerContext#start() - */ @Override public void start() { - loggerDisruptor.start(); + getAsyncLoggerDisruptor().start(); super.start(); } - /* - * (non-Javadoc) - * - * @see org.apache.logging.log4j.core.LoggerContext#start(org.apache.logging.log4j.core.config.Configuration) - */ @Override public void start(final Configuration config) { - maybeStartHelper(config); - super.start(config); - } - - private void maybeStartHelper(final Configuration config) { // If no log4j configuration was found, there are no loggers // and there is no point in starting the disruptor (which takes up // significant memory and starts a thread). if (config instanceof DefaultConfiguration) { StatusLogger.getLogger().debug("[{}] Not starting Disruptor for DefaultConfiguration.", getName()); } else { - loggerDisruptor.start(); + getAsyncLoggerDisruptor().start(); } + super.start(config); } @Override public boolean stop(final long timeout, final TimeUnit timeUnit) { setStopping(); // first stop Disruptor - loggerDisruptor.stop(timeout, timeUnit); + if (loggerDisruptor.isInitialized()) { + loggerDisruptor.get().stop(timeout, timeUnit); + } super.stop(timeout, timeUnit); return true; } @@ -146,6 +97,19 @@ public boolean includeLocation() { // package-protected for tests AsyncLoggerDisruptor getAsyncLoggerDisruptor() { - return loggerDisruptor; + return loggerDisruptor.get(); + } + + public static final class Builder extends LoggerContext.Builder { + + @Inject + public Builder(final ConfigurableInstanceFactory parentInstanceFactory) { + super(parentInstanceFactory); + } + + @Override + public LoggerContext build() { + return new AsyncLoggerContext(getContextName(), null, getConfigLocation(), createInstanceFactory()); + } } } diff --git a/log4j-async-logger/src/main/java/org/apache/logging/log4j/async/logger/AsyncLoggerContextSelector.java b/log4j-async-logger/src/main/java/org/apache/logging/log4j/async/logger/AsyncLoggerContextSelector.java index 31e5b9c4031..22a36e0b3fc 100644 --- a/log4j-async-logger/src/main/java/org/apache/logging/log4j/async/logger/AsyncLoggerContextSelector.java +++ b/log4j-async-logger/src/main/java/org/apache/logging/log4j/async/logger/AsyncLoggerContextSelector.java @@ -16,7 +16,6 @@ */ package org.apache.logging.log4j.async.logger; -import java.net.URI; import org.apache.logging.log4j.core.LoggerContext; import org.apache.logging.log4j.core.selector.ClassLoaderContextSelector; import org.apache.logging.log4j.plugins.Inject; @@ -37,9 +36,8 @@ public AsyncLoggerContextSelector(final ConfigurableInstanceFactory instanceFact } @Override - protected LoggerContext createContext( - final String name, final URI configLocation, final ConfigurableInstanceFactory instanceFactory) { - return new AsyncLoggerContext(name, null, configLocation, instanceFactory); + protected LoggerContext.Builder newBuilder() { + return instanceFactory.getInstance(AsyncLoggerContext.Builder.class); } @Override diff --git a/log4j-async-logger/src/main/java/org/apache/logging/log4j/async/logger/AsyncLoggerDisruptor.java b/log4j-async-logger/src/main/java/org/apache/logging/log4j/async/logger/AsyncLoggerDisruptor.java index b2280e62b7e..d634899c137 100644 --- a/log4j-async-logger/src/main/java/org/apache/logging/log4j/async/logger/AsyncLoggerDisruptor.java +++ b/log4j-async-logger/src/main/java/org/apache/logging/log4j/async/logger/AsyncLoggerDisruptor.java @@ -22,13 +22,12 @@ import com.lmax.disruptor.WaitStrategy; import com.lmax.disruptor.dsl.Disruptor; import com.lmax.disruptor.dsl.ProducerType; -import java.util.Objects; import java.util.concurrent.ThreadFactory; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; -import java.util.function.Supplier; import org.apache.logging.log4j.Level; +import org.apache.logging.log4j.async.logger.internal.DisruptorUtil; import org.apache.logging.log4j.core.AbstractLifeCycle; import org.apache.logging.log4j.core.async.AsyncQueueFullPolicy; import org.apache.logging.log4j.core.async.AsyncQueueFullPolicyFactory; @@ -46,7 +45,7 @@ * life cycle of the context. The AsyncLoggerDisruptor of the context is shared by all AsyncLogger objects created by * that AsyncLoggerContext. */ -class AsyncLoggerDisruptor extends AbstractLifeCycle { +public class AsyncLoggerDisruptor extends AbstractLifeCycle { private static final int SLEEP_MILLIS_BETWEEN_DRAIN_ATTEMPTS = 50; private static final int MAX_DRAIN_ATTEMPTS_BEFORE_SHUTDOWN = 200; @@ -55,17 +54,14 @@ class AsyncLoggerDisruptor extends AbstractLifeCycle { private volatile Disruptor disruptor; private String contextName; - private final Supplier waitStrategyFactorySupplier; private long backgroundThreadId; private AsyncQueueFullPolicy asyncQueueFullPolicy; private WaitStrategy waitStrategy; - AsyncLoggerDisruptor( - final String contextName, final Supplier waitStrategyFactorySupplier) { + public AsyncLoggerDisruptor(final String contextName, final WaitStrategy waitStrategy) { this.contextName = contextName; - this.waitStrategyFactorySupplier = - Objects.requireNonNull(waitStrategyFactorySupplier, "waitStrategyFactorySupplier"); + this.waitStrategy = waitStrategy; } // package-protected for testing @@ -104,9 +100,6 @@ public void start() { LOGGER.trace("[{}] AsyncLoggerDisruptor creating new disruptor for this context.", contextName); final int ringBufferSize = DisruptorUtil.calculateRingBufferSize(Log4jPropertyKey.ASYNC_LOGGER_RING_BUFFER_SIZE); - final AsyncWaitStrategyFactory factory = - waitStrategyFactorySupplier.get(); // get factory from configuration - waitStrategy = DisruptorUtil.createWaitStrategy(Log4jPropertyKey.ASYNC_LOGGER_WAIT_STRATEGY, factory); final ThreadFactory threadFactory = new Log4jThreadFactory("AsyncLogger[" + contextName + "]", true, Thread.NORM_PRIORITY) { @@ -288,4 +281,9 @@ private void logWarningOnNpeFromDisruptorPublish( RingBuffer getRingBuffer() { return disruptor.getRingBuffer(); } + + @FunctionalInterface + public interface Factory { + AsyncLoggerDisruptor createAsyncLoggerDisruptor(String contextName); + } } diff --git a/log4j-async-logger/src/main/java/org/apache/logging/log4j/async/logger/AsyncWaitStrategyFactory.java b/log4j-async-logger/src/main/java/org/apache/logging/log4j/async/logger/AsyncWaitStrategyFactory.java index 491b37eebf7..24fd78688be 100644 --- a/log4j-async-logger/src/main/java/org/apache/logging/log4j/async/logger/AsyncWaitStrategyFactory.java +++ b/log4j-async-logger/src/main/java/org/apache/logging/log4j/async/logger/AsyncWaitStrategyFactory.java @@ -17,6 +17,7 @@ package org.apache.logging.log4j.async.logger; import com.lmax.disruptor.WaitStrategy; +import java.util.function.Supplier; /** * This interface allows users to configure a custom Disruptor WaitStrategy used for @@ -24,7 +25,8 @@ * * @since 2.17.3 */ -public interface AsyncWaitStrategyFactory { +@FunctionalInterface +public interface AsyncWaitStrategyFactory extends Supplier { /** * Creates and returns a non-null implementation of the LMAX Disruptor's WaitStrategy interface. * This WaitStrategy will be used by Log4j Async Loggers and Async LoggerConfigs. @@ -32,4 +34,9 @@ public interface AsyncWaitStrategyFactory { * @return the WaitStrategy instance to be used by Async Loggers and Async LoggerConfigs */ WaitStrategy createWaitStrategy(); + + @Override + default WaitStrategy get() { + return createWaitStrategy(); + } } diff --git a/log4j-async-logger/src/main/java/org/apache/logging/log4j/async/logger/BasicAsyncLoggerContextSelector.java b/log4j-async-logger/src/main/java/org/apache/logging/log4j/async/logger/BasicAsyncLoggerContextSelector.java index e8d458f4662..fd40008033a 100644 --- a/log4j-async-logger/src/main/java/org/apache/logging/log4j/async/logger/BasicAsyncLoggerContextSelector.java +++ b/log4j-async-logger/src/main/java/org/apache/logging/log4j/async/logger/BasicAsyncLoggerContextSelector.java @@ -16,7 +16,6 @@ */ package org.apache.logging.log4j.async.logger; -import java.net.URI; import org.apache.logging.log4j.core.LoggerContext; import org.apache.logging.log4j.core.selector.BasicContextSelector; import org.apache.logging.log4j.plugins.Inject; @@ -36,8 +35,13 @@ public BasicAsyncLoggerContextSelector(final ConfigurableInstanceFactory instanc super(instanceFactory); } + @Override + protected LoggerContext.Builder newBuilder() { + return instanceFactory.getInstance(AsyncLoggerContext.Builder.class); + } + @Override protected LoggerContext createContext() { - return new AsyncLoggerContext("AsyncDefault", null, (URI) null, instanceFactory); + return createContext("AsyncDefault", null, getClass().getClassLoader()); } } diff --git a/log4j-async-logger/src/main/java/org/apache/logging/log4j/async/logger/DisruptorConfiguration.java b/log4j-async-logger/src/main/java/org/apache/logging/log4j/async/logger/DisruptorConfiguration.java index 01eccd43e64..9dce16de99a 100644 --- a/log4j-async-logger/src/main/java/org/apache/logging/log4j/async/logger/DisruptorConfiguration.java +++ b/log4j-async-logger/src/main/java/org/apache/logging/log4j/async/logger/DisruptorConfiguration.java @@ -20,16 +20,30 @@ import java.util.concurrent.TimeUnit; import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.core.AbstractLifeCycle; +import org.apache.logging.log4j.core.config.Configuration; import org.apache.logging.log4j.core.config.ConfigurationExtension; 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.plugins.PluginAliases; import org.apache.logging.log4j.plugins.PluginBuilderAttribute; import org.apache.logging.log4j.plugins.PluginFactory; +import org.apache.logging.log4j.plugins.di.Key; import org.apache.logging.log4j.status.StatusLogger; import org.apache.logging.log4j.util.Lazy; import org.apache.logging.log4j.util.LoaderUtil; - +import org.jspecify.annotations.Nullable; + +/** + * A container for: + *
    + *
  1. A user provided wait strategy factory.
  2. + *
  3. The common {@link AsyncLoggerConfigDisruptor} instance shared by all logger configs.
  4. + *
+ * TODO: the only reason the disruptor needs a holder is that + * {@link org.apache.logging.log4j.plugins.di.InstanceFactory} is currently unable to stop the services it creates. + * In the future the disruptor will be in the instance factory. + */ @Configurable(printObject = true) @Plugin("Disruptor") @PluginAliases("AsyncWaitStrategyFactory") @@ -37,37 +51,35 @@ public final class DisruptorConfiguration extends AbstractLifeCycle implements C private static final Logger LOGGER = StatusLogger.getLogger(); - private final AsyncWaitStrategyFactory waitStrategyFactory; - private final Lazy loggerConfigDisruptor = - Lazy.lazy(() -> new AsyncLoggerConfigDisruptor(getWaitStrategyFactory())); + private final @Nullable AsyncWaitStrategyFactory waitStrategyFactory; + private final Lazy loggerConfigDisruptor; - private DisruptorConfiguration(final AsyncWaitStrategyFactory waitStrategyFactory) { + private DisruptorConfiguration( + final @Nullable AsyncWaitStrategyFactory waitStrategyFactory, final Configuration configuration) { this.waitStrategyFactory = waitStrategyFactory; + this.loggerConfigDisruptor = + Lazy.lazy(() -> configuration.getComponent(Key.forClass(AsyncLoggerConfigDisruptor.class))); } - public AsyncWaitStrategyFactory getWaitStrategyFactory() { + public @Nullable AsyncWaitStrategyFactory getWaitStrategyFactory() { return waitStrategyFactory; } - public AsyncLoggerConfigDelegate getAsyncLoggerConfigDelegate() { + AsyncLoggerConfigDisruptor getLoggerConfigDisruptor() { return loggerConfigDisruptor.get(); } @Override public void start() { - if (loggerConfigDisruptor.isInitialized()) { - LOGGER.info("Starting AsyncLoggerConfigDisruptor."); - loggerConfigDisruptor.get().start(); - } + LOGGER.info("Starting AsyncLoggerConfigDisruptor."); + loggerConfigDisruptor.get().start(); super.start(); } @Override public boolean stop(final long timeout, final TimeUnit timeUnit) { - if (loggerConfigDisruptor.isInitialized()) { - LOGGER.info("Stopping AsyncLoggerConfigDisruptor."); - loggerConfigDisruptor.get().stop(timeout, timeUnit); - } + LOGGER.info("Stopping AsyncLoggerConfigDisruptor."); + loggerConfigDisruptor.get().stop(timeout, timeUnit); return super.stop(timeout, timeUnit); } @@ -84,6 +96,8 @@ public static final class Builder implements org.apache.logging.log4j.plugins.ut @PluginBuilderAttribute private String waitFactory; + private Configuration configuration; + public Builder setFactoryClassName(final String factoryClassName) { this.factoryClassName = factoryClassName; return this; @@ -94,19 +108,21 @@ public Builder setWaitFactory(final String waitFactory) { return this; } - @Override - public DisruptorConfiguration build() { - return new DisruptorConfiguration( - createWaitStrategyFactory(Objects.toString(waitFactory, factoryClassName))); + @Inject + public Builder setConfiguration(final Configuration configuration) { + this.configuration = configuration; + return this; } - private static AsyncWaitStrategyFactory createWaitStrategyFactory(final String factoryClassName) { + @Override + public DisruptorConfiguration build() { + final String factoryClassName = Objects.toString(waitFactory, this.factoryClassName); if (factoryClassName != null) { try { final AsyncWaitStrategyFactory asyncWaitStrategyFactory = LoaderUtil.newCheckedInstanceOf(factoryClassName, AsyncWaitStrategyFactory.class); LOGGER.info("Using configured AsyncWaitStrategy factory {}.", factoryClassName); - return asyncWaitStrategyFactory; + return new DisruptorConfiguration(asyncWaitStrategyFactory, configuration); } catch (final ClassCastException e) { LOGGER.error( "Ignoring factory '{}': it is not assignable to AsyncWaitStrategyFactory", @@ -118,9 +134,10 @@ private static AsyncWaitStrategyFactory createWaitStrategyFactory(final String f e.getMessage(), e); } + } else { + LOGGER.info("Using default AsyncWaitStrategy factory."); } - LOGGER.info("Using default AsyncWaitStrategy factory."); - return null; + return new DisruptorConfiguration(null, configuration); } } } diff --git a/log4j-async-logger/src/main/java/org/apache/logging/log4j/async/logger/DefaultAsyncWaitStrategyFactory.java b/log4j-async-logger/src/main/java/org/apache/logging/log4j/async/logger/internal/DefaultAsyncWaitStrategyFactory.java similarity index 95% rename from log4j-async-logger/src/main/java/org/apache/logging/log4j/async/logger/DefaultAsyncWaitStrategyFactory.java rename to log4j-async-logger/src/main/java/org/apache/logging/log4j/async/logger/internal/DefaultAsyncWaitStrategyFactory.java index d0084a17ee3..6ee433a7a57 100644 --- a/log4j-async-logger/src/main/java/org/apache/logging/log4j/async/logger/DefaultAsyncWaitStrategyFactory.java +++ b/log4j-async-logger/src/main/java/org/apache/logging/log4j/async/logger/internal/DefaultAsyncWaitStrategyFactory.java @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.logging.log4j.async.logger; +package org.apache.logging.log4j.async.logger.internal; import com.lmax.disruptor.BlockingWaitStrategy; import com.lmax.disruptor.BusySpinWaitStrategy; @@ -23,13 +23,14 @@ import com.lmax.disruptor.YieldingWaitStrategy; import java.util.concurrent.TimeUnit; import org.apache.logging.log4j.Logger; +import org.apache.logging.log4j.async.logger.AsyncWaitStrategyFactory; import org.apache.logging.log4j.core.impl.Log4jPropertyKey; import org.apache.logging.log4j.status.StatusLogger; import org.apache.logging.log4j.util.PropertiesUtil; import org.apache.logging.log4j.util.PropertyKey; import org.apache.logging.log4j.util.Strings; -class DefaultAsyncWaitStrategyFactory implements AsyncWaitStrategyFactory { +public class DefaultAsyncWaitStrategyFactory implements AsyncWaitStrategyFactory { static final String DEFAULT_WAIT_STRATEGY_CLASSNAME = TimeoutBlockingWaitStrategy.class.getName(); private static final Logger LOGGER = StatusLogger.getLogger(); private final PropertyKey propertyKey; diff --git a/log4j-async-logger/src/main/java/org/apache/logging/log4j/async/logger/internal/DefaultBundle.java b/log4j-async-logger/src/main/java/org/apache/logging/log4j/async/logger/internal/DefaultBundle.java new file mode 100644 index 00000000000..7fe21f07d81 --- /dev/null +++ b/log4j-async-logger/src/main/java/org/apache/logging/log4j/async/logger/internal/DefaultBundle.java @@ -0,0 +1,67 @@ +/* + * 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.async.logger.internal; + +import com.lmax.disruptor.WaitStrategy; +import org.apache.logging.log4j.async.logger.AsyncLoggerConfigDisruptor; +import org.apache.logging.log4j.async.logger.AsyncLoggerDisruptor; +import org.apache.logging.log4j.async.logger.DisruptorConfiguration; +import org.apache.logging.log4j.core.config.Configuration; +import org.apache.logging.log4j.core.impl.Log4jPropertyKey; +import org.apache.logging.log4j.core.impl.LogEventFactory; +import org.apache.logging.log4j.plugins.Factory; +import org.apache.logging.log4j.plugins.Named; +import org.apache.logging.log4j.plugins.SingletonFactory; +import org.apache.logging.log4j.plugins.condition.ConditionalOnMissingBinding; +import org.apache.logging.log4j.plugins.condition.ConditionalOnPresentBindings; + +/** + * Provides default services for the per-context instance factory. + */ +public class DefaultBundle { + + @Factory + @Named("AsyncLogger") + @ConditionalOnMissingBinding + public WaitStrategy asyncLoggerWaitStrategy() { + return DisruptorUtil.createWaitStrategy(Log4jPropertyKey.ASYNC_LOGGER_WAIT_STRATEGY, null); + } + + @SingletonFactory + @ConditionalOnMissingBinding + public AsyncLoggerDisruptor.Factory asyncLoggerDisruptorFactory( + final @Named("AsyncLogger") WaitStrategy waitStrategy) { + return contextName -> new AsyncLoggerDisruptor(contextName, waitStrategy); + } + + @Factory + @Named("AsyncLoggerConfig") + @ConditionalOnPresentBindings(bindings = Configuration.class) + public WaitStrategy defaultAsyncLoggerWaitStrategy(final Configuration configuration) { + final DisruptorConfiguration disruptorConfiguration = configuration.getExtension(DisruptorConfiguration.class); + return DisruptorUtil.createWaitStrategy( + Log4jPropertyKey.ASYNC_CONFIG_WAIT_STRATEGY, + disruptorConfiguration != null ? disruptorConfiguration.getWaitStrategyFactory() : null); + } + + @Factory + @ConditionalOnPresentBindings(bindings = Configuration.class) + public AsyncLoggerConfigDisruptor asyncLoggerConfigDisruptor( + final @Named("AsyncLoggerConfig") WaitStrategy waitStrategy, final LogEventFactory logEventFactory) { + return new AsyncLoggerConfigDisruptor(waitStrategy, logEventFactory); + } +} diff --git a/log4j-async-logger/src/main/java/org/apache/logging/log4j/async/logger/DisruptorUtil.java b/log4j-async-logger/src/main/java/org/apache/logging/log4j/async/logger/internal/DisruptorUtil.java similarity index 75% rename from log4j-async-logger/src/main/java/org/apache/logging/log4j/async/logger/DisruptorUtil.java rename to log4j-async-logger/src/main/java/org/apache/logging/log4j/async/logger/internal/DisruptorUtil.java index 9716276ed65..dc639223838 100644 --- a/log4j-async-logger/src/main/java/org/apache/logging/log4j/async/logger/DisruptorUtil.java +++ b/log4j-async-logger/src/main/java/org/apache/logging/log4j/async/logger/internal/DisruptorUtil.java @@ -14,16 +14,19 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.logging.log4j.async.logger; +package org.apache.logging.log4j.async.logger.internal; import static org.apache.logging.log4j.core.impl.Log4jPropertyKey.ASYNC_CONFIG_EXCEPTION_HANDLER_CLASS_NAME; import static org.apache.logging.log4j.core.impl.Log4jPropertyKey.ASYNC_LOGGER_EXCEPTION_HANDLER_CLASS_NAME; import com.lmax.disruptor.ExceptionHandler; import com.lmax.disruptor.WaitStrategy; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Future; import org.apache.logging.log4j.Logger; +import org.apache.logging.log4j.async.logger.AsyncLoggerConfigDefaultExceptionHandler; +import org.apache.logging.log4j.async.logger.AsyncLoggerConfigDisruptor; +import org.apache.logging.log4j.async.logger.AsyncLoggerDefaultExceptionHandler; +import org.apache.logging.log4j.async.logger.AsyncWaitStrategyFactory; +import org.apache.logging.log4j.async.logger.RingBufferLogEvent; import org.apache.logging.log4j.core.impl.Log4jPropertyKey; import org.apache.logging.log4j.core.util.Integers; import org.apache.logging.log4j.status.StatusLogger; @@ -34,7 +37,7 @@ /** * Utility methods for getting Disruptor related configuration. */ -final class DisruptorUtil { +public final class DisruptorUtil { private static final Logger LOGGER = StatusLogger.getLogger(); private static final int RINGBUFFER_MIN_SIZE = 128; private static final int RINGBUFFER_DEFAULT_SIZE = 4 * 1024; @@ -45,15 +48,15 @@ final class DisruptorUtil { * especially when the number of application threads vastly outnumbered the number of cores. * CPU utilization is significantly reduced by restricting access to the enqueue operation. */ - static final boolean ASYNC_LOGGER_SYNCHRONIZE_ENQUEUE_WHEN_QUEUE_FULL = PropertiesUtil.getProperties() + public static final boolean ASYNC_LOGGER_SYNCHRONIZE_ENQUEUE_WHEN_QUEUE_FULL = PropertiesUtil.getProperties() .getBooleanProperty(Log4jPropertyKey.ASYNC_LOGGER_SYNCHRONIZE_ENQUEUE_WHEN_QUEUE_FULL, true); - static final boolean ASYNC_CONFIG_SYNCHRONIZE_ENQUEUE_WHEN_QUEUE_FULL = PropertiesUtil.getProperties() + public static final boolean ASYNC_CONFIG_SYNCHRONIZE_ENQUEUE_WHEN_QUEUE_FULL = PropertiesUtil.getProperties() .getBooleanProperty(Log4jPropertyKey.ASYNC_CONFIG_SYNCHRONIZE_ENQUEUE_WHEN_QUEUE_FULL, true); private DisruptorUtil() {} - static WaitStrategy createWaitStrategy( + public static WaitStrategy createWaitStrategy( final PropertyKey key, final AsyncWaitStrategyFactory asyncWaitStrategyFactory) { if (asyncWaitStrategyFactory == null) { @@ -66,7 +69,7 @@ static WaitStrategy createWaitStrategy( return asyncWaitStrategyFactory.createWaitStrategy(); } - static int calculateRingBufferSize(final PropertyKey key) { + public static int calculateRingBufferSize(final PropertyKey key) { int ringBufferSize = RINGBUFFER_DEFAULT_SIZE; final String userPreferredRBSize = PropertiesUtil.getProperties().getStringProperty(key, String.valueOf(ringBufferSize)); @@ -84,7 +87,7 @@ static int calculateRingBufferSize(final PropertyKey key) { return Integers.ceilingNextPowerOfTwo(ringBufferSize); } - static ExceptionHandler getAsyncLoggerExceptionHandler() { + public static ExceptionHandler getAsyncLoggerExceptionHandler() { try { return LoaderUtil.newCheckedInstanceOfProperty( ASYNC_LOGGER_EXCEPTION_HANDLER_CLASS_NAME, @@ -96,7 +99,8 @@ static ExceptionHandler getAsyncLoggerExceptionHandler() { } } - static ExceptionHandler getAsyncLoggerConfigExceptionHandler() { + public static ExceptionHandler + getAsyncLoggerConfigExceptionHandler() { try { return LoaderUtil.newCheckedInstanceOfProperty( ASYNC_CONFIG_EXCEPTION_HANDLER_CLASS_NAME, @@ -107,22 +111,4 @@ static ExceptionHandler getAsyncLo return new AsyncLoggerConfigDefaultExceptionHandler(); } } - - /** - * Returns the thread ID of the background appender thread. This allows us to detect Logger.log() calls initiated - * from the appender thread, which may cause deadlock when the RingBuffer is full. (LOG4J2-471) - * - * @param executor runs the appender thread - * @return the thread ID of the background appender thread - */ - public static long getExecutorThreadId(final ExecutorService executor) { - final Future result = executor.submit(() -> Thread.currentThread().getId()); - try { - return result.get(); - } catch (final Exception ex) { - final String msg = - "Could not obtain executor thread Id. " + "Giving up to avoid the risk of application deadlock."; - throw new IllegalStateException(msg, ex); - } - } } diff --git a/log4j-async-logger/src/main/java/org/apache/logging/log4j/async/logger/internal/InstanceFactoryPostProcessor.java b/log4j-async-logger/src/main/java/org/apache/logging/log4j/async/logger/internal/InstanceFactoryPostProcessor.java new file mode 100644 index 00000000000..18bb9dddbbd --- /dev/null +++ b/log4j-async-logger/src/main/java/org/apache/logging/log4j/async/logger/internal/InstanceFactoryPostProcessor.java @@ -0,0 +1,32 @@ +/* + * 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.async.logger.internal; + +import aQute.bnd.annotation.Resolution; +import aQute.bnd.annotation.spi.ServiceProvider; +import org.apache.logging.log4j.plugins.Ordered; +import org.apache.logging.log4j.plugins.di.ConfigurableInstanceFactory; +import org.apache.logging.log4j.plugins.di.spi.ConfigurableInstanceFactoryPostProcessor; + +@Ordered(Ordered.LAST - 2000) +@ServiceProvider(value = ConfigurableInstanceFactoryPostProcessor.class, resolution = Resolution.OPTIONAL) +public class InstanceFactoryPostProcessor implements ConfigurableInstanceFactoryPostProcessor { + @Override + public void postProcessFactory(final ConfigurableInstanceFactory factory) { + factory.registerBundle(new DefaultBundle()); + } +} diff --git a/log4j-async-logger/src/main/java/org/apache/logging/log4j/async/logger/TimeoutBlockingWaitStrategy.java b/log4j-async-logger/src/main/java/org/apache/logging/log4j/async/logger/internal/TimeoutBlockingWaitStrategy.java similarity index 97% rename from log4j-async-logger/src/main/java/org/apache/logging/log4j/async/logger/TimeoutBlockingWaitStrategy.java rename to log4j-async-logger/src/main/java/org/apache/logging/log4j/async/logger/internal/TimeoutBlockingWaitStrategy.java index 657da6fec47..faacfdd6aa8 100644 --- a/log4j-async-logger/src/main/java/org/apache/logging/log4j/async/logger/TimeoutBlockingWaitStrategy.java +++ b/log4j-async-logger/src/main/java/org/apache/logging/log4j/async/logger/internal/TimeoutBlockingWaitStrategy.java @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.logging.log4j.async.logger; +package org.apache.logging.log4j.async.logger.internal; import com.lmax.disruptor.AlertException; import com.lmax.disruptor.BatchEventProcessor; @@ -46,7 +46,7 @@ // // Log4j 3.0.0 NOTE: // Implementation was updated to use Lock/Condition API for https://github.com/apache/logging-log4j2/issues/1532 -class TimeoutBlockingWaitStrategy implements WaitStrategy { +public class TimeoutBlockingWaitStrategy implements WaitStrategy { private final Lock mutex = new ReentrantLock(); private final Condition condition = mutex.newCondition(); private final long timeoutInNanos; diff --git a/log4j-async-logger/src/test/java/org/apache/logging/log4j/async/logger/AsyncLoggerClassLoadDeadlockTest.java b/log4j-async-logger/src/test/java/org/apache/logging/log4j/async/logger/AsyncLoggerClassLoadDeadlockTest.java index b4ff970790c..eb694e6a972 100644 --- a/log4j-async-logger/src/test/java/org/apache/logging/log4j/async/logger/AsyncLoggerClassLoadDeadlockTest.java +++ b/log4j-async-logger/src/test/java/org/apache/logging/log4j/async/logger/AsyncLoggerClassLoadDeadlockTest.java @@ -31,7 +31,7 @@ @Tag("async") @SetTestProperty( key = "LoggerContext.selector", - value = "org.apache.logging.log4j.core.async.AsyncLoggerContextSelector") + value = "org.apache.logging.log4j.async.logger.AsyncLoggerContextSelector") @SetTestProperty(key = "AsyncLogger.ringBufferSize", value = "128") @SetTestProperty( key = "Configuration.file", diff --git a/log4j-async-logger/src/test/java/org/apache/logging/log4j/async/logger/AsyncLoggerConfigAutoFlushTest.java b/log4j-async-logger/src/test/java/org/apache/logging/log4j/async/logger/AsyncLoggerConfigAutoFlushTest.java index d59738208a2..9e5ec1189fd 100644 --- a/log4j-async-logger/src/test/java/org/apache/logging/log4j/async/logger/AsyncLoggerConfigAutoFlushTest.java +++ b/log4j-async-logger/src/test/java/org/apache/logging/log4j/async/logger/AsyncLoggerConfigAutoFlushTest.java @@ -16,11 +16,12 @@ */ package org.apache.logging.log4j.async.logger; -import static org.junit.jupiter.api.Assertions.assertTrue; +import static java.nio.charset.StandardCharsets.UTF_8; +import static org.assertj.core.api.Assertions.assertThat; -import java.io.File; import java.nio.file.Files; import java.nio.file.Path; +import java.util.List; import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.core.LoggerContext; import org.apache.logging.log4j.core.test.junit.LoggerContextSource; @@ -37,14 +38,14 @@ public class AsyncLoggerConfigAutoFlushTest { @Test @LoggerContextSource public void testFlushAtEndOfBatch(final LoggerContext ctx) throws Exception { - final File file = - loggingPath.resolve("AsyncLoggerConfigAutoFlushTest.log").toFile(); + final Path file = loggingPath.resolve("AsyncLoggerConfigAutoFlushTest.log"); final Logger log = ctx.getLogger("com.foo.Bar"); final String msg = "Message flushed with immediate flush=false"; log.info(msg); ctx.stop(); // stop async thread - final String contents = Files.readString(file.toPath()); - assertTrue(contents.contains(msg), "line1 correct"); + final List contents = Files.readAllLines(file, UTF_8); + assertThat(contents).hasSize(1); + assertThat(contents.get(0)).contains(msg); } } diff --git a/log4j-async-logger/src/test/java/org/apache/logging/log4j/async/logger/AsyncLoggerConfigTest.java b/log4j-async-logger/src/test/java/org/apache/logging/log4j/async/logger/AsyncLoggerConfigTest.java index 6a08fdaf867..5a63ba1e40f 100644 --- a/log4j-async-logger/src/test/java/org/apache/logging/log4j/async/logger/AsyncLoggerConfigTest.java +++ b/log4j-async-logger/src/test/java/org/apache/logging/log4j/async/logger/AsyncLoggerConfigTest.java @@ -107,8 +107,7 @@ public void testSingleFilterInvocation() { when(appender.getName()).thenReturn("test"); config.addAppender(appender, null, null); final DisruptorConfiguration disruptorConfig = configuration.getExtension(DisruptorConfiguration.class); - final AsyncLoggerConfigDisruptor disruptor = - (AsyncLoggerConfigDisruptor) disruptorConfig.getAsyncLoggerConfigDelegate(); + final AsyncLoggerConfigDisruptor disruptor = disruptorConfig.getLoggerConfigDisruptor(); disruptor.start(); try { config.log(FQCN, FQCN, null, Level.INFO, new SimpleMessage(), null); diff --git a/log4j-async-logger/src/test/java/org/apache/logging/log4j/async/logger/AsyncLoggerContextTest.java b/log4j-async-logger/src/test/java/org/apache/logging/log4j/async/logger/AsyncLoggerContextTest.java index bba0cbd178c..996621b3d41 100644 --- a/log4j-async-logger/src/test/java/org/apache/logging/log4j/async/logger/AsyncLoggerContextTest.java +++ b/log4j-async-logger/src/test/java/org/apache/logging/log4j/async/logger/AsyncLoggerContextTest.java @@ -19,11 +19,9 @@ import static org.junit.Assert.assertTrue; import org.apache.logging.log4j.Logger; -import org.apache.logging.log4j.core.LoggerContext; import org.apache.logging.log4j.core.test.CoreLoggerContexts; import org.apache.logging.log4j.core.test.categories.AsyncLoggers; -import org.apache.logging.log4j.internal.recycler.DummyRecyclerFactoryProvider; -import org.apache.logging.log4j.status.StatusLogger; +import org.apache.logging.log4j.plugins.di.DI; import org.junit.Test; import org.junit.experimental.categories.Category; @@ -32,14 +30,7 @@ public class AsyncLoggerContextTest { @Test public void testNewInstanceReturnsAsyncLogger() { - final Logger logger = new AsyncLoggerContext("a") - .newInstance( - new LoggerContext("a"), - "a", - null, - null, - new DummyRecyclerFactoryProvider().createForEnvironment(null), - StatusLogger.getLogger()); + final Logger logger = new AsyncLoggerContext("a", null, null, DI.createInitializedFactory()).getLogger("a"); assertTrue(logger instanceof AsyncLogger); CoreLoggerContexts.stopLoggerContext(); // stop async thread diff --git a/log4j-async-logger/src/test/java/org/apache/logging/log4j/async/logger/AsyncLoggerCustomSelectorLocationTest.java b/log4j-async-logger/src/test/java/org/apache/logging/log4j/async/logger/AsyncLoggerCustomSelectorLocationTest.java index f84dd735abe..c81b3d3f55d 100644 --- a/log4j-async-logger/src/test/java/org/apache/logging/log4j/async/logger/AsyncLoggerCustomSelectorLocationTest.java +++ b/log4j-async-logger/src/test/java/org/apache/logging/log4j/async/logger/AsyncLoggerCustomSelectorLocationTest.java @@ -34,6 +34,7 @@ import org.apache.logging.log4j.core.test.junit.ContextSelectorType; import org.apache.logging.log4j.core.test.junit.LoggerContextSource; import org.apache.logging.log4j.plugins.Singleton; +import org.apache.logging.log4j.plugins.di.DI; import org.apache.logging.log4j.test.junit.TempLoggingDir; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; @@ -74,7 +75,8 @@ public void testCustomAsyncSelectorLocation(final LoggerContext ctx) throws Exce @Singleton public static final class CustomAsyncContextSelector implements ContextSelector { - private static final LoggerContext CONTEXT = new AsyncLoggerContext("AsyncDefault"); + private static final LoggerContext CONTEXT = + new AsyncLoggerContext("AsyncDefault", null, null, DI.createInitializedFactory()); @Override public LoggerContext getContext(final String fqcn, final ClassLoader loader, final boolean currentContext) { diff --git a/log4j-async-logger/src/test/java/org/apache/logging/log4j/async/logger/AsyncThreadContextTest.java b/log4j-async-logger/src/test/java/org/apache/logging/log4j/async/logger/AsyncThreadContextTest.java index c8d57007bc1..8ce0ec2fffb 100644 --- a/log4j-async-logger/src/test/java/org/apache/logging/log4j/async/logger/AsyncThreadContextTest.java +++ b/log4j-async-logger/src/test/java/org/apache/logging/log4j/async/logger/AsyncThreadContextTest.java @@ -37,6 +37,7 @@ import org.apache.logging.log4j.core.selector.ContextSelector; import org.apache.logging.log4j.core.test.CoreLoggerContexts; import org.apache.logging.log4j.plugins.di.DI; +import org.apache.logging.log4j.spi.LoggingSystem; import org.apache.logging.log4j.spi.LoggingSystemProperty; import org.apache.logging.log4j.spi.ReadOnlyThreadContextMap; import org.apache.logging.log4j.test.TestProperties; @@ -97,6 +98,7 @@ void init() { System.setProperty( LoggingSystemProperty.Constant.THREAD_CONTEXT_MAP_CLASS, PACKAGE + implClassSimpleName()); PropertiesUtil.getProperties().reload(); + LoggingSystem.reset(); ThreadContextTestAccess.init(); } @@ -169,7 +171,7 @@ private static void runTest( ((AsyncLoggerContext) context).getAsyncLoggerDisruptor().getRingBuffer()::remainingCapacity; } else { remainingCapacity = - ((AsyncLoggerConfigDisruptor) ((AsyncLoggerConfig) log.get()).getAsyncLoggerConfigDelegate()) + ((AsyncLoggerConfigDisruptor) ((AsyncLoggerConfig) log.get()).getAsyncLoggerConfigDisruptor()) .getRingBuffer()::remainingCapacity; } diff --git a/log4j-async-logger/src/test/java/org/apache/logging/log4j/async/logger/DefaultIncludeLocationTest.java b/log4j-async-logger/src/test/java/org/apache/logging/log4j/async/logger/DefaultIncludeLocationTest.java index 9f92ff02c6b..6911b7a224a 100644 --- a/log4j-async-logger/src/test/java/org/apache/logging/log4j/async/logger/DefaultIncludeLocationTest.java +++ b/log4j-async-logger/src/test/java/org/apache/logging/log4j/async/logger/DefaultIncludeLocationTest.java @@ -16,12 +16,13 @@ */ package org.apache.logging.log4j.async.logger; +import java.net.URI; import java.util.stream.Stream; import org.apache.logging.log4j.core.LoggerContext; import org.apache.logging.log4j.core.config.Configuration; import org.apache.logging.log4j.core.config.LoggerConfig; import org.apache.logging.log4j.core.config.NullConfiguration; -import org.apache.logging.log4j.util.PropertiesUtil; +import org.apache.logging.log4j.plugins.di.DI; import org.assertj.core.api.Assertions; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; @@ -58,8 +59,7 @@ private static Stream loggerConfigs(final Configuration config) { } static Stream loggerContextDefaultLocation() { - final LoggerContext ctx = new LoggerContext("sync"); - ctx.setProperties(PropertiesUtil.getProperties()); + final LoggerContext ctx = new LoggerContext("sync", null, (URI) null, DI.createInitializedFactory()); final NullConfiguration config = new NullConfiguration(ctx); ctx.setConfiguration(config); return loggerConfigs(config); @@ -74,8 +74,7 @@ void loggerContextDefaultLocation(final LoggerConfig loggerConfig, final boolean } static Stream asyncLoggerContextDefaultLocation() { - final AsyncLoggerContext ctx = new AsyncLoggerContext("async"); - ctx.setProperties(PropertiesUtil.getProperties()); + final AsyncLoggerContext ctx = new AsyncLoggerContext("async", null, null, DI.createInitializedFactory()); final NullConfiguration config = new NullConfiguration(ctx); ctx.setConfiguration(config); return loggerConfigs(config).map(args -> (LoggerConfig) args.get()[0]); diff --git a/log4j-async-logger/src/test/java/org/apache/logging/log4j/async/logger/DisruptorConfigurationInvalidTest.java b/log4j-async-logger/src/test/java/org/apache/logging/log4j/async/logger/DisruptorConfigurationInvalidTest.java index c7742987d5e..a4473efdf58 100644 --- a/log4j-async-logger/src/test/java/org/apache/logging/log4j/async/logger/DisruptorConfigurationInvalidTest.java +++ b/log4j-async-logger/src/test/java/org/apache/logging/log4j/async/logger/DisruptorConfigurationInvalidTest.java @@ -19,6 +19,7 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertEquals; +import org.apache.logging.log4j.async.logger.internal.TimeoutBlockingWaitStrategy; import org.apache.logging.log4j.core.LoggerContext; import org.apache.logging.log4j.core.test.junit.ContextSelectorType; import org.apache.logging.log4j.core.test.junit.LoggerContextSource; diff --git a/log4j-async-logger/src/test/java/org/apache/logging/log4j/async/logger/DisruptorConfigurationTest.java b/log4j-async-logger/src/test/java/org/apache/logging/log4j/async/logger/DisruptorConfigurationTest.java index 190dbee16bc..f3424774230 100644 --- a/log4j-async-logger/src/test/java/org/apache/logging/log4j/async/logger/DisruptorConfigurationTest.java +++ b/log4j-async-logger/src/test/java/org/apache/logging/log4j/async/logger/DisruptorConfigurationTest.java @@ -20,6 +20,8 @@ import com.lmax.disruptor.WaitStrategy; import com.lmax.disruptor.YieldingWaitStrategy; +import org.apache.logging.log4j.async.logger.internal.DefaultAsyncWaitStrategyFactory; +import org.apache.logging.log4j.async.logger.internal.TimeoutBlockingWaitStrategy; import org.apache.logging.log4j.core.LoggerContext; import org.apache.logging.log4j.core.test.appender.ListAppender; import org.apache.logging.log4j.core.test.junit.LoggerContextSource; @@ -57,8 +59,7 @@ public void testWaitStrategy(final LoggerContext context) throws Exception { final AsyncLoggerConfig loggerConfig = (AsyncLoggerConfig) ((org.apache.logging.log4j.core.Logger) logger).get(); - final AsyncLoggerConfigDisruptor delegate = - (AsyncLoggerConfigDisruptor) loggerConfig.getAsyncLoggerConfigDelegate(); + final AsyncLoggerConfigDisruptor delegate = loggerConfig.getAsyncLoggerConfigDisruptor(); assertThat(delegate.getWaitStrategy().getClass()).isEqualTo(YieldingWaitStrategy.class); assertThat(delegate.getWaitStrategy()).isInstanceOf(com.lmax.disruptor.YieldingWaitStrategy.class); } @@ -81,7 +82,7 @@ public void testIncorrectWaitStrategyFallsBackToDefault( final AsyncLoggerConfig loggerConfig = (AsyncLoggerConfig) ((org.apache.logging.log4j.core.Logger) logger).get(); final AsyncLoggerConfigDisruptor delegate = - (AsyncLoggerConfigDisruptor) loggerConfig.getAsyncLoggerConfigDelegate(); + (AsyncLoggerConfigDisruptor) loggerConfig.getAsyncLoggerConfigDisruptor(); assertThat(delegate.getWaitStrategy().getClass()).isEqualTo(TimeoutBlockingWaitStrategy.class); assertThat(delegate.getWaitStrategy()).isInstanceOf(TimeoutBlockingWaitStrategy.class); } diff --git a/log4j-async-logger/src/test/java/org/apache/logging/log4j/async/logger/QueueFullAsyncAbstractTest.java b/log4j-async-logger/src/test/java/org/apache/logging/log4j/async/logger/QueueFullAsyncAbstractTest.java index 1e5e4bf3bc0..501c8b68d98 100644 --- a/log4j-async-logger/src/test/java/org/apache/logging/log4j/async/logger/QueueFullAsyncAbstractTest.java +++ b/log4j-async-logger/src/test/java/org/apache/logging/log4j/async/logger/QueueFullAsyncAbstractTest.java @@ -57,23 +57,21 @@ protected static void assertAsyncLoggerConfig(final LoggerContext ctx, final int assertThat(config).isNotNull(); assertThat(config.getRootLogger()).isInstanceOf(AsyncLoggerConfig.class); final DisruptorConfiguration disruptorConfig = config.getExtension(DisruptorConfiguration.class); - final AsyncLoggerConfigDisruptor disruptor = - (AsyncLoggerConfigDisruptor) disruptorConfig.getAsyncLoggerConfigDelegate(); + final AsyncLoggerConfigDisruptor disruptor = disruptorConfig.getLoggerConfigDisruptor(); assertThat(disruptor.getRingBuffer().getBufferSize()).isEqualTo(expectedBufferSize); } @Override protected long asyncRemainingCapacity(final Logger logger) { if (logger instanceof final AsyncLogger asyncLogger) { - return Optional.ofNullable(asyncLogger.getAsyncLoggerDisruptor()) + return Optional.of(asyncLogger.getAsyncLoggerDisruptor()) .map(AsyncLoggerDisruptor::getRingBuffer) .map(RingBuffer::remainingCapacity) .orElse(0L); } else { final LoggerConfig loggerConfig = ((org.apache.logging.log4j.core.Logger) logger).get(); if (loggerConfig instanceof final AsyncLoggerConfig asyncLoggerConfig) { - return Optional.ofNullable( - (AsyncLoggerConfigDisruptor) asyncLoggerConfig.getAsyncLoggerConfigDelegate()) + return Optional.ofNullable(asyncLoggerConfig.getAsyncLoggerConfigDisruptor()) .map(AsyncLoggerConfigDisruptor::getRingBuffer) .map(RingBuffer::remainingCapacity) .orElse(0L); diff --git a/log4j-config-properties/src/main/java/org/apache/logging/log4j/config/properties/JavaPropsConfiguration.java b/log4j-config-properties/src/main/java/org/apache/logging/log4j/config/properties/JavaPropsConfiguration.java index 03224bbec72..92d16ff6901 100644 --- a/log4j-config-properties/src/main/java/org/apache/logging/log4j/config/properties/JavaPropsConfiguration.java +++ b/log4j-config-properties/src/main/java/org/apache/logging/log4j/config/properties/JavaPropsConfiguration.java @@ -29,12 +29,13 @@ */ public class JavaPropsConfiguration extends AbstractJacksonConfiguration { - public JavaPropsConfiguration(final LoggerContext loggerContext, final ConfigurationSource configSource) { - super(loggerContext, configSource); + public JavaPropsConfiguration(final LoggerContext loggerContext, final ConfigurationSource configurationSource) { + super(loggerContext, configurationSource); } @Override - protected Configuration createConfiguration(LoggerContext loggerContext, ConfigurationSource configurationSource) { + protected Configuration createConfiguration( + final LoggerContext loggerContext, final ConfigurationSource configurationSource) { return new JavaPropsConfiguration(loggerContext, configurationSource); } diff --git a/log4j-config-properties/src/main/java/org/apache/logging/log4j/config/properties/JavaPropsConfigurationFactory.java b/log4j-config-properties/src/main/java/org/apache/logging/log4j/config/properties/JavaPropsConfigurationFactory.java index 5230d140008..b585cdc6e5c 100644 --- a/log4j-config-properties/src/main/java/org/apache/logging/log4j/config/properties/JavaPropsConfigurationFactory.java +++ b/log4j-config-properties/src/main/java/org/apache/logging/log4j/config/properties/JavaPropsConfigurationFactory.java @@ -40,7 +40,7 @@ public Configuration getConfiguration(final LoggerContext loggerContext, final C } @Override - public String[] getSupportedTypes() { + protected String[] getSupportedTypes() { return SUFFIXES; } } diff --git a/log4j-config-yaml/src/main/java/org/apache/logging/log4j/config/yaml/YamlConfiguration.java b/log4j-config-yaml/src/main/java/org/apache/logging/log4j/config/yaml/YamlConfiguration.java index 9cc255f5e6e..0deff0b9dd1 100644 --- a/log4j-config-yaml/src/main/java/org/apache/logging/log4j/config/yaml/YamlConfiguration.java +++ b/log4j-config-yaml/src/main/java/org/apache/logging/log4j/config/yaml/YamlConfiguration.java @@ -39,6 +39,7 @@ protected Configuration createConfiguration( return new YamlConfiguration(loggerContext, configurationSource); } + @Override protected ObjectMapper getObjectMapper() { return YAMLMapper.builder() .configure(JsonParser.Feature.ALLOW_COMMENTS, true) diff --git a/log4j-config-yaml/src/main/java/org/apache/logging/log4j/config/yaml/YamlConfigurationFactory.java b/log4j-config-yaml/src/main/java/org/apache/logging/log4j/config/yaml/YamlConfigurationFactory.java index fc6671ba248..ae6c4d95035 100644 --- a/log4j-config-yaml/src/main/java/org/apache/logging/log4j/config/yaml/YamlConfigurationFactory.java +++ b/log4j-config-yaml/src/main/java/org/apache/logging/log4j/config/yaml/YamlConfigurationFactory.java @@ -40,7 +40,7 @@ public Configuration getConfiguration(final LoggerContext loggerContext, final C } @Override - public String[] getSupportedTypes() { + protected String[] getSupportedTypes() { return SUFFIXES; } } diff --git a/log4j-core-test/src/main/java/org/apache/logging/log4j/core/test/BasicConfigurationFactory.java b/log4j-core-test/src/main/java/org/apache/logging/log4j/core/test/BasicConfigurationFactory.java index 30e06d28ce4..643b77741dd 100644 --- a/log4j-core-test/src/main/java/org/apache/logging/log4j/core/test/BasicConfigurationFactory.java +++ b/log4j-core-test/src/main/java/org/apache/logging/log4j/core/test/BasicConfigurationFactory.java @@ -24,6 +24,9 @@ import org.apache.logging.log4j.core.config.ConfigurationFactory; import org.apache.logging.log4j.core.config.ConfigurationSource; import org.apache.logging.log4j.core.config.LoggerConfig; +import org.apache.logging.log4j.plugins.di.ConfigurableInstanceFactory; +import org.apache.logging.log4j.plugins.di.DI; +import org.apache.logging.log4j.util.PropertiesUtil; /** * @@ -33,11 +36,11 @@ public class BasicConfigurationFactory extends ConfigurationFactory { @Override public Configuration getConfiguration( final LoggerContext loggerContext, final String name, final URI configLocation) { - return new BasicConfiguration(); + return new BasicConfiguration(loggerContext); } @Override - public String[] getSupportedTypes() { + protected String[] getSupportedTypes() { return null; } @@ -51,7 +54,17 @@ public static class BasicConfiguration extends AbstractConfiguration { private static final String DEFAULT_LEVEL = "org.apache.logging.log4j.level"; public BasicConfiguration() { - super(null, ConfigurationSource.NULL_SOURCE); + this(null); + } + + public BasicConfiguration(final LoggerContext loggerContext) { + super( + loggerContext, + ConfigurationSource.NULL_SOURCE, + loggerContext != null ? loggerContext.getEnvironment() : PropertiesUtil.getProperties(), + loggerContext != null + ? (ConfigurableInstanceFactory) loggerContext.getInstanceFactory() + : DI.createInitializedFactory()); final LoggerConfig root = getRootLogger(); final String name = System.getProperty(DEFAULT_LEVEL); diff --git a/log4j-core-test/src/main/java/org/apache/logging/log4j/core/test/config/AbstractNestedLoggerConfigTest.java b/log4j-core-test/src/main/java/org/apache/logging/log4j/core/test/config/AbstractNestedLoggerConfigTest.java index 4c0a3cbb5f5..2f0ec5d34a8 100644 --- a/log4j-core-test/src/main/java/org/apache/logging/log4j/core/test/config/AbstractNestedLoggerConfigTest.java +++ b/log4j-core-test/src/main/java/org/apache/logging/log4j/core/test/config/AbstractNestedLoggerConfigTest.java @@ -20,11 +20,13 @@ import java.io.IOException; import java.io.InputStream; +import java.net.URI; import org.apache.logging.log4j.Level; import org.apache.logging.log4j.core.LoggerContext; import org.apache.logging.log4j.core.config.Configuration; import org.apache.logging.log4j.core.config.ConfigurationSource; import org.apache.logging.log4j.core.config.xml.XmlConfiguration; +import org.apache.logging.log4j.plugins.di.DI; import org.junit.jupiter.api.Test; public abstract class AbstractNestedLoggerConfigTest { @@ -51,8 +53,9 @@ public void testInheritParentLevel() throws IOException { private Configuration loadConfiguration(final String resourcePath) throws IOException { try (final InputStream in = getClass().getResourceAsStream(getClass().getSimpleName() + resourcePath)) { - final Configuration configuration = - new XmlConfiguration(new LoggerContext("test"), new ConfigurationSource(in)); + final Configuration configuration = new XmlConfiguration( + new LoggerContext("test", null, (URI) null, DI.createInitializedFactory()), + new ConfigurationSource(in)); configuration.initialize(); configuration.start(); return configuration; diff --git a/log4j-core-test/src/main/java/org/apache/logging/log4j/core/test/junit/ConfigurationFactoryType.java b/log4j-core-test/src/main/java/org/apache/logging/log4j/core/test/junit/ConfigurationFactoryType.java index 01f67048266..be40bc61b0e 100644 --- a/log4j-core-test/src/main/java/org/apache/logging/log4j/core/test/junit/ConfigurationFactoryType.java +++ b/log4j-core-test/src/main/java/org/apache/logging/log4j/core/test/junit/ConfigurationFactoryType.java @@ -22,6 +22,7 @@ import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import org.apache.logging.log4j.core.config.ConfigurationFactory; +import org.apache.logging.log4j.core.config.URIConfigurationFactory; /** * Specifies a particular {@link ConfigurationFactory} class to use for a test class or method instead of the default. @@ -31,5 +32,5 @@ @Inherited @Log4jTest public @interface ConfigurationFactoryType { - Class value(); + Class value(); } diff --git a/log4j-core-test/src/main/java/org/apache/logging/log4j/core/test/junit/Log4jExtension.java b/log4j-core-test/src/main/java/org/apache/logging/log4j/core/test/junit/Log4jExtension.java index 1cb0c2d93b7..4827d843bb9 100644 --- a/log4j-core-test/src/main/java/org/apache/logging/log4j/core/test/junit/Log4jExtension.java +++ b/log4j-core-test/src/main/java/org/apache/logging/log4j/core/test/junit/Log4jExtension.java @@ -25,7 +25,7 @@ import java.util.concurrent.TimeUnit; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.core.LoggerContext; -import org.apache.logging.log4j.core.config.ConfigurationFactory; +import org.apache.logging.log4j.core.config.URIConfigurationFactory; import org.apache.logging.log4j.core.impl.Log4jContextFactory; import org.apache.logging.log4j.core.impl.Log4jPropertyKey; import org.apache.logging.log4j.core.selector.ContextSelector; @@ -33,6 +33,7 @@ import org.apache.logging.log4j.plugins.di.ConfigurableInstanceFactory; import org.apache.logging.log4j.plugins.di.DI; import org.apache.logging.log4j.spi.LoggerContextFactory; +import org.apache.logging.log4j.spi.Provider; import org.junit.jupiter.api.extension.AfterEachCallback; import org.junit.jupiter.api.extension.BeforeAllCallback; import org.junit.jupiter.api.extension.BeforeEachCallback; @@ -104,14 +105,15 @@ private static void configure( final AnnotatedElement element, final Class testClass) { final ConfigurableInstanceFactory instanceFactory = configureInstanceFactory(builder, element); - final Log4jContextFactory factory = instanceFactory.getInstance(Log4jContextFactory.class); + final Provider provider = instanceFactory.getInstance(Provider.class); + final Log4jContextFactory factory = (Log4jContextFactory) provider.getLoggerContextFactory(); store.put(LoggerContextFactoryHolder.class, new LoggerContextFactoryHolder(factory)); if (AnnotationSupport.isAnnotated(element, LoggingResolvers.class)) { AnnotationSupport.findAnnotation(element, LoggerContextSource.class) .map(source -> configureLoggerContextSource(source, testClass, factory)) .or(() -> AnnotationSupport.findAnnotation(element, LegacyLoggerContextSource.class) .map(source -> configureLegacyLoggerContextSource(source, testClass, factory))) - .ifPresent(provider -> store.put(LoggerContextProvider.class, provider)); + .ifPresent(p -> store.put(LoggerContextProvider.class, p)); } } @@ -125,7 +127,7 @@ private static ConfigurableInstanceFactory configureInstanceFactory( .toFunction(instanceFactory -> instanceFactory.getFactory(clazz))); AnnotationSupport.findAnnotation(element, ConfigurationFactoryType.class) .map(ConfigurationFactoryType::value) - .ifPresent(clazz -> builder.addBindingFrom(ConfigurationFactory.KEY) + .ifPresent(clazz -> builder.addBindingFrom(URIConfigurationFactory.KEY) .toFunction(instanceFactory -> instanceFactory.getFactory(clazz))); return builder.build(); } diff --git a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/LoggerContextTest.java b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/LoggerContextTest.java deleted file mode 100644 index a381f161c53..00000000000 --- a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/LoggerContextTest.java +++ /dev/null @@ -1,46 +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; - -import static org.junit.jupiter.api.Assertions.assertTrue; - -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.core.impl.Log4jContextFactory; -import org.apache.logging.log4j.core.impl.internal.InternalLoggerContext; -import org.apache.logging.log4j.core.util.DefaultShutdownCallbackRegistry; -import org.apache.logging.log4j.core.util.ShutdownCallbackRegistry; -import org.apache.logging.log4j.spi.LoggerContextFactory; -import org.junit.jupiter.api.Test; - -/** - * Validate Logging after Shutdown. - */ -public class LoggerContextTest { - - @Test - public void shutdownTest() { - LoggerContextFactory contextFactory = LogManager.getFactory(); - assertTrue(contextFactory instanceof Log4jContextFactory); - Log4jContextFactory factory = (Log4jContextFactory) contextFactory; - ShutdownCallbackRegistry registry = factory.getShutdownCallbackRegistry(); - assertTrue(registry instanceof DefaultShutdownCallbackRegistry); - ((DefaultShutdownCallbackRegistry) registry).start(); - ((DefaultShutdownCallbackRegistry) registry).stop(); - org.apache.logging.log4j.spi.LoggerContext loggerContext = LogManager.getContext(false); - assertTrue(loggerContext instanceof InternalLoggerContext); - } -} diff --git a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/LoggerTest.java b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/LoggerTest.java index 84aab071807..49f30589562 100644 --- a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/LoggerTest.java +++ b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/LoggerTest.java @@ -52,6 +52,7 @@ import org.apache.logging.log4j.message.StringFormatterMessageFactory; import org.apache.logging.log4j.message.StructuredDataMessage; import org.apache.logging.log4j.spi.LoggingSystem; +import org.awaitility.Awaitility; import org.hamcrest.MatcherAssert; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; @@ -125,7 +126,7 @@ public void builder() { final List events = app.getEvents(); assertEventCount(events, 3); assertEquals( - "org.apache.logging.log4j.core.LoggerTest.builder(LoggerTest.java:121)", + "org.apache.logging.log4j.core.LoggerTest.builder(LoggerTest.java:122)", events.get(0).getSource().toString(), "Incorrect location"); assertEquals(Level.DEBUG, events.get(0).getLevel(), "Incorrect Level"); @@ -505,13 +506,7 @@ public void testReconfiguration(final LoggerContext context) throws Exception { for (int i = 0; i < 17; ++i) { logger.debug("Reconfigure"); } - Thread.sleep(100); - for (int i = 0; i < 20; i++) { - if (context.getConfiguration() != oldConfig) { - break; - } - Thread.sleep(50); - } + Awaitility.await().atMost(10, TimeUnit.MINUTES).until(() -> context.getConfiguration() != oldConfig); final Configuration newConfig = context.getConfiguration(); assertNotNull(newConfig, "No configuration"); assertNotSame(newConfig, oldConfig, "Reconfiguration failed"); diff --git a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/appender/ReconfigureAppenderTest.java b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/appender/ReconfigureAppenderTest.java index df2e82d2651..373601a6659 100644 --- a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/appender/ReconfigureAppenderTest.java +++ b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/appender/ReconfigureAppenderTest.java @@ -134,13 +134,15 @@ private void createAndAddAppender() { config_builder.add(config_builder.newRootLogger(Level.INFO)); // Initialise the logger context. - final LoggerContext logger_context = Configurator.initialize(config_builder.build()); + final BuiltConfiguration configuration = config_builder.build(); + final LoggerContext logger_context = Configurator.initialize(configuration); // Retrieve the logger. final Logger logger = (Logger) LogManager.getLogger(this.getClass()); - final Builder pattern_builder = - PatternLayout.newBuilder().setPattern("[%d{dd-MM-yy HH:mm:ss}] %p %m %throwable %n"); + final Builder pattern_builder = PatternLayout.newBuilder() + .setConfiguration(configuration) + .setPattern("[%d{dd-MM-yy HH:mm:ss}] %p %m %throwable %n"); final PatternLayout pattern_layout = (PatternLayout) pattern_builder.build(); diff --git a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/appender/rolling/RollingAppenderCronTest.java b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/appender/rolling/RollingAppenderCronTest.java index 90d7f95bf73..7398c87c815 100644 --- a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/appender/rolling/RollingAppenderCronTest.java +++ b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/appender/rolling/RollingAppenderCronTest.java @@ -32,10 +32,12 @@ import org.apache.logging.log4j.plugins.Named; import org.apache.logging.log4j.test.junit.CleanUpDirectories; import org.junit.jupiter.api.Test; +import org.junitpioneer.jupiter.DisabledUntil; /** * */ +@DisabledUntil(date = "2024-04-01", reason = "Temporarily disabled due to deadlocks.") public class RollingAppenderCronTest extends AbstractRollingListenerTest { private static final String CONFIG = "log4j-rolling-cron.xml"; @@ -54,7 +56,6 @@ public void testAppender(final LoggerContext context, @Named("RollingFile") fina final File file = new File(FILE); assertThat(file).exists(); logger.debug("This is test message number 1"); - currentTimeMillis.addAndGet(2500); rollover.await(); final File dir = new File(DIR); @@ -67,12 +68,10 @@ public void testAppender(final LoggerContext context, @Named("RollingFile") fina Files.newOutputStream(Path.of("target", "test-classes", "log4j-rolling-cron.xml"))) { Files.copy(src, os); } - currentTimeMillis.addAndGet(5000); // force a reconfiguration for (int i = 0; i < 20; ++i) { logger.debug("Adding new event {}", i); } - currentTimeMillis.addAndGet(1000); reconfigured.await(); final RollingFileAppender appender = context.getConfiguration().getAppender("RollingFile"); final TriggeringPolicy policy = appender.getManager().getTriggeringPolicy(); diff --git a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/appender/rolling/RollingAppenderDirectWriteWithHtmlLayoutTest.java b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/appender/rolling/RollingAppenderDirectWriteWithHtmlLayoutTest.java index c8cc306acec..afe24c95f2e 100644 --- a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/appender/rolling/RollingAppenderDirectWriteWithHtmlLayoutTest.java +++ b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/appender/rolling/RollingAppenderDirectWriteWithHtmlLayoutTest.java @@ -71,7 +71,7 @@ private void checkAppenderWithHtmlLayout(final boolean append, final Configurati .setStrategy(DirectWriteRolloverStrategy.newBuilder() .setConfig(config) .build()) - .setLayout(HtmlLayout.createDefaultLayout()) + .setLayout(HtmlLayout.newBuilder().setConfiguration(config).build()) .setAppend(append) .build(); boolean stopped = false; diff --git a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/config/JiraLog4j2_2134Test.java b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/config/JiraLog4j2_2134Test.java index 2b80472a263..67f7c38e284 100644 --- a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/config/JiraLog4j2_2134Test.java +++ b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/config/JiraLog4j2_2134Test.java @@ -55,7 +55,7 @@ public void testRefresh() { .setAdditivity(false) .setLevel(Level.INFO) .setLoggerName("testlog4j2refresh") - .setIncludeLocation("true") + .setIncludeLocation(true) .setRefs(refs) .setConfig(config) .build(); @@ -98,7 +98,6 @@ public void testRefreshMinimalCodeStopStartConfig() { assertDoesNotThrow(() -> log.error("Info message")); } - @SuppressWarnings("deprecation") @Test public void testRefreshDeprecatedApis() { final Logger log = LogManager.getLogger(this.getClass()); @@ -138,7 +137,7 @@ public void testRefreshDeprecatedApis() { .setAdditivity(false) .setLevel(Level.INFO) .setLoggerName("testlog4j2refresh") - .setIncludeLocation("true") + .setIncludeLocation(true) .setRefs(refs) .setConfig(config) .build(); diff --git a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/config/LoggerContextAwarePostProcessorTest.java b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/config/LoggerContextAwarePostProcessorTest.java index 20c0e66c4e5..70a151e1de9 100644 --- a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/config/LoggerContextAwarePostProcessorTest.java +++ b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/config/LoggerContextAwarePostProcessorTest.java @@ -18,7 +18,9 @@ import static org.assertj.core.api.Assertions.assertThat; +import java.net.URI; import org.apache.logging.log4j.core.LoggerContext; +import org.apache.logging.log4j.plugins.di.DI; import org.apache.logging.log4j.plugins.di.InstanceFactory; import org.junit.jupiter.api.Test; @@ -34,7 +36,8 @@ public void setLoggerContext(final LoggerContext loggerContext) { @Test void loggerContextAwareInjection() { - try (final LoggerContext context = new LoggerContext(getClass().getName())) { + try (final LoggerContext context = + new LoggerContext(getClass().getName(), null, (URI) null, DI.createInitializedFactory())) { final InstanceFactory instanceFactory = context.getInstanceFactory(); final TestBean instance = instanceFactory.getInstance(TestBean.class); assertThat(instance.context).isSameAs(context); diff --git a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/impl/ThreadContextDataInjectorTest.java b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/impl/ThreadContextDataInjectorTest.java index 5bb6691169e..7ea70a46146 100644 --- a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/impl/ThreadContextDataInjectorTest.java +++ b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/impl/ThreadContextDataInjectorTest.java @@ -122,8 +122,8 @@ private void testContextDataInjector() { } private void prepareThreadContext(final boolean isThreadContextMapInheritable) { - LoggingSystem.getInstance() - .setThreadContextMapFactory(() -> threadContextMapConstructor.apply(isThreadContextMapInheritable)); + LoggingSystem.getProvider() + .setThreadContextMapFactory(threadContextMapConstructor.apply(isThreadContextMapInheritable)); ThreadContext.init(); ThreadContext.remove("baz"); ThreadContext.put("foo", "bar"); diff --git a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/layout/HtmlLayoutTest.java b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/layout/HtmlLayoutTest.java index 044a8cac7c7..cb14f727eab 100644 --- a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/layout/HtmlLayoutTest.java +++ b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/layout/HtmlLayoutTest.java @@ -38,6 +38,7 @@ 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.DefaultConfiguration; import org.apache.logging.log4j.core.test.BasicConfigurationFactory; import org.apache.logging.log4j.core.test.appender.ListAppender; @@ -53,6 +54,9 @@ @UsingAnyThreadContext @ConfigurationFactoryType(BasicConfigurationFactory.class) public class HtmlLayoutTest { + + private final Configuration CONFIGURATION = new DefaultConfiguration(); + private static class MyLogEvent extends AbstractLogEvent { @Override @@ -91,14 +95,15 @@ public Message getMessage() { @Test public void testDefaultContentType() { - final HtmlLayout layout = HtmlLayout.createDefaultLayout(); + final HtmlLayout layout = + HtmlLayout.newBuilder().setConfiguration(CONFIGURATION).build(); assertEquals("text/html; charset=UTF-8", layout.getContentType()); } @Test public void testContentType() { final HtmlLayout layout = HtmlLayout.newBuilder() - .setConfiguration(new DefaultConfiguration()) + .setConfiguration(CONFIGURATION) .setContentType("text/html; charset=UTF-16") .build(); assertEquals("text/html; charset=UTF-16", layout.getContentType()); @@ -108,7 +113,8 @@ public void testContentType() { @Test public void testDefaultCharset() { - final HtmlLayout layout = HtmlLayout.createDefaultLayout(); + final HtmlLayout layout = + HtmlLayout.newBuilder().setConfiguration(CONFIGURATION).build(); assertEquals(StandardCharsets.UTF_8, layout.getCharset()); } 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 e5fc2ec29bc..0ceed7c43ae 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,6 +16,7 @@ */ package org.apache.logging.log4j.core.lookup; +import static org.apache.logging.log4j.core.lookup.StrSubstitutorTest.LOOKUP_PLUGINS; import static org.junit.Assert.assertSame; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; @@ -66,7 +67,7 @@ public void testGetDefaultLookup() { final Map map = new HashMap<>(); map.put(TESTKEY, TESTVAL); final MapLookup defaultLookup = new MapLookup(map); - final Interpolator interpolator = new Interpolator(defaultLookup); + final Interpolator interpolator = new Interpolator(defaultLookup, LOOKUP_PLUGINS); assertEquals(defaultLookup.getMap(), ((MapLookup) interpolator.getDefaultLookup()).getMap()); assertSame(defaultLookup, interpolator.getDefaultLookup()); } @@ -75,7 +76,7 @@ public void testGetDefaultLookup() { public void testLookup() { final Map map = new HashMap<>(); map.put(TESTKEY, TESTVAL); - final StrLookup lookup = new Interpolator(new MapLookup(map)); + final StrLookup lookup = new Interpolator(new MapLookup(map), LOOKUP_PLUGINS); ThreadContext.put(TESTKEY, TESTVAL); String value = lookup.lookup(TESTKEY); assertEquals(TESTVAL, value); @@ -101,7 +102,7 @@ private void assertLookupNotEmpty(final StrLookup lookup, final String key) { @Test public void testLookupWithDefaultInterpolator() { - final StrLookup lookup = new Interpolator(); + final StrLookup lookup = new Interpolator(new PropertiesLookup(Map.of()), LOOKUP_PLUGINS); String value = lookup.lookup("sys:" + TESTKEY); assertEquals(TESTVAL, value); value = lookup.lookup("env:PATH"); @@ -123,7 +124,7 @@ public void testLookupWithDefaultInterpolator() { public void testInterpolatorMapMessageWithNoPrefix() { final HashMap configProperties = new HashMap<>(); configProperties.put("key", "configProperties"); - final Interpolator interpolator = new Interpolator(configProperties); + final Interpolator interpolator = new Interpolator(new PropertiesLookup(configProperties), LOOKUP_PLUGINS); final HashMap map = new HashMap<>(); map.put("key", "mapMessage"); final LogEvent event = Log4jLogEvent.newBuilder() @@ -137,7 +138,8 @@ public void testInterpolatorMapMessageWithNoPrefix() { @Test public void testInterpolatorMapMessageWithNoPrefixConfigDoesntMatch() { - final Interpolator interpolator = new Interpolator(Collections.emptyMap()); + final Interpolator interpolator = + new Interpolator(new PropertiesLookup(Collections.emptyMap()), LOOKUP_PLUGINS); final HashMap map = new HashMap<>(); map.put("key", "mapMessage"); final LogEvent event = Log4jLogEvent.newBuilder() @@ -153,7 +155,7 @@ public void testInterpolatorMapMessageWithNoPrefixConfigDoesntMatch() { public void testInterpolatorMapMessageWithMapPrefix() { final HashMap configProperties = new HashMap<>(); configProperties.put("key", "configProperties"); - final Interpolator interpolator = new Interpolator(configProperties); + final Interpolator interpolator = new Interpolator(new PropertiesLookup(configProperties), LOOKUP_PLUGINS); final HashMap map = new HashMap<>(); map.put("key", "mapMessage"); final LogEvent event = Log4jLogEvent.newBuilder() diff --git a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/lookup/MainLookupTest.java b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/lookup/MainLookupTest.java index c0802beb8ea..14d5cd0be5d 100644 --- a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/lookup/MainLookupTest.java +++ b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/lookup/MainLookupTest.java @@ -16,6 +16,7 @@ */ package org.apache.logging.log4j.core.lookup; +import static org.apache.logging.log4j.core.lookup.StrSubstitutorTest.LOOKUP_PLUGINS; import static org.junit.jupiter.api.Assertions.assertEquals; import java.util.HashMap; @@ -35,7 +36,7 @@ public void testMainArgs() { final Map properties = new HashMap(); properties.put("key", "value"); properties.put("bar", "default_bar_value"); - final Interpolator lookup = new Interpolator(properties); + final Interpolator lookup = new Interpolator(new PropertiesLookup(properties), LOOKUP_PLUGINS); final StrSubstitutor substitutor = new StrSubstitutor(lookup); final String replacedValue = substitutor.replace(null, str); final String[] values = replacedValue.split(" "); diff --git a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/lookup/StrSubstitutorTest.java b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/lookup/StrSubstitutorTest.java index 6f5a119d2a9..0b88a4e662d 100644 --- a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/lookup/StrSubstitutorTest.java +++ b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/lookup/StrSubstitutorTest.java @@ -20,8 +20,13 @@ import java.util.HashMap; import java.util.Map; +import java.util.function.Supplier; +import java.util.stream.Collectors; import org.apache.logging.log4j.ThreadContext; import org.apache.logging.log4j.core.LogEvent; +import org.apache.logging.log4j.plugins.di.ConfigurableInstanceFactory; +import org.apache.logging.log4j.plugins.di.DI; +import org.apache.logging.log4j.plugins.model.PluginType; import org.apache.logging.log4j.test.junit.StatusLoggerLevel; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; @@ -30,6 +35,14 @@ @StatusLoggerLevel("OFF") public class StrSubstitutorTest { + static final ConfigurableInstanceFactory INSTANCE_FACTORY = DI.createInitializedFactory(); + static final Map> LOOKUP_PLUGINS = + INSTANCE_FACTORY.getInstance(StrLookup.PLUGIN_CATEGORY_KEY).stream() + .collect(Collectors.toMap( + PluginType::getKey, + value -> INSTANCE_FACTORY.getFactory( + value.getPluginClass().asSubclass(StrLookup.class)))); + private static final String TESTKEY = "TestKey"; private static final String TESTVAL = "TestValue"; @@ -47,7 +60,7 @@ public static void after() { public void testLookup() { final Map map = new HashMap<>(); map.put(TESTKEY, TESTVAL); - final StrLookup lookup = new Interpolator(new MapLookup(map)); + final StrLookup lookup = new Interpolator(new MapLookup(map), LOOKUP_PLUGINS); final StrSubstitutor subst = new StrSubstitutor(lookup); ThreadContext.put(TESTKEY, TESTVAL); String value = subst.replace("${TestKey}-${ctx:TestKey}-${sys:TestKey}"); @@ -162,7 +175,7 @@ public void testValueEscapeDelimiter() { public void testDefault() { final Map map = new HashMap<>(); map.put(TESTKEY, TESTVAL); - final StrLookup lookup = new Interpolator(new MapLookup(map)); + final StrLookup lookup = new Interpolator(new MapLookup(map), LOOKUP_PLUGINS); final StrSubstitutor subst = new StrSubstitutor(lookup); ThreadContext.put(TESTKEY, TESTVAL); // String value = subst.replace("${sys:TestKey1:-${ctx:TestKey}}"); @@ -174,7 +187,7 @@ public void testDefault() { public void testDefaultReferencesLookupValue() { final Map map = new HashMap<>(); map.put(TESTKEY, "${java:version}"); - final StrLookup lookup = new Interpolator(new MapLookup(map)); + final StrLookup lookup = new Interpolator(new MapLookup(map), LOOKUP_PLUGINS); final StrSubstitutor subst = new StrSubstitutor(lookup); subst.setRecursiveEvaluationAllowed(false); final String value = subst.replace("${sys:TestKey1:-${ctx:TestKey}}"); @@ -183,7 +196,7 @@ public void testDefaultReferencesLookupValue() { @Test public void testInfiniteSubstitutionOnString() { - final StrLookup lookup = new Interpolator(new MapLookup(new HashMap<>())); + final StrLookup lookup = new Interpolator(new MapLookup(new HashMap<>()), LOOKUP_PLUGINS); final StrSubstitutor subst = new StrSubstitutor(lookup); subst.setRecursiveEvaluationAllowed(true); final String infiniteSubstitution = "${${::-${::-$${::-j}}}}"; @@ -192,7 +205,7 @@ public void testInfiniteSubstitutionOnString() { @Test public void testInfiniteSubstitutionOnStringBuilder() { - final StrLookup lookup = new Interpolator(new MapLookup(new HashMap<>())); + final StrLookup lookup = new Interpolator(new MapLookup(new HashMap<>()), LOOKUP_PLUGINS); final StrSubstitutor subst = new StrSubstitutor(lookup); subst.setRecursiveEvaluationAllowed(true); final String infiniteSubstitution = "${${::-${::-$${::-j}}}}"; @@ -204,7 +217,7 @@ public void testRecursiveSubstitution() { final Map map = new HashMap<>(); map.put("first", "${ctx:first}"); map.put("second", "secondValue"); - final StrLookup lookup = new Interpolator(new MapLookup(map)); + final StrLookup lookup = new Interpolator(new MapLookup(map), LOOKUP_PLUGINS); final StrSubstitutor subst = new StrSubstitutor(lookup); subst.setRecursiveEvaluationAllowed(true); assertEquals("${ctx:first} and secondValue", subst.replace("${ctx:first} and ${ctx:second}")); @@ -214,7 +227,7 @@ public void testRecursiveSubstitution() { public void testRecursiveWithDefault() { final Map map = new HashMap<>(); map.put("first", "${ctx:first:-default}"); - final StrLookup lookup = new Interpolator(new MapLookup(map)); + final StrLookup lookup = new Interpolator(new MapLookup(map), LOOKUP_PLUGINS); final StrSubstitutor subst = new StrSubstitutor(lookup); subst.setRecursiveEvaluationAllowed(true); assertEquals("default", subst.replace("${ctx:first}")); @@ -224,7 +237,7 @@ public void testRecursiveWithDefault() { public void testRecursiveWithRecursiveDefault() { final Map map = new HashMap<>(); map.put("first", "${ctx:first:-${ctx:first}}"); - final StrLookup lookup = new Interpolator(new MapLookup(map)); + final StrLookup lookup = new Interpolator(new MapLookup(map), LOOKUP_PLUGINS); final StrSubstitutor subst = new StrSubstitutor(lookup); subst.setRecursiveEvaluationAllowed(true); assertEquals("${ctx:first}", subst.replace("${ctx:first}")); @@ -234,7 +247,7 @@ public void testRecursiveWithRecursiveDefault() { public void testNestedSelfReferenceWithRecursiveEvaluation() { final Map map = new HashMap<>(); map.put("first", "${${ctx:first}}"); - final StrLookup lookup = new Interpolator(new MapLookup(map)); + final StrLookup lookup = new Interpolator(new MapLookup(map), LOOKUP_PLUGINS); final StrSubstitutor subst = new StrSubstitutor(lookup); subst.setRecursiveEvaluationAllowed(true); assertEquals("${${ctx:first}}", subst.replace("${ctx:first}")); @@ -244,7 +257,7 @@ public void testNestedSelfReferenceWithRecursiveEvaluation() { public void testRandomWithRecursiveDefault() { final Map map = new HashMap<>(); map.put("first", "${env:RANDOM:-${ctx:first}}"); - final StrLookup lookup = new Interpolator(new MapLookup(map)); + final StrLookup lookup = new Interpolator(new MapLookup(map), LOOKUP_PLUGINS); final StrSubstitutor subst = new StrSubstitutor(lookup); subst.setRecursiveEvaluationAllowed(true); assertEquals("${ctx:first}", subst.replace("${ctx:first}")); @@ -255,7 +268,7 @@ public void testNoRecursiveEvaluationWithDefault() { final Map map = new HashMap<>(); map.put("first", "${java:version}"); map.put("second", "${java:runtime}"); - final StrLookup lookup = new Interpolator(new MapLookup(map)); + final StrLookup lookup = new Interpolator(new MapLookup(map), LOOKUP_PLUGINS); final StrSubstitutor subst = new StrSubstitutor(lookup); subst.setRecursiveEvaluationAllowed(false); assertEquals("${java:version}", subst.replace("${ctx:first:-${ctx:second}}")); @@ -265,7 +278,7 @@ public void testNoRecursiveEvaluationWithDefault() { public void testNoRecursiveEvaluationWithDepthOne() { final Map map = new HashMap<>(); map.put("first", "${java:version}"); - final StrLookup lookup = new Interpolator(new MapLookup(map)); + final StrLookup lookup = new Interpolator(new MapLookup(map), LOOKUP_PLUGINS); final StrSubstitutor subst = new StrSubstitutor(lookup); subst.setRecursiveEvaluationAllowed(false); assertEquals("${java:version}", subst.replace("${ctx:first}")); @@ -275,7 +288,7 @@ public void testNoRecursiveEvaluationWithDepthOne() { public void testLookupsNestedWithoutRecursiveEvaluation() { final Map map = new HashMap<>(); map.put("first", "${java:version}"); - final StrLookup lookup = new Interpolator(new MapLookup(map)); + final StrLookup lookup = new Interpolator(new MapLookup(map), LOOKUP_PLUGINS); final StrSubstitutor subst = new StrSubstitutor(lookup); subst.setRecursiveEvaluationAllowed(false); assertEquals("${java:version}", subst.replace("${${lower:C}t${lower:X}:first}")); @@ -283,21 +296,23 @@ public void testLookupsNestedWithoutRecursiveEvaluation() { @Test public void testLookupThrows() { - final StrSubstitutor subst = new StrSubstitutor(new Interpolator(new StrLookup() { - - @Override - public String lookup(final String key) { - if ("throw".equals(key)) { - throw new RuntimeException(); - } - return "success"; - } - - @Override - public String lookup(final LogEvent event, final String key) { - return lookup(key); - } - })); + final StrSubstitutor subst = new StrSubstitutor(new Interpolator( + new StrLookup() { + + @Override + public String lookup(final String key) { + if ("throw".equals(key)) { + throw new RuntimeException(); + } + return "success"; + } + + @Override + public String lookup(final LogEvent event, final String key) { + return lookup(key); + } + }, + LOOKUP_PLUGINS)); subst.setRecursiveEvaluationAllowed(false); assertEquals("success ${foo:throw} success", subst.replace("${foo:a} ${foo:throw} ${foo:c}")); } @@ -306,7 +321,7 @@ public String lookup(final LogEvent event, final String key) { public void testTopLevelLookupsWithoutRecursiveEvaluation() { final Map map = new HashMap<>(); map.put("key", "VaLuE"); - final StrLookup lookup = new Interpolator(new MapLookup(map)); + final StrLookup lookup = new Interpolator(new MapLookup(map), LOOKUP_PLUGINS); final StrSubstitutor subst = new StrSubstitutor(lookup); subst.setRecursiveEvaluationAllowed(false); assertEquals("value", subst.replace("${lower:${ctx:key}}")); @@ -316,7 +331,7 @@ public void testTopLevelLookupsWithoutRecursiveEvaluation() { public void testTopLevelLookupsWithoutRecursiveEvaluation_doubleLower() { final Map map = new HashMap<>(); map.put("key", "VaLuE"); - final StrLookup lookup = new Interpolator(new MapLookup(map)); + final StrLookup lookup = new Interpolator(new MapLookup(map), LOOKUP_PLUGINS); final StrSubstitutor subst = new StrSubstitutor(lookup); subst.setRecursiveEvaluationAllowed(false); assertEquals("value", subst.replace("${lower:${lower:${ctx:key}}}")); @@ -326,7 +341,7 @@ public void testTopLevelLookupsWithoutRecursiveEvaluation_doubleLower() { public void testTopLevelLookupsWithoutRecursiveEvaluationAndDefaultValueLookup() { final Map map = new HashMap<>(); map.put("key2", "TWO"); - final StrLookup lookup = new Interpolator(new MapLookup(map)); + final StrLookup lookup = new Interpolator(new MapLookup(map), LOOKUP_PLUGINS); final StrSubstitutor subst = new StrSubstitutor(lookup); subst.setRecursiveEvaluationAllowed(false); assertEquals("two", subst.replace("${lower:${ctx:key1:-${ctx:key2}}}")); diff --git a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/pattern/PatternParserTest2.java b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/pattern/PatternParserTest2.java index 94e83708247..1e4f51b4243 100644 --- a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/pattern/PatternParserTest2.java +++ b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/pattern/PatternParserTest2.java @@ -22,10 +22,14 @@ import java.util.Calendar; import java.util.Date; import java.util.List; +import org.apache.logging.log4j.core.config.Configuration; +import org.apache.logging.log4j.core.config.NullConfiguration; import org.junit.jupiter.api.Test; public class PatternParserTest2 { + private static final Configuration CONFIGURATION = new NullConfiguration(); + @Test public void testParseConvertBackslashes() { final boolean convert = true; @@ -58,7 +62,7 @@ public void testParseDontConvertBackslashes() { private void parse( final String pattern, final boolean convert, final StringBuilder buf, final Date date, final int i) { - final PatternParser parser0 = new PatternParser(null, "Converter", null); + final PatternParser parser0 = new PatternParser(CONFIGURATION, "Converter", null); final List converters = new ArrayList<>(); final List fields = new ArrayList<>(); parser0.parse(pattern, converters, fields, false, false, convert); diff --git a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/pattern/StyleConverterTest.java b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/pattern/StyleConverterTest.java index 783300bc2fe..2481fe8b626 100644 --- a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/pattern/StyleConverterTest.java +++ b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/pattern/StyleConverterTest.java @@ -19,8 +19,6 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.junit.Assert.assertNull; import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertTrue; import java.util.List; import org.apache.logging.log4j.Level; @@ -54,11 +52,8 @@ public void testReplacement(final LoggerContext context, @Named("List") final Li logger.error(this.getClass().getName()); final List msgs = app.getMessages(); - assertNotNull(msgs); - assertEquals(1, msgs.size(), "Incorrect number of messages. Should be 1 is " + msgs.size()); - assertTrue( - msgs.get(0).endsWith(EXPECTED), - "Replacement failed - expected ending " + EXPECTED + ", actual " + msgs.get(0)); + assertThat(msgs).isNotEmpty(); + assertThat(msgs.get(0)).as("check formatted message").endsWith(EXPECTED); } @Test diff --git a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/util/NamedLoggerContextPropertiesTest.java b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/util/NamedLoggerContextPropertiesTest.java index 8e8f2dea73a..a483be69a4e 100644 --- a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/util/NamedLoggerContextPropertiesTest.java +++ b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/util/NamedLoggerContextPropertiesTest.java @@ -19,7 +19,6 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; -import java.net.URI; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.core.LifeCycle; import org.apache.logging.log4j.core.LoggerContext; @@ -38,7 +37,7 @@ public class NamedLoggerContextPropertiesTest { public void testProperties() { final LoggerContext context = LoggerContext.getContext(); assertEquals(LifeCycle.State.STARTED, context.getState()); - final PropertyEnvironment props = context.getProperties(); + final PropertyEnvironment props = context.getEnvironment(); assertNotNull(props, "Logger Context Properties were not loaded"); final String scriptLanguages = props.getStringProperty("Script.enableLanguages"); assertEquals("Groovy,JavaScript", scriptLanguages); @@ -56,8 +55,9 @@ public TestContextSelector(final ConfigurableInstanceFactory injector) { super(injector); } + @Override protected LoggerContext createContext() { - return new LoggerContext("my-app", null, (URI) null, instanceFactory); + return createContext("my-app", null, getClass().getClassLoader()); } } } diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/Logger.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/Logger.java index ab87808b5a4..09a6d89482c 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/Logger.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/Logger.java @@ -32,6 +32,8 @@ import org.apache.logging.log4j.message.FlowMessageFactory; import org.apache.logging.log4j.message.Message; import org.apache.logging.log4j.message.MessageFactory; +import org.apache.logging.log4j.plugins.Inject; +import org.apache.logging.log4j.plugins.Named; import org.apache.logging.log4j.spi.recycler.RecyclerFactory; import org.apache.logging.log4j.util.Strings; import org.apache.logging.log4j.util.Supplier; @@ -802,4 +804,76 @@ public boolean equals(final Object o) { public int hashCode() { return getName().hashCode(); } + + public static class Builder { + private String name; + /** + * Message factory explicitly requested by the user. + */ + private @Nullable MessageFactory messageFactory; + + private final LoggerContext context; + private final MessageFactory defaultMessageFactory; + private final FlowMessageFactory flowMessageFactory; + private final RecyclerFactory recyclerFactory; + private final org.apache.logging.log4j.Logger statusLogger; + + @Inject + public Builder( + final LoggerContext context, + final MessageFactory defaultMessageFactory, + final FlowMessageFactory flowMessageFactory, + final RecyclerFactory recyclerFactory, + final @Named("StatusLogger") org.apache.logging.log4j.Logger statusLogger) { + this.context = context; + this.defaultMessageFactory = defaultMessageFactory; + this.flowMessageFactory = flowMessageFactory; + this.recyclerFactory = recyclerFactory; + this.statusLogger = statusLogger; + } + + public Builder setName(final String name) { + this.name = name; + return this; + } + + public Builder setMessageFactory(final MessageFactory messageFactory) { + this.messageFactory = messageFactory; + return this; + } + + protected LoggerContext getContext() { + return context; + } + + protected String getName() { + return name; + } + + protected MessageFactory getActualMessageFactory() { + return messageFactory != null ? messageFactory : defaultMessageFactory; + } + + protected FlowMessageFactory getFlowMessageFactory() { + return flowMessageFactory; + } + + protected RecyclerFactory getRecyclerFactory() { + return recyclerFactory; + } + + protected org.apache.logging.log4j.Logger getStatusLogger() { + return statusLogger; + } + + public Logger build() { + return new Logger( + getContext(), + getName(), + getActualMessageFactory(), + getFlowMessageFactory(), + getRecyclerFactory(), + getStatusLogger()); + } + } } 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 53e7b30fdaf..6b2a2898c16 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 @@ -18,13 +18,14 @@ import static org.apache.logging.log4j.core.util.ShutdownCallbackRegistry.SHUTDOWN_HOOK_MARKER; -import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import java.io.File; import java.net.URI; import java.util.ArrayList; import java.util.Collection; +import java.util.Comparator; import java.util.List; import java.util.Objects; +import java.util.ServiceLoader; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.CopyOnWriteArrayList; @@ -35,24 +36,25 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.ThreadContext; import org.apache.logging.log4j.core.config.Configuration; -import org.apache.logging.log4j.core.config.ConfigurationFactory; +import org.apache.logging.log4j.core.config.ConfigurationScheduler; import org.apache.logging.log4j.core.config.ConfigurationSource; import org.apache.logging.log4j.core.config.DefaultConfiguration; import org.apache.logging.log4j.core.config.LoggerContextAwarePostProcessor; import org.apache.logging.log4j.core.config.NullConfiguration; import org.apache.logging.log4j.core.config.Reconfigurable; +import org.apache.logging.log4j.core.config.URIConfigurationFactory; import org.apache.logging.log4j.core.impl.Log4jPropertyKey; import org.apache.logging.log4j.core.util.Cancellable; -import org.apache.logging.log4j.core.util.Constants; import org.apache.logging.log4j.core.util.ExecutorServices; import org.apache.logging.log4j.core.util.NetUtils; import org.apache.logging.log4j.core.util.ShutdownCallbackRegistry; -import org.apache.logging.log4j.message.FlowMessageFactory; import org.apache.logging.log4j.message.MessageFactory; +import org.apache.logging.log4j.plugins.Inject; import org.apache.logging.log4j.plugins.di.ConfigurableInstanceFactory; -import org.apache.logging.log4j.plugins.di.DI; import org.apache.logging.log4j.plugins.di.InstanceFactory; import org.apache.logging.log4j.plugins.di.Key; +import org.apache.logging.log4j.plugins.di.spi.ConfigurableInstanceFactoryPostProcessor; +import org.apache.logging.log4j.plugins.util.OrderedComparator; import org.apache.logging.log4j.spi.ExtendedLogger; import org.apache.logging.log4j.spi.LoggerContextFactory; import org.apache.logging.log4j.spi.LoggerContextShutdownAware; @@ -60,11 +62,12 @@ import org.apache.logging.log4j.spi.LoggerRegistry; import org.apache.logging.log4j.spi.LoggingSystem; import org.apache.logging.log4j.spi.Terminable; -import org.apache.logging.log4j.spi.recycler.RecyclerFactory; import org.apache.logging.log4j.status.StatusLogger; import org.apache.logging.log4j.util.Lazy; import org.apache.logging.log4j.util.PropertiesUtil; import org.apache.logging.log4j.util.PropertyEnvironment; +import org.apache.logging.log4j.util.ServiceLoaderUtil; +import org.jspecify.annotations.Nullable; /** * The LoggerContext is the anchor for the logging system. It maintains a list of all the loggers requested by @@ -81,17 +84,18 @@ public class LoggerContext extends AbstractLifeCycle public static final Key KEY = Key.forClass(LoggerContext.class); private final LoggerRegistry loggerRegistry = new LoggerRegistry<>(); - private final List> configurationStartedListeners = new ArrayList<>(); - private final List> configurationStoppedListeners = new ArrayList<>(); + private final Collection> configurationStartedListeners = new ArrayList<>(); + private final Collection> configurationStoppedListeners = new ArrayList<>(); private final Lazy> listeners = Lazy.relaxed(CopyOnWriteArrayList::new); private final ConfigurableInstanceFactory instanceFactory; - private PropertiesUtil properties; + private final PropertyEnvironment environment; + private final ConfigurationScheduler configurationScheduler; /** * The Configuration is volatile to guarantee that initialization of the Configuration has completed before the * reference is updated. */ - private volatile Configuration configuration = new DefaultConfiguration(); + private volatile Configuration configuration; private final Configuration nullConfiguration; private static final String EXTERNAL_CONTEXT_KEY = "__EXTERNAL_CONTEXT_KEY__"; @@ -102,115 +106,67 @@ public class LoggerContext extends AbstractLifeCycle private final Lock configLock = new ReentrantLock(); - /** - * Constructor used to create an InternalLoggerContext. - */ - protected LoggerContext() { - setStarted(); - instanceFactory = null; - this.nullConfiguration = null; - } - - /** - * Constructor taking only a name. - * - * @param name The context name. - */ - public LoggerContext(final String name) { - this(name, null, (URI) null); - } - - /** - * Constructor taking a name and a reference to an external context. - * - * @param name The context name. - * @param externalContext The external context. - */ - public LoggerContext(final String name, final Object externalContext) { - this(name, externalContext, (URI) null); - } - - /** - * Constructor taking a name, external context and a configuration URI. - * - * @param name The context name. - * @param externalContext The external context. - * @param configLocn The location of the configuration as a URI. - */ - public LoggerContext(final String name, final Object externalContext, final URI configLocn) { - this(name, externalContext, configLocn, DI.createInitializedFactory()); - } - /** * Constructs a LoggerContext with a name, external context, configuration URI, and a ConfigurableInstanceFactory. * * @param name context name * @param externalContext external context or null - * @param configLocn location of configuration as a URI + * @param configLocation location of configuration as a URI * @param instanceFactory initialized ConfigurableInstanceFactory */ public LoggerContext( final String name, - final Object externalContext, - final URI configLocn, + final @Nullable Object externalContext, + final @Nullable URI configLocation, final ConfigurableInstanceFactory instanceFactory) { - this.contextName = name; + this.contextName = Objects.requireNonNull(name); + this.instanceFactory = Objects.requireNonNull(instanceFactory); + this.instanceFactory.registerBinding(KEY, Lazy.weak(this)); + // Post-process the factory, after registering itself + ServiceLoaderUtil.safeStream(ServiceLoader.load( + ConfigurableInstanceFactoryPostProcessor.class, LoggerContext.class.getClassLoader())) + .sorted(Comparator.comparing( + ConfigurableInstanceFactoryPostProcessor::getClass, OrderedComparator.INSTANCE)) + .forEachOrdered(processor -> processor.postProcessFactory(instanceFactory)); + + this.instanceFactory.registerInstancePostProcessor(new LoggerContextAwarePostProcessor(this)); if (externalContext != null) { externalMap.put(EXTERNAL_CONTEXT_KEY, externalContext); } - this.configLocation = configLocn; - this.instanceFactory = instanceFactory.newChildInstanceFactory(); - initializeInstanceFactory(); + this.configLocation = configLocation; + this.environment = instanceFactory.getInstance(PropertyEnvironment.class); + this.configurationScheduler = instanceFactory.getInstance(ConfigurationScheduler.class); + + this.configuration = new DefaultConfiguration(this); this.nullConfiguration = new NullConfiguration(this); } - /** - * Constructor taking a name external context and a configuration location String. The location must be resolvable - * to a File. - * - * @param name The configuration location. - * @param externalContext The external context. - * @param configLocn The configuration location. - */ - @SuppressFBWarnings( - value = "PATH_TRAVERSAL_IN", - justification = "The configLocn comes from a secure source (Log4j properties)") - public LoggerContext(final String name, final Object externalContext, final String configLocn) { - this(name, externalContext, configLocn, DI.createInitializedFactory()); + private static @Nullable URI fileToUri(final String fileName) { + if (fileName != null) { + try { + return new File(fileName).toURI(); + } catch (final Exception ignored) { + // NOP + } + } + return null; } /** * Constructs a LoggerContext with a name, external context, configuration location string, and an instance factory. * The location must be resolvable to a File. * - * @param name context name + * @param contextName context name * @param externalContext external context or null - * @param configLocn configuration location + * @param configLocation configuration location * @param instanceFactory initialized ConfigurableInstanceFactory */ public LoggerContext( - final String name, + final String contextName, final Object externalContext, - final String configLocn, + final String configLocation, final ConfigurableInstanceFactory instanceFactory) { - this.contextName = name; - if (externalContext != null) { - externalMap.put(EXTERNAL_CONTEXT_KEY, externalContext); - } - if (configLocn != null) { - URI uri; - try { - uri = new File(configLocn).toURI(); - } catch (final Exception ex) { - uri = null; - } - configLocation = uri; - } else { - configLocation = null; - } - this.instanceFactory = instanceFactory.newChildInstanceFactory(); - initializeInstanceFactory(); - this.nullConfiguration = new NullConfiguration(this); + this(contextName, externalContext, fileToUri(configLocation), instanceFactory); } /** @@ -245,19 +201,9 @@ public static void checkMessageFactory(final ExtendedLogger logger, final Messag } } - private void initializeInstanceFactory() { - final Lazy ref = Lazy.weak(this); - instanceFactory.registerBinding(KEY, ref); - instanceFactory.registerInstancePostProcessor(new LoggerContextAwarePostProcessor(this)); - } - - public void setProperties(final PropertiesUtil properties) { - this.properties = properties; - } - @Override - public PropertyEnvironment getProperties() { - return properties; + public PropertyEnvironment getEnvironment() { + return environment; } @Override @@ -357,7 +303,7 @@ public static LoggerContext getContext( @Override public void start() { LOGGER.debug("Starting {}...", this); - if (getProperties().getBooleanProperty(Log4jPropertyKey.STACKTRACE_ON_START, false)) { + if (getEnvironment().getBooleanProperty(Log4jPropertyKey.STACKTRACE_ON_START, false)) { LOGGER.debug( "Stack trace to locate invoker", new Exception("Not a real error, showing stack trace to locate invoker")); @@ -479,6 +425,9 @@ public boolean stop(final long timeout, final TimeUnit timeUnit) { shutdownCallback.cancel(); shutdownCallback = null; } + if (configurationScheduler.isStarted()) { + configurationScheduler.stop(timeout, timeUnit); + } final Configuration prev = configuration; configuration = nullConfiguration; updateLoggers(); @@ -618,15 +567,9 @@ public Logger getLogger(final String name, final MessageFactory messageFactory) checkMessageFactory(logger, messageFactory); return logger; } - final MessageFactory actualMessageFactory = - messageFactory != null ? messageFactory : instanceFactory.getInstance(MessageFactory.class); - final FlowMessageFactory flowMessageFactory = instanceFactory.getInstance(FlowMessageFactory.class); - final RecyclerFactory recyclerFactory = instanceFactory.getInstance(RecyclerFactory.class); - final org.apache.logging.log4j.Logger statusLogger = - instanceFactory.getInstance(Constants.DEFAULT_STATUS_LOGGER_KEY); - logger = newInstance(this, name, actualMessageFactory, flowMessageFactory, recyclerFactory, statusLogger); - loggerRegistry.putIfAbsent(name, actualMessageFactory, logger); - return loggerRegistry.getLogger(name, actualMessageFactory); + logger = newLogger(name, messageFactory); + loggerRegistry.putIfAbsent(name, logger.getMessageFactory(), logger); + return loggerRegistry.getLogger(name, logger.getMessageFactory()); } /** @@ -715,8 +658,8 @@ public Configuration getConfiguration(final String name, final URI configLocatio return getConfigurationFactory().getConfiguration(this, name, configLocation, loader); } - private ConfigurationFactory getConfigurationFactory() { - return instanceFactory.getInstance(ConfigurationFactory.KEY); + private URIConfigurationFactory getConfigurationFactory() { + return instanceFactory.getInstance(URIConfigurationFactory.KEY); } /** @@ -873,34 +816,22 @@ private void reconfigure(final URI configURI) { final Object externalContext = externalMap.get(EXTERNAL_CONTEXT_KEY); final ClassLoader cl = externalContext instanceof ClassLoader ? (ClassLoader) externalContext : null; LOGGER.debug("Reconfiguration started for {} at URI {} with optional ClassLoader: {}", this, configURI, cl); - boolean setProperties = false; - if (properties != null && !PropertiesUtil.hasThreadProperties()) { - PropertiesUtil.setThreadProperties(properties); - setProperties = true; - } - try { - final Configuration instance = getConfiguration(contextName, configURI, cl); - if (instance == null) { - LOGGER.error( - "Reconfiguration failed: No configuration found for '{}' at '{}' in '{}'", - contextName, - configURI, - cl); - } else { - setConfiguration(instance); - /* - * instance.start(); Configuration old = setConfiguration(instance); updateLoggers(); if (old != null) { - * old.stop(); } - */ - final String location = - configuration == null ? "?" : String.valueOf(configuration.getConfigurationSource()); - LOGGER.debug( - "Reconfiguration complete for {} at URI {} with optional ClassLoader: {}", this, location, cl); - } - } finally { - if (setProperties) { - PropertiesUtil.clearThreadProperties(); - } + final Configuration instance = getConfiguration(contextName, configURI, cl); + if (instance == null) { + LOGGER.error( + "Reconfiguration failed: No configuration found for '{}' at '{}' in '{}'", + contextName, + configURI, + cl); + } else { + setConfiguration(instance); + /* + * instance.start(); Configuration old = setConfiguration(instance); updateLoggers(); if (old != null) { + * old.stop(); } + */ + final String location = + configuration == null ? "?" : String.valueOf(configuration.getConfigurationSource()); + LOGGER.debug("Reconfiguration complete for {} at URI {} with optional ClassLoader: {}", this, location, cl); } } @@ -980,15 +911,17 @@ public String toString() { return "LoggerContext[" + contextName + "]"; } - // LOG4J2-151: changed visibility from private to protected - protected Logger newInstance( - final LoggerContext ctx, - final String name, - final MessageFactory messageFactory, - final FlowMessageFactory flowMessageFactory, - final RecyclerFactory recyclerFactory, - final org.apache.logging.log4j.Logger statusLogger) { - return new Logger(ctx, name, messageFactory, flowMessageFactory, recyclerFactory, statusLogger); + protected Class getLoggerBuilderClass() { + return Logger.Builder.class; + } + + private Logger newLogger(final String name, final @Nullable MessageFactory messageFactory) { + final Logger.Builder builder = + instanceFactory.getInstance(getLoggerBuilderClass()).setName(name); + if (messageFactory != null) { + builder.setMessageFactory(messageFactory); + } + return builder.build(); } /** @@ -997,4 +930,57 @@ protected Logger newInstance( public boolean includeLocation() { return true; } + + public static class Builder { + private final ConfigurableInstanceFactory parentInstanceFactory; + + private String contextName; + private @Nullable URI configLocation; + private ClassLoader loader = LoggerContext.class.getClassLoader(); + + @Inject + public Builder(final ConfigurableInstanceFactory parentInstanceFactory) { + this.parentInstanceFactory = parentInstanceFactory; + } + + private PropertyEnvironment createProperties(final String contextName, final ClassLoader loader) { + return PropertiesUtil.getContextProperties(loader, contextName); + } + + private ConfigurableInstanceFactory createInstanceFactory( + final PropertyEnvironment environment, final ClassLoader loader) { + return parentInstanceFactory.newChildInstanceFactory(() -> environment, () -> loader); + } + + protected String getContextName() { + return contextName; + } + + public Builder setContextName(final String contextName) { + this.contextName = contextName; + return this; + } + + protected @Nullable URI getConfigLocation() { + return configLocation; + } + + public Builder setConfigLocation(final URI configLocation) { + this.configLocation = configLocation; + return this; + } + + public Builder setLoader(final ClassLoader loader) { + this.loader = loader; + return this; + } + + protected ConfigurableInstanceFactory createInstanceFactory() { + return createInstanceFactory(createProperties(contextName, loader), loader); + } + + public LoggerContext build() { + return new LoggerContext(getContextName(), null, getConfigLocation(), createInstanceFactory()); + } + } } diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/ConsoleAppender.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/ConsoleAppender.java index c3dfa9a78cb..614084161af 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/ConsoleAppender.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/ConsoleAppender.java @@ -168,7 +168,7 @@ public ConsoleAppender build() { final Configuration configuration = getConfiguration(); final PropertyEnvironment propertyEnvironment = configuration != null && configuration.getLoggerContext() != null - ? configuration.getLoggerContext().getProperties() + ? configuration.getLoggerContext().getEnvironment() : PropertiesUtil.getProperties(); return new ConsoleAppender( getName(), diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/SyslogAppender.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/SyslogAppender.java index 18896aa5696..9bd613138eb 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/SyslogAppender.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/SyslogAppender.java @@ -22,7 +22,6 @@ import org.apache.logging.log4j.core.Filter; import org.apache.logging.log4j.core.Layout; import org.apache.logging.log4j.core.config.Configuration; -import org.apache.logging.log4j.core.config.DefaultConfiguration; import org.apache.logging.log4j.core.layout.LoggerFields; import org.apache.logging.log4j.core.layout.Rfc5424Layout; import org.apache.logging.log4j.core.layout.SyslogLayout; @@ -113,7 +112,7 @@ public SyslogAppender build() { if (layout == null) { layout = RFC5424.equalsIgnoreCase(format) ? new Rfc5424Layout.Rfc5424LayoutBuilder() - .setConfig(new DefaultConfiguration()) + .setConfig(configuration) .setFacility(facility) .setId(id) .setEin(enterpriseNumber) @@ -131,12 +130,11 @@ public SyslogAppender build() { .setExceptionPattern(exceptionPattern) .setUseTLSMessageFormat(useTlsMessageFormat) .setLoggerFields(loggerFields) - .setConfig(configuration) .build() : // @formatter:off SyslogLayout.newBuilder() - .setConfiguration(new DefaultConfiguration()) + .setConfiguration(configuration) .setFacility(facility) .setIncludeNewLine(newLine) .setEscapeNL(escapeNL) diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/rolling/DefaultRolloverStrategy.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/rolling/DefaultRolloverStrategy.java index 316551eca7b..24f415b56bf 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/rolling/DefaultRolloverStrategy.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/rolling/DefaultRolloverStrategy.java @@ -145,7 +145,7 @@ public DefaultRolloverStrategy build() { final String trimmedCompressionLevelStr = compressionLevelStr != null ? compressionLevelStr.trim() : compressionLevelStr; final int compressionLevel = Integers.parseInt(trimmedCompressionLevelStr, Deflater.DEFAULT_COMPRESSION); - // The config object can be null when this object is built programmatically. + // The config object can be null only in tests final Configuration configuration = config != null ? config : new NullConfiguration(); final StrSubstitutor nonNullStrSubstitutor = configuration.getStrSubstitutor(); return new DefaultRolloverStrategy( 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 189a1b8e5c9..d895437054f 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 @@ -22,6 +22,7 @@ import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.Collections; +import java.util.Comparator; import java.util.HashSet; import java.util.List; import java.util.Map; @@ -72,21 +73,25 @@ import org.apache.logging.log4j.plugins.Namespace; import org.apache.logging.log4j.plugins.Node; import org.apache.logging.log4j.plugins.di.ConfigurableInstanceFactory; -import org.apache.logging.log4j.plugins.di.DI; import org.apache.logging.log4j.plugins.di.Key; +import org.apache.logging.log4j.plugins.di.spi.ConfigurableInstanceFactoryPostProcessor; import org.apache.logging.log4j.plugins.di.spi.StringValueResolver; import org.apache.logging.log4j.plugins.model.PluginNamespace; import org.apache.logging.log4j.plugins.model.PluginType; +import org.apache.logging.log4j.plugins.util.OrderedComparator; import org.apache.logging.log4j.util.Cast; import org.apache.logging.log4j.util.Lazy; import org.apache.logging.log4j.util.NameUtil; -import org.apache.logging.log4j.util.PropertiesUtil; import org.apache.logging.log4j.util.PropertyEnvironment; import org.apache.logging.log4j.util.ServiceLoaderUtil; +import org.apache.logging.log4j.util.Strings; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; /** * The base Configuration. Many configuration implementations will extend this class. */ +@NullMarked @ServiceConsumer(value = ScriptManagerFactory.class, cardinality = Cardinality.SINGLE, resolution = Resolution.OPTIONAL) public abstract class AbstractConfiguration extends AbstractFilterable implements Configuration { @@ -151,13 +156,13 @@ public abstract class AbstractConfiguration extends AbstractFilterable implement private final Interpolator tempLookup; private final StrSubstitutor runtimeStrSubstitutor; private final StrSubstitutor configurationStrSubstitutor; - private LoggerConfig root = new LoggerConfig(); + private LoggerConfig root; private final ConcurrentMap componentMap = new ConcurrentHashMap<>(); private final ConfigurationSource configurationSource; private final ConfigurationScheduler configurationScheduler; private final WatchManager watchManager; private final WeakReference loggerContext; - private final PropertyEnvironment contextProperties; + private final PropertyEnvironment environment; private final Lock configLock = new ReentrantLock(); private final List extensions = new CopyOnWriteArrayList<>(); @@ -165,30 +170,43 @@ public abstract class AbstractConfiguration extends AbstractFilterable implement * Constructor. */ protected AbstractConfiguration(final LoggerContext loggerContext, final ConfigurationSource configurationSource) { + this( + Objects.requireNonNull(loggerContext), + configurationSource, + loggerContext.getEnvironment(), + (ConfigurableInstanceFactory) loggerContext.getInstanceFactory()); + } + + protected AbstractConfiguration( + final @Nullable LoggerContext loggerContext, + final ConfigurationSource configurationSource, + final PropertyEnvironment environment, + final ConfigurableInstanceFactory parentInstanceFactory) { this.loggerContext = new WeakReference<>(loggerContext); - // The loggerContext is null for the NullConfiguration class. - // this.loggerContext = new WeakReference(Objects.requireNonNull(loggerContext, "loggerContext is null")); this.configurationSource = Objects.requireNonNull(configurationSource, "configurationSource is null"); - if (loggerContext != null) { - instanceFactory = loggerContext.newChildInstanceFactory(); - this.contextProperties = loggerContext.getProperties(); - } else { - // for NullConfiguration - instanceFactory = DI.createInitializedFactory(); - this.contextProperties = PropertiesUtil.getProperties(); - } + // The scheduler is shared by all configurations + this.configurationScheduler = parentInstanceFactory.getInstance(ConfigurationScheduler.class); + this.environment = environment; + this.instanceFactory = parentInstanceFactory.newChildInstanceFactory(); + this.watchManager = new WatchManager(configurationScheduler); + configurationProcessor = new ConfigurationProcessor(instanceFactory); - final var ref = Lazy.weak(this); - instanceFactory.registerBinding(Configuration.KEY, ref); - instanceFactory.registerInstancePostProcessor(new ConfigurationAwarePostProcessor(ref)); + instanceFactory.registerBinding(Configuration.KEY, Lazy.weak(this)); + ServiceLoaderUtil.safeStream(ServiceLoader.load( + ConfigurableInstanceFactoryPostProcessor.class, AbstractConfiguration.class.getClassLoader())) + .sorted(Comparator.comparing( + ConfigurableInstanceFactoryPostProcessor::getClass, OrderedComparator.INSTANCE)) + .forEachOrdered(processor -> processor.postProcessFactory(instanceFactory)); + + instanceFactory.registerInstancePostProcessor(new ConfigurationAwarePostProcessor(Lazy.weak(this))); componentMap.put(Configuration.CONTEXT_PROPERTIES, properties); interpolatorFactory = instanceFactory.getInstance(InterpolatorFactory.class); tempLookup = interpolatorFactory.newInterpolator(new PropertiesLookup(properties)); instanceFactory.injectMembers(tempLookup); runtimeStrSubstitutor = new RuntimeStrSubstitutor(tempLookup); configurationStrSubstitutor = new ConfigurationStrSubstitutor(runtimeStrSubstitutor); - configurationScheduler = instanceFactory.getInstance(ConfigurationScheduler.class); - watchManager = instanceFactory.getInstance(WatchManager.class); + // Root logger + root = new LoggerConfig(Strings.EMPTY, Level.ERROR, true, this); setState(State.INITIALIZING); } @@ -202,9 +220,13 @@ public Map getProperties() { return properties; } + protected ConfigurableInstanceFactory getInstanceFactory() { + return instanceFactory; + } + @Override - public PropertyEnvironment getContextProperties() { - return contextProperties; + public PropertyEnvironment getEnvironment() { + return environment; } @Override @@ -527,6 +549,11 @@ public Supplier getFactory(final Key key) { return instanceFactory.getFactory(key); } + @Override + public void setComponent(final Key key, final Supplier supplier) { + instanceFactory.registerBinding(key, supplier); + } + @Override public void addComponent(final String componentName, final Object obj) { componentMap.putIfAbsent(componentName, obj); @@ -755,7 +782,7 @@ protected void setToDefault() { addAppender(appender); final LoggerConfig rootLoggerConfig = getRootLogger(); rootLoggerConfig.addAppender(appender, null, null); - final String defaultLevelName = contextProperties.getStringProperty(Log4jPropertyKey.CONFIG_DEFAULT_LEVEL); + final String defaultLevelName = environment.getStringProperty(Log4jPropertyKey.CONFIG_DEFAULT_LEVEL); final Level defaultLevel = Level.toLevel(defaultLevelName, Level.ERROR); rootLoggerConfig.setLevel(defaultLevel); } @@ -860,7 +887,9 @@ public Advertiser getAdvertiser() { */ @Override public ReliabilityStrategy getReliabilityStrategy(final LoggerConfig loggerConfig) { - return ReliabilityStrategyFactory.getReliabilityStrategy(loggerConfig); + final String strategy = + getEnvironment().getStringProperty(Log4jPropertyKey.CONFIG_RELIABILITY_STRATEGY, "AwaitCompletion"); + return ReliabilityStrategyFactory.getReliabilityStrategy(loggerConfig, strategy); } /** @@ -885,7 +914,9 @@ public void addLoggerAppender(final org.apache.logging.log4j.core.Logger logger, if (lc.getName().equals(loggerName)) { lc.addAppender(appender, null, null); } else { - final LoggerConfig nlc = new LoggerConfig(loggerName, lc.getLevel(), lc.isAdditive()); + final Level level = lc.getLevel(); + final boolean additivity = lc.isAdditive(); + final LoggerConfig nlc = new LoggerConfig(loggerName, level, additivity, this); nlc.addAppender(appender, null, null); nlc.setParent(lc); loggerConfigs.putIfAbsent(loggerName, nlc); @@ -915,7 +946,9 @@ public void addLoggerFilter(final org.apache.logging.log4j.core.Logger logger, f if (lc.getName().equals(loggerName)) { lc.addFilter(filter); } else { - final LoggerConfig nlc = new LoggerConfig(loggerName, lc.getLevel(), lc.isAdditive()); + final Level level = lc.getLevel(); + final boolean additivity = lc.isAdditive(); + final LoggerConfig nlc = new LoggerConfig(loggerName, level, additivity, this); nlc.addFilter(filter); nlc.setParent(lc); loggerConfigs.putIfAbsent(loggerName, nlc); @@ -945,7 +978,8 @@ public void setLoggerAdditive(final org.apache.logging.log4j.core.Logger logger, if (lc.getName().equals(loggerName)) { lc.setAdditive(additive); } else { - final LoggerConfig nlc = new LoggerConfig(loggerName, lc.getLevel(), additive); + final Level level = lc.getLevel(); + final LoggerConfig nlc = new LoggerConfig(loggerName, level, additive, this); nlc.setParent(lc); loggerConfigs.putIfAbsent(loggerName, nlc); setParents(); diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/Configuration.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/Configuration.java index c082d5c2bc4..86ca98e64c3 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/Configuration.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/Configuration.java @@ -111,12 +111,12 @@ public interface Configuration extends Filterable { Map getProperties(); /** - * Returns the {@linkplain org.apache.logging.log4j.spi.LoggerContext#getProperties() context properties} + * Returns the {@linkplain org.apache.logging.log4j.spi.LoggerContext#getEnvironment() context properties} * associated with the logger context for this configuration. * * @return the context properties */ - PropertyEnvironment getContextProperties(); + PropertyEnvironment getEnvironment(); /** * Returns the root Logger. @@ -149,6 +149,8 @@ default T getComponent(Key key) { return getFactory(key).get(); } + void setComponent(Key key, Supplier supplier); + void addComponent(String name, Object object); void setAdvertiser(Advertiser advertiser); 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 493638450f8..d145f71c968 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 @@ -29,7 +29,6 @@ import org.apache.logging.log4j.status.StatusLogger; import org.apache.logging.log4j.util.LoaderUtil; import org.apache.logging.log4j.util.PropertiesUtil; -import org.apache.logging.log4j.util.PropertyEnvironment; import org.apache.logging.log4j.util.PropertyKey; /** @@ -52,12 +51,7 @@ * be called in their respective order. DefaultConfiguration is always called * last if no configuration has been returned. */ -public abstract class ConfigurationFactory extends ConfigurationBuilderFactory { - - public ConfigurationFactory() { - super(); - // TEMP For breakpoints - } +public abstract class ConfigurationFactory extends ConfigurationBuilderFactory implements URIConfigurationFactory { public static final PropertyKey LOG4J1_CONFIGURATION_FILE_PROPERTY = Log4jPropertyKey.CONFIG_V1_FILE_NAME; @@ -91,7 +85,6 @@ public ConfigurationFactory() { protected static final String DEFAULT_PREFIX = "log4j2"; protected static final String LOG4J1_VERSION = "1"; - protected static final String LOG4J2_VERSION = "2"; /** * The name of the classloader URI scheme. @@ -105,20 +98,9 @@ public ConfigurationFactory() { protected abstract String[] getSupportedTypes(); - protected String getTestPrefix() { - return TEST_PREFIX; - } - - protected String getDefaultPrefix() { - return DEFAULT_PREFIX; - } - - protected String getVersion() { - return LOG4J2_VERSION; - } - - protected boolean isActive() { - return true; + @Override + public String[] getSupportedExtensions() { + return getSupportedTypes(); } @Deprecated(since = "3.0.0", forRemoval = true) @@ -132,10 +114,23 @@ public static ConfigurationFactory getInstance() { * @return the AuthorizationProvider, if any. */ public static AuthorizationProvider authorizationProvider(final PropertiesUtil props) { - return AuthorizationProvider.getAuthorizationProvider((PropertyEnvironment) props); + return AuthorizationProvider.getAuthorizationProvider(props); } - public abstract Configuration getConfiguration(final LoggerContext loggerContext, ConfigurationSource source); + @Override + public String getTestPrefix() { + return TEST_PREFIX; + } + + @Override + public String getDefaultPrefix() { + return DEFAULT_PREFIX; + } + + @Override + public String getVersion() { + return LOG4J2_VERSION; + } /** * Returns the Configuration. @@ -144,11 +139,9 @@ public static AuthorizationProvider authorizationProvider(final PropertiesUtil p * @param configLocation The configuration location. * @return The Configuration. */ + @Override public Configuration getConfiguration( final LoggerContext loggerContext, final String name, final URI configLocation) { - if (!isActive()) { - return null; - } if (configLocation != null) { final ConfigurationSource source = ConfigurationSource.fromUri(configLocation); if (source != null) { @@ -168,11 +161,9 @@ public Configuration getConfiguration( * * @return The Configuration. */ + @Override public Configuration getConfiguration( final LoggerContext loggerContext, final String name, final URI configLocation, final ClassLoader loader) { - if (!isActive()) { - return null; - } if (loader == null) { return getConfiguration(loggerContext, name, configLocation); } diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/ConfigurationScheduler.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/ConfigurationScheduler.java index fcd235ee674..69a43e8f882 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/ConfigurationScheduler.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/ConfigurationScheduler.java @@ -226,7 +226,10 @@ public void setScheduledFuture(final CronScheduledFuture future) { @Override public void run() { try { - final long millis = scheduledFuture.getFireTime().getTime() - System.currentTimeMillis(); + final CronScheduledFuture scheduledFuture = this.scheduledFuture; + final long millis = scheduledFuture != null + ? scheduledFuture.getFireTime().getTime() - System.currentTimeMillis() + : 0L; if (millis > 0) { LOGGER.debug("{} Cron thread woke up {} millis early. Sleeping", name, millis); try { diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/Configurator.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/Configurator.java index 76eeabef888..410fe25f0fa 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/Configurator.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/Configurator.java @@ -434,7 +434,7 @@ private static boolean setLevel(final String loggerName, final Level level, fina LoggerConfig loggerConfig = config.getLoggerConfig(loggerName); if (!loggerName.equals(loggerConfig.getName())) { // TODO Should additivity be inherited? - loggerConfig = new LoggerConfig(loggerName, level, true); + loggerConfig = new LoggerConfig(loggerName, level, true, config); config.addLogger(loggerName, loggerConfig); loggerConfig.setLevel(level); set = true; diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/DefaultConfiguration.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/DefaultConfiguration.java index 6e0b7be8e0e..607c971ecdf 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/DefaultConfiguration.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/DefaultConfiguration.java @@ -16,6 +16,10 @@ */ package org.apache.logging.log4j.core.config; +import org.apache.logging.log4j.core.LoggerContext; +import org.apache.logging.log4j.plugins.di.DI; +import org.apache.logging.log4j.util.PropertiesUtil; + /** * The default configuration writes all output to the Console using the default logging level. You configure default * logging level by setting the system property "org.apache.logging.log4j.level" to a level name. If you do not @@ -35,10 +39,19 @@ public class DefaultConfiguration extends AbstractConfiguration { public static final String DEFAULT_PATTERN = "%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n"; /** - * Constructor to create the default configuration. + * Only for tests. */ + @Deprecated public DefaultConfiguration() { - super(null, ConfigurationSource.NULL_SOURCE); + super(null, ConfigurationSource.NULL_SOURCE, PropertiesUtil.getProperties(), DI.createInitializedFactory()); + setToDefault(); + } + + /** + * Constructor to create the default configuration. + */ + public DefaultConfiguration(final LoggerContext loggerContext) { + super(loggerContext, ConfigurationSource.NULL_SOURCE); setToDefault(); } diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/DefaultConfigurationFactory.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/DefaultConfigurationFactory.java index 47fe7133dae..2011816c849 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/DefaultConfigurationFactory.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/DefaultConfigurationFactory.java @@ -27,13 +27,12 @@ import org.apache.logging.log4j.core.LoggerContext; import org.apache.logging.log4j.core.config.composite.CompositeConfiguration; import org.apache.logging.log4j.core.impl.Log4jPropertyKey; +import org.apache.logging.log4j.core.lookup.ConfigurationStrSubstitutor; import org.apache.logging.log4j.core.lookup.StrSubstitutor; import org.apache.logging.log4j.core.util.Loader; import org.apache.logging.log4j.core.util.NetUtils; -import org.apache.logging.log4j.plugins.Inject; -import org.apache.logging.log4j.plugins.di.ConfigurableInstanceFactory; +import org.apache.logging.log4j.plugins.di.InstanceFactory; import org.apache.logging.log4j.spi.LoggingSystemProperty; -import org.apache.logging.log4j.util.Lazy; import org.apache.logging.log4j.util.LoaderUtil; import org.apache.logging.log4j.util.PropertiesUtil; import org.apache.logging.log4j.util.PropertyEnvironment; @@ -47,16 +46,6 @@ public class DefaultConfigurationFactory extends ConfigurationFactory { private static final String ALL_TYPES = "*"; private static final String OVERRIDE_PARAM = "override"; - private final Lazy> configurationFactories; - private final StrSubstitutor substitutor; - - @Inject - public DefaultConfigurationFactory( - final ConfigurableInstanceFactory instanceFactory, final StrSubstitutor substitutor) { - configurationFactories = Lazy.lazy(() -> loadConfigurationFactories(instanceFactory)); - this.substitutor = substitutor; - } - /** * Default Factory Constructor. * @@ -67,9 +56,11 @@ public DefaultConfigurationFactory( @Override public Configuration getConfiguration( final LoggerContext loggerContext, final String name, final URI configLocation) { - + final InstanceFactory instanceFactory = loggerContext.getInstanceFactory(); + final List configurationFactories = loadConfigurationFactories(instanceFactory); + final StrSubstitutor substitutor = instanceFactory.getInstance(ConfigurationStrSubstitutor.class); if (configLocation == null) { - PropertyEnvironment properties = loggerContext.getProperties(); + PropertyEnvironment properties = loggerContext.getEnvironment(); if (properties == null) { properties = PropertiesUtil.getProperties(); } @@ -80,7 +71,8 @@ public Configuration getConfiguration( if (sources.length > 1) { final List configs = new ArrayList<>(); for (final String sourceLocation : sources) { - final Configuration config = getConfiguration(loggerContext, sourceLocation.trim()); + final Configuration config = + getConfiguration(null, loggerContext, sourceLocation.trim(), configurationFactories); if (config != null) { if (config instanceof AbstractConfiguration) { configs.add((AbstractConfiguration) config); @@ -93,22 +85,22 @@ public Configuration getConfiguration( } } if (configs.size() > 1) { - return new CompositeConfiguration(configs); + return new CompositeConfiguration(loggerContext, configs); } else if (configs.size() == 1) { return configs.get(0); } } - return getConfiguration(loggerContext, configLocationStr); + return getConfiguration(null, loggerContext, configLocationStr, configurationFactories); } else { final String log4j1ConfigStr = substitutor.replace(properties.getStringProperty(LOG4J1_CONFIGURATION_FILE_PROPERTY)); if (log4j1ConfigStr != null) { System.setProperty(LOG4J1_EXPERIMENTAL.getSystemKey(), "true"); - return getConfiguration(LOG4J1_VERSION, loggerContext, log4j1ConfigStr); + return getConfiguration(LOG4J1_VERSION, loggerContext, log4j1ConfigStr, configurationFactories); } } - for (final ConfigurationFactory factory : configurationFactories.get()) { - final String[] types = factory.getSupportedTypes(); + for (final URIConfigurationFactory factory : configurationFactories) { + final String[] types = factory.getSupportedExtensions(); if (types != null) { for (final String type : types) { if (type.equals(ALL_TYPES)) { @@ -126,7 +118,8 @@ public Configuration getConfiguration( if (sources.length > 1) { final List configs = new ArrayList<>(); for (final String sourceLocation : sources) { - final Configuration config = getConfiguration(loggerContext, sourceLocation.trim()); + final Configuration config = + getConfiguration(null, loggerContext, sourceLocation.trim(), configurationFactories); if (config instanceof AbstractConfiguration) { configs.add((AbstractConfiguration) config); } else { @@ -134,11 +127,11 @@ public Configuration getConfiguration( return null; } } - return new CompositeConfiguration(configs); + return new CompositeConfiguration(loggerContext, configs); } final String configLocationStr = configLocation.toString(); - for (final ConfigurationFactory factory : configurationFactories.get()) { - final String[] types = factory.getSupportedTypes(); + for (final URIConfigurationFactory factory : configurationFactories) { + final String[] types = factory.getSupportedExtensions(); if (types != null) { for (final String type : types) { if (type.equals(ALL_TYPES) || configLocationStr.endsWith(type)) { @@ -152,13 +145,13 @@ public Configuration getConfiguration( } } - Configuration config = getConfiguration(loggerContext, true, name); + Configuration config = getConfiguration(loggerContext, true, name, configurationFactories); if (config == null) { - config = getConfiguration(loggerContext, true, null); + config = getConfiguration(loggerContext, true, null, configurationFactories); if (config == null) { - config = getConfiguration(loggerContext, false, name); + config = getConfiguration(loggerContext, false, name, configurationFactories); if (config == null) { - config = getConfiguration(loggerContext, false, null); + config = getConfiguration(loggerContext, false, null, configurationFactories); } } } @@ -173,31 +166,30 @@ public Configuration getConfiguration( + "to show Log4j 2 internal initialization logging. " + "See https://logging.apache.org/log4j/2.x/manual/configuration.html for instructions on how to configure Log4j 2", LoggingSystemProperty.STATUS_LOGGER_DEBUG); - return new DefaultConfiguration(); - } - - private Configuration getConfiguration(final LoggerContext loggerContext, final String configLocationStr) { - return getConfiguration(null, loggerContext, configLocationStr); + return new DefaultConfiguration(loggerContext); } private Configuration getConfiguration( - final String requiredVersion, final LoggerContext loggerContext, final String configLocationStr) { + final String requiredVersion, + final LoggerContext loggerContext, + final String configLocation, + final Iterable configurationFactories) { ConfigurationSource source = null; try { - source = ConfigurationSource.fromUri(NetUtils.toURI(configLocationStr)); + source = ConfigurationSource.fromUri(NetUtils.toURI(configLocation)); } catch (final Exception ex) { // Ignore the error and try as a String. LOGGER.catching(Level.DEBUG, ex); } if (source != null) { - for (final ConfigurationFactory factory : configurationFactories.get()) { + for (final URIConfigurationFactory factory : configurationFactories) { if (requiredVersion != null && !factory.getVersion().equals(requiredVersion)) { continue; } - final String[] types = factory.getSupportedTypes(); + final String[] types = factory.getSupportedExtensions(); if (types != null) { for (final String type : types) { - if (type.equals(ALL_TYPES) || configLocationStr.endsWith(type)) { + if (type.equals(ALL_TYPES) || configLocation.endsWith(type)) { final Configuration config = factory.getConfiguration(loggerContext, source); if (config != null) { return config; @@ -210,13 +202,17 @@ private Configuration getConfiguration( return null; } - private Configuration getConfiguration(final LoggerContext loggerContext, final boolean isTest, final String name) { + private Configuration getConfiguration( + final LoggerContext loggerContext, + final boolean isTest, + final CharSequence name, + final Iterable configurationFactories) { final boolean named = Strings.isNotEmpty(name); final ClassLoader loader = LoaderUtil.getThreadContextClassLoader(); - for (final ConfigurationFactory factory : configurationFactories.get()) { + for (final URIConfigurationFactory factory : configurationFactories) { String configName; final String prefix = isTest ? factory.getTestPrefix() : factory.getDefaultPrefix(); - final String[] types = factory.getSupportedTypes(); + final String[] types = factory.getSupportedExtensions(); if (types == null) { continue; } @@ -229,13 +225,15 @@ private Configuration getConfiguration(final LoggerContext loggerContext, final final ConfigurationSource source = ConfigurationSource.fromResource(configName, loader); if (source != null) { - if (!factory.isActive()) { + try { + return factory.getConfiguration(loggerContext, source); + } catch (final LinkageError e) { LOGGER.warn( - "Found configuration file {} for inactive ConfigurationFactory {}", - configName, - factory.getClass().getName()); + "Failed to create configuration from resource {} using {}.", + source, + factory.getClass().getName(), + e); } - return factory.getConfiguration(loggerContext, source); } } } @@ -243,16 +241,18 @@ private Configuration getConfiguration(final LoggerContext loggerContext, final } @Override - public String[] getSupportedTypes() { + protected String[] getSupportedTypes() { return null; } @Override public Configuration getConfiguration(final LoggerContext loggerContext, final ConfigurationSource source) { if (source != null) { + final List configurationFactories = + loadConfigurationFactories(loggerContext.getInstanceFactory()); final String config = source.getLocation(); - for (final ConfigurationFactory factory : configurationFactories.get()) { - final String[] types = factory.getSupportedTypes(); + for (final URIConfigurationFactory factory : configurationFactories) { + final String[] types = factory.getSupportedExtensions(); if (types != null) { for (final String type : types) { if (type.equals(ALL_TYPES) || config != null && config.endsWith(type)) { @@ -303,11 +303,11 @@ private String[] parseConfigLocations(final String configLocations) { return new String[] {configLocations}; } - private static List loadConfigurationFactories( - final ConfigurableInstanceFactory instanceFactory) { - final List factories = new ArrayList<>(); + private static List loadConfigurationFactories(final InstanceFactory instanceFactory) { + final List factories = new ArrayList<>(); - Optional.ofNullable(PropertiesUtil.getProperties() + Optional.ofNullable(instanceFactory + .getInstance(PropertyEnvironment.class) .getStringProperty(Log4jPropertyKey.CONFIG_CONFIGURATION_FACTORY_CLASS_NAME)) .flatMap(DefaultConfigurationFactory::tryLoadFactoryClass) .map(clazz -> { @@ -320,10 +320,10 @@ private static List loadConfigurationFactories( }) .ifPresent(factories::add); - final List> configurationFactoryPluginClasses = new ArrayList<>(); + final List> configurationFactoryPluginClasses = new ArrayList<>(); instanceFactory.getInstance(PLUGIN_NAMESPACE_KEY).forEach(type -> { try { - configurationFactoryPluginClasses.add(type.getPluginClass().asSubclass(ConfigurationFactory.class)); + configurationFactoryPluginClasses.add(type.getPluginClass().asSubclass(URIConfigurationFactory.class)); } catch (final Exception ex) { LOGGER.warn("Unable to add class {}", type.getPluginClass(), ex); } @@ -340,7 +340,7 @@ private static List loadConfigurationFactories( return factories; } - private static Optional> tryLoadFactoryClass(final String factoryClass) { + private static Optional> tryLoadFactoryClass(final String factoryClass) { try { return Optional.of(Loader.loadClass(factoryClass).asSubclass(ConfigurationFactory.class)); } catch (final Exception ex) { diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/HttpWatcher.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/HttpWatcher.java index 1fa925a824c..2b7bcd5c253 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/HttpWatcher.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/HttpWatcher.java @@ -62,7 +62,7 @@ public HttpWatcher( final List> configurationListeners, final long lastModifiedMillis) { super(configuration, reconfigurable, configurationListeners); - properties = configuration.getContextProperties(); + properties = configuration.getEnvironment(); sslConfiguration = SslConfigurationFactory.getSslConfiguration(properties); authorizationProvider = AuthorizationProvider.getAuthorizationProvider(properties); this.lastModifiedMillis = lastModifiedMillis; diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/LoggerConfig.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/LoggerConfig.java index ab9930c633b..046364b7aae 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/LoggerConfig.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/LoggerConfig.java @@ -18,6 +18,7 @@ import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; import java.util.List; import java.util.Map; import java.util.function.Predicate; @@ -32,21 +33,20 @@ import org.apache.logging.log4j.core.filter.AbstractFilterable; import org.apache.logging.log4j.core.impl.Log4jLogEvent; import org.apache.logging.log4j.core.impl.LogEventFactory; -import org.apache.logging.log4j.core.impl.ReusableLogEventFactory; import org.apache.logging.log4j.core.lookup.StrSubstitutor; import org.apache.logging.log4j.message.Message; 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.plugins.PluginAttribute; import org.apache.logging.log4j.plugins.PluginBuilderAttribute; import org.apache.logging.log4j.plugins.PluginElement; import org.apache.logging.log4j.plugins.PluginFactory; -import org.apache.logging.log4j.plugins.di.DI; +import org.apache.logging.log4j.plugins.di.Key; import org.apache.logging.log4j.plugins.validation.constraints.Required; import org.apache.logging.log4j.util.PerformanceSensitive; import org.apache.logging.log4j.util.StackLocatorUtil; import org.apache.logging.log4j.util.Strings; +import org.jspecify.annotations.Nullable; /** * Logger object that is created via configuration. @@ -56,6 +56,7 @@ public class LoggerConfig extends AbstractFilterable { public static final String ROOT = "root"; + static Key KEY = Key.forClass(LoggerConfig.class).withQualifierType(PluginElement.class); private List appenderRefs = new ArrayList<>(); private final AppenderControlArraySet appenders = new AppenderControlArraySet(); @@ -86,20 +87,19 @@ public static class Builder> implements org.apache.logging.log4j.plugins.util.Builder { @PluginBuilderAttribute - private Boolean additivity; + private boolean additivity = true; private Level level; private String levelAndRefs; private String loggerName; - private String includeLocation; + private @Nullable Boolean includeLocation; private AppenderRef[] refs; private Property[] properties; private Configuration config; private Filter filter; - private LogEventFactory logEventFactory; public boolean isAdditivity() { - return additivity == null || additivity; + return additivity; } public B setAdditivity(final boolean additivity) { @@ -135,15 +135,21 @@ public B setLoggerName( return asBuilder(); } - public String getIncludeLocation() { + public @Nullable Boolean getIncludeLocation() { return includeLocation; } - public B setIncludeLocation(@PluginAttribute final String includeLocation) { + public B setIncludeLocation(final @Nullable Boolean includeLocation) { this.includeLocation = includeLocation; return asBuilder(); } + // TODO: remove this once https://github.com/apache/logging-log4j2/pull/2329 is solved. + @Deprecated + public B setIncludeLocation(final @PluginAttribute @Nullable String includeLocation) { + return setIncludeLocation(includeLocation != null ? Boolean.valueOf(includeLocation) : null); + } + public AppenderRef[] getRefs() { return refs; } @@ -180,31 +186,13 @@ public B setFilter(@PluginElement final Filter filter) { return asBuilder(); } - public LogEventFactory getLogEventFactory() { - return logEventFactory; - } - - @Inject - public B setLogEventFactory(final LogEventFactory logEventFactory) { - this.logEventFactory = logEventFactory; - return asBuilder(); - } - @Override public LoggerConfig build() { final String name = loggerName.equals(ROOT) ? Strings.EMPTY : loggerName; final LevelAndRefs container = LoggerConfig.getLevelAndRefs(level, refs, levelAndRefs, config); - final boolean useLocation = includeLocation(includeLocation, config); + final boolean useLocation = includeLocation(getIncludeLocation(), config); return new LoggerConfig( - name, - container.refs, - filter, - container.level, - isAdditivity(), - properties, - config, - useLocation, - logEventFactory); + name, container.refs, filter, container.level, isAdditivity(), properties, config, useLocation); } @SuppressWarnings("unchecked") @@ -213,19 +201,6 @@ public B asBuilder() { } } - /** - * Default constructor. - */ - public LoggerConfig() { - this.logEventFactory = DI.createInitializedFactory().getInstance(ReusableLogEventFactory.class); - this.level = Level.ERROR; - this.name = Strings.EMPTY; - this.properties = null; - this.propertiesRequireLookup = false; - this.config = null; - this.reliabilityStrategy = new DefaultReliabilityStrategy(this); - } - /** * Constructor that sets the name, level and additive values. * @@ -233,15 +208,8 @@ public LoggerConfig() { * @param level The Level. * @param additive true if the Logger is additive, false otherwise. */ - public LoggerConfig(final String name, final Level level, final boolean additive) { - this.logEventFactory = DI.createInitializedFactory().getInstance(ReusableLogEventFactory.class); - this.name = name; - this.level = level; - this.additive = additive; - this.properties = null; - this.propertiesRequireLookup = false; - this.config = null; - this.reliabilityStrategy = new DefaultReliabilityStrategy(this); + public LoggerConfig(final String name, final Level level, final boolean additive, final Configuration config) { + this(name, Collections.emptyList(), null, level, additive, null, config, includeLocation(null, config)); } protected LoggerConfig( @@ -252,12 +220,9 @@ protected LoggerConfig( final boolean additive, final Property[] properties, final Configuration config, - final boolean includeLocation, - final LogEventFactory logEventFactory) { + final boolean includeLocation) { super(filter, null); - this.logEventFactory = logEventFactory != null - ? logEventFactory - : DI.createInitializedFactory().getInstance(ReusableLogEventFactory.class); + this.logEventFactory = config.getLogEventFactory(); this.name = name; this.appenderRefs = appenders; this.level = level; @@ -669,17 +634,12 @@ public String toString() { // Note: for asynchronous loggers, includeLocation default is FALSE, // for synchronous loggers, includeLocation default is TRUE. - protected static boolean includeLocation( - final String includeLocationConfigValue, final Configuration configuration) { - if (includeLocationConfigValue == null) { - if (configuration != null) { - final LoggerContext context = configuration.getLoggerContext(); - return context != null ? context.includeLocation() : false; - } else { - return false; - } + protected static boolean includeLocation(final Boolean configuredValue, final Configuration configuration) { + if (configuredValue == null) { + final LoggerContext context = configuration.getLoggerContext(); + return context != null && context.includeLocation(); } - return Boolean.parseBoolean(includeLocationConfigValue); + return configuredValue; } protected final boolean hasAppenders() { @@ -691,112 +651,101 @@ protected final boolean hasAppenders() { */ @Configurable(printObject = true) @Plugin(ROOT) - public static class RootLogger extends LoggerConfig { + public static final class RootLogger extends LoggerConfig { @PluginFactory - public static > B newRootBuilder() { - return new Builder().asBuilder(); + public static Builder newRootBuilder() { + return new Builder(); + } + + private RootLogger() { + super(Strings.EMPTY, Level.ERROR, false, null); } /** * Builds LoggerConfig instances. - * - * @param * The type to build */ - public static class Builder> - implements org.apache.logging.log4j.plugins.util.Builder { + public static class Builder implements org.apache.logging.log4j.plugins.util.Builder { + + protected static final boolean ADDITIVITY = true; - private boolean additivity; private Level level; private String levelAndRefs; - private String includeLocation; + // TODO: Change to Boolean, once the DI starts supporting null Booleans. + private @Nullable Boolean includeLocation; private AppenderRef[] refs; private Property[] properties; private Configuration config; private Filter filter; - private LogEventFactory logEventFactory; - - public boolean isAdditivity() { - return additivity; - } - - public B setAdditivity(@PluginAttribute final boolean additivity) { - this.additivity = additivity; - return asBuilder(); - } public Level getLevel() { return level; } - public B setLevel(@PluginAttribute final Level level) { + public Builder setLevel(@PluginAttribute final Level level) { this.level = level; - return asBuilder(); + return this; } public String getLevelAndRefs() { return levelAndRefs; } - public B setLevelAndRefs(@PluginAttribute final String levelAndRefs) { + public Builder setLevelAndRefs(@PluginAttribute final String levelAndRefs) { this.levelAndRefs = levelAndRefs; - return asBuilder(); + return this; } - public String getIncludeLocation() { + public @Nullable Boolean getIncludeLocation() { return includeLocation; } - public B setIncludeLocation(@PluginAttribute final String includeLocation) { + public Builder setIncludeLocation(final @Nullable Boolean includeLocation) { this.includeLocation = includeLocation; - return asBuilder(); + return this; + } + + // TODO: remove this once https://github.com/apache/logging-log4j2/pull/2329 is solved. + @Deprecated + public Builder setIncludeLocation(final @PluginAttribute @Nullable String includeLocation) { + return setIncludeLocation(includeLocation != null ? Boolean.valueOf(includeLocation) : null); } public AppenderRef[] getRefs() { return refs; } - public B setRefs(@PluginElement final AppenderRef[] refs) { + public Builder setRefs(@PluginElement final AppenderRef[] refs) { this.refs = refs; - return asBuilder(); + return this; } public Property[] getProperties() { return properties; } - public B setProperties(@PluginElement final Property[] properties) { + public Builder setProperties(@PluginElement final Property[] properties) { this.properties = properties; - return asBuilder(); + return this; } public Configuration getConfig() { return config; } - public B setConfig(@PluginConfiguration final Configuration config) { + public Builder setConfig(@PluginConfiguration final Configuration config) { this.config = config; - return asBuilder(); + return this; } public Filter getFilter() { return filter; } - public B setFilter(@PluginElement final Filter filter) { + public Builder setFilter(@PluginElement final Filter filter) { this.filter = filter; - return asBuilder(); - } - - public LogEventFactory getLogEventFactory() { - return logEventFactory; - } - - @Inject - public B setLogEventFactory(final LogEventFactory logEventFactory) { - this.logEventFactory = logEventFactory; - return asBuilder(); + return this; } @Override @@ -807,16 +756,10 @@ public LoggerConfig build() { container.refs, filter, container.level, - additivity, + ADDITIVITY, properties, config, - includeLocation(includeLocation, config), - logEventFactory); - } - - @SuppressWarnings("unchecked") - public B asBuilder() { - return (B) this; + includeLocation(getIncludeLocation(), config)); } } } diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/NullConfiguration.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/NullConfiguration.java index 3384b42fd67..fa95c0c8aa7 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/NullConfiguration.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/NullConfiguration.java @@ -18,6 +18,8 @@ import org.apache.logging.log4j.Level; import org.apache.logging.log4j.core.LoggerContext; +import org.apache.logging.log4j.plugins.di.DI; +import org.apache.logging.log4j.util.PropertiesUtil; /** * This configuration defaults to no logging. @@ -29,8 +31,15 @@ public class NullConfiguration extends AbstractConfiguration { */ public static final String NULL_NAME = "Null"; + /** + * Used only in tests + */ + @Deprecated public NullConfiguration() { - this(null); + super(null, ConfigurationSource.NULL_SOURCE, PropertiesUtil.getProperties(), DI.createInitializedFactory()); + setName(NULL_NAME); + final LoggerConfig root = getRootLogger(); + root.setLevel(Level.OFF); } public NullConfiguration(final LoggerContext loggerContext) { diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/ReliabilityStrategyFactory.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/ReliabilityStrategyFactory.java index 3b6e7b459b5..9bfa18ababb 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/ReliabilityStrategyFactory.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/ReliabilityStrategyFactory.java @@ -44,9 +44,13 @@ private ReliabilityStrategyFactory() {} * configuration change */ public static ReliabilityStrategy getReliabilityStrategy(final LoggerConfig loggerConfig) { + return getReliabilityStrategy( + loggerConfig, + PropertiesUtil.getProperties() + .getStringProperty(Log4jPropertyKey.CONFIG_RELIABILITY_STRATEGY, "AwaitCompletion")); + } - final String strategy = PropertiesUtil.getProperties() - .getStringProperty(Log4jPropertyKey.CONFIG_RELIABILITY_STRATEGY, "AwaitCompletion"); + static ReliabilityStrategy getReliabilityStrategy(final LoggerConfig loggerConfig, final String strategy) { if ("AwaitCompletion".equals(strategy)) { return new AwaitCompletionReliabilityStrategy(loggerConfig); } diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/URIConfigurationFactory.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/URIConfigurationFactory.java new file mode 100644 index 00000000000..9c9f631b372 --- /dev/null +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/URIConfigurationFactory.java @@ -0,0 +1,86 @@ +/* + * 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 java.net.URI; +import org.apache.logging.log4j.core.LoggerContext; +import org.apache.logging.log4j.plugins.di.Key; +import org.jspecify.annotations.Nullable; + +/** + * Creates configuration from an {@link URI}. + */ +public interface URIConfigurationFactory { + + Key KEY = Key.forClass(URIConfigurationFactory.class); + + String LOG4J2_VERSION = "2"; + + /** + * The prefix to use for automatic discovery of configuration files. + */ + String getDefaultPrefix(); + + /** + * The prefix to use for automatic discovery of test configuration files. + */ + String getTestPrefix(); + + /** + * A list of supported file extensions. + *

+ * Can contain {@code *}, if an attempt should be made to parse files with any file extension. + *

+ */ + String[] getSupportedExtensions(); + + /** + * The version of the configuration format. + *

+ * Should return {@value LOG4J2_VERSION} for the Log4j 2.x configuration format. + *

+ */ + String getVersion(); + + /** + * @param loggerContext The logger context to associate with the configuration. + * @param source The source of the configuration. + * @return A Configuration or {@code null}. + */ + @Nullable + Configuration getConfiguration(LoggerContext loggerContext, ConfigurationSource source); + + /** + * @param loggerContext The logger context to associate with the configuration. + * @param name The name of the logger context. + * @param configLocation The configuration location or {@code null} + * @return A Configuration or {@code null}. + */ + @Nullable + Configuration getConfiguration(LoggerContext loggerContext, String name, @Nullable URI configLocation); + + /** + * @param loggerContext The logger context to associate with the configuration. + * @param name The name of the logger context. + * @param configLocation The configuration location or {@code null} + * @param loader The classloader to use to load resources. + * @return A Configuration or {@code null}. + */ + @Nullable + Configuration getConfiguration( + LoggerContext loggerContext, String name, @Nullable URI configLocation, ClassLoader loader); +} diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/impl/BuiltConfiguration.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/impl/BuiltConfiguration.java index ca52e83c72c..1f561b8c31d 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/impl/BuiltConfiguration.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/impl/BuiltConfiguration.java @@ -26,7 +26,11 @@ import org.apache.logging.log4j.core.config.builder.api.Component; import org.apache.logging.log4j.core.config.status.StatusConfiguration; import org.apache.logging.log4j.plugins.Node; +import org.apache.logging.log4j.plugins.di.ConfigurableInstanceFactory; +import org.apache.logging.log4j.plugins.di.DI; import org.apache.logging.log4j.plugins.model.PluginType; +import org.apache.logging.log4j.util.PropertiesUtil; +import org.jspecify.annotations.Nullable; /** * This is the general version of the Configuration created by the Builder. It may be extended to @@ -46,8 +50,16 @@ public class BuiltConfiguration extends AbstractConfiguration { private String contentType = "text"; public BuiltConfiguration( - final LoggerContext loggerContext, final ConfigurationSource source, final Component rootComponent) { - super(loggerContext, source); + final @Nullable LoggerContext loggerContext, + final ConfigurationSource source, + final Component rootComponent) { + super( + loggerContext, + source, + loggerContext != null ? loggerContext.getEnvironment() : PropertiesUtil.getProperties(), + loggerContext != null + ? (ConfigurableInstanceFactory) loggerContext.getInstanceFactory() + : DI.createInitializedFactory()); statusConfig = new StatusConfiguration().setStatus(getDefaultStatus()); for (final Component component : rootComponent.getComponents()) { switch (component.getPluginType()) { diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/composite/CompositeConfiguration.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/composite/CompositeConfiguration.java index 5fced3a969f..8b9455aa227 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/composite/CompositeConfiguration.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/composite/CompositeConfiguration.java @@ -23,11 +23,12 @@ import java.util.List; import java.util.Map; import org.apache.logging.log4j.Level; +import org.apache.logging.log4j.core.LoggerContext; import org.apache.logging.log4j.core.config.AbstractConfiguration; import org.apache.logging.log4j.core.config.Configuration; -import org.apache.logging.log4j.core.config.ConfigurationFactory; import org.apache.logging.log4j.core.config.ConfigurationSource; import org.apache.logging.log4j.core.config.Reconfigurable; +import org.apache.logging.log4j.core.config.URIConfigurationFactory; import org.apache.logging.log4j.core.config.status.StatusConfiguration; import org.apache.logging.log4j.core.util.Source; import org.apache.logging.log4j.core.util.WatchManager; @@ -48,11 +49,12 @@ public class CompositeConfiguration extends AbstractConfiguration implements Rec * * @param configurations The List of Configurations to merge. */ - public CompositeConfiguration(final List configurations) { - super(configurations.get(0).getLoggerContext(), ConfigurationSource.COMPOSITE_SOURCE); + public CompositeConfiguration( + final LoggerContext loggerContext, final List configurations) { + super(loggerContext, ConfigurationSource.COMPOSITE_SOURCE); rootNode = configurations.get(0).getRootNode(); this.configurations = configurations; - mergeStrategy = getComponent(MergeStrategy.KEY); + this.mergeStrategy = getInstanceFactory().getInstance(MergeStrategy.class); for (final AbstractConfiguration config : configurations) { mergeStrategy.mergeRootProperties(rootNode, config); } @@ -127,7 +129,7 @@ public void setup() { public Configuration reconfigure() { LOGGER.debug("Reconfiguring composite configuration"); final List configs = new ArrayList<>(); - final ConfigurationFactory factory = instanceFactory.getInstance(ConfigurationFactory.KEY); + final URIConfigurationFactory factory = instanceFactory.getInstance(URIConfigurationFactory.KEY); for (final AbstractConfiguration config : configurations) { final ConfigurationSource source = config.getConfigurationSource(); final URI sourceURI = source.getURI(); @@ -145,7 +147,7 @@ public Configuration reconfigure() { configs.add((AbstractConfiguration) currentConfig); } - return new CompositeConfiguration(configs); + return new CompositeConfiguration(getLoggerContext(), configs); } private void staffChildConfiguration(final AbstractConfiguration childConfiguration) { diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/xml/XmlConfiguration.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/xml/XmlConfiguration.java index 47f7b895e01..01cb345f394 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/xml/XmlConfiguration.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/xml/XmlConfiguration.java @@ -27,6 +27,7 @@ import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; +import javax.xml.transform.Source; import javax.xml.transform.stream.StreamSource; import javax.xml.validation.Schema; import javax.xml.validation.SchemaFactory; @@ -69,19 +70,19 @@ public class XmlConfiguration extends AbstractConfiguration implements Reconfigu @SuppressFBWarnings( value = "XXE_DOCUMENT", justification = "The `newDocumentBuilder` method disables DTD processing.") - public XmlConfiguration(final LoggerContext loggerContext, final ConfigurationSource configSource) { - super(loggerContext, configSource); + public XmlConfiguration(final LoggerContext loggerContext, final ConfigurationSource configurationSource) { + super(loggerContext, configurationSource); byte[] buffer = null; try { - final InputStream configStream = configSource.getInputStream(); + final InputStream configStream = configurationSource.getInputStream(); try { buffer = configStream.readAllBytes(); } finally { Closer.closeSilently(configStream); } final InputSource source = new InputSource(new ByteArrayInputStream(buffer)); - source.setSystemId(configSource.getLocation()); + source.setSystemId(configurationSource.getLocation()); final DocumentBuilder documentBuilder = newDocumentBuilder(true); Document document; try { @@ -128,19 +129,19 @@ public XmlConfiguration(final LoggerContext loggerContext, final ConfigurationSo } else if ("monitorInterval".equalsIgnoreCase(key)) { monitorIntervalSeconds = Integers.parseInt(value); } else if ("advertiser".equalsIgnoreCase(key)) { - createAdvertiser(value, configSource, buffer, "text/xml"); + createAdvertiser(value, configurationSource, buffer, "text/xml"); } } - initializeWatchers(this, configSource, monitorIntervalSeconds); + initializeWatchers(this, configurationSource, monitorIntervalSeconds); statusConfig.initialize(); } catch (final SAXException | IOException | ParserConfigurationException e) { - LOGGER.error("Error parsing " + configSource.getLocation(), e); + LOGGER.error("Error parsing " + configurationSource.getLocation(), e); } if (strict && schemaResource != null && buffer != null) { try (final InputStream is = Loader.getResourceAsStream(schemaResource, XmlConfiguration.class.getClassLoader())) { if (is != null) { - final javax.xml.transform.Source src = new StreamSource(is, LOG4J_XSD); + final Source src = new StreamSource(is, LOG4J_XSD); final SchemaFactory factory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI); Schema schema = null; try { @@ -165,7 +166,7 @@ public XmlConfiguration(final LoggerContext loggerContext, final ConfigurationSo } if (getName() == null) { - setName(configSource.getLocation()); + setName(configurationSource.getLocation()); } } diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/xml/XmlConfigurationFactory.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/xml/XmlConfigurationFactory.java index 5b3104d436a..0e75d5cc201 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/xml/XmlConfigurationFactory.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/xml/XmlConfigurationFactory.java @@ -53,7 +53,7 @@ public Configuration getConfiguration(final LoggerContext loggerContext, final C * @return An array of File extensions. */ @Override - public String[] getSupportedTypes() { + protected String[] getSupportedTypes() { return SUFFIXES; } } diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/filter/MutableThreadContextMapFilter.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/filter/MutableThreadContextMapFilter.java index 1918d92c297..4cb19175332 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/filter/MutableThreadContextMapFilter.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/filter/MutableThreadContextMapFilter.java @@ -331,7 +331,7 @@ public MutableThreadContextMapFilter build() { return new MutableThreadContextMapFilter( new NoOpFilter(), null, 0, null, getOnMatch(), getOnMismatch(), configuration); } - final PropertyEnvironment props = configuration.getContextProperties(); + final PropertyEnvironment props = configuration.getEnvironment(); final AuthorizationProvider authorizationProvider = AuthorizationProvider.getAuthorizationProvider(props); final SslConfiguration sslConfiguration = SslConfigurationFactory.getSslConfiguration(props); Filter filter; @@ -371,7 +371,7 @@ private class FileMonitor implements Runnable { @Override public void run() { - final PropertyEnvironment properties = configuration.getContextProperties(); + final PropertyEnvironment properties = configuration.getEnvironment(); final SslConfiguration sslConfiguration = SslConfigurationFactory.getSslConfiguration(properties); final ConfigResult result = getConfig(source, authorizationProvider, properties, sslConfiguration); if (result.status == Status.SUCCESS) { diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/DefaultBundle.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/DefaultBundle.java index 102a585bcf6..2f176ea286d 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/DefaultBundle.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/DefaultBundle.java @@ -24,10 +24,13 @@ 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.config.URIConfigurationFactory; import org.apache.logging.log4j.core.config.composite.DefaultMergeStrategy; import org.apache.logging.log4j.core.config.composite.MergeStrategy; +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.RuntimeStrSubstitutor; import org.apache.logging.log4j.core.lookup.StrLookup; import org.apache.logging.log4j.core.lookup.StrSubstitutor; import org.apache.logging.log4j.core.selector.ClassLoaderContextSelector; @@ -39,14 +42,15 @@ import org.apache.logging.log4j.core.util.ShutdownCallbackRegistry; import org.apache.logging.log4j.message.FlowMessageFactory; import org.apache.logging.log4j.message.MessageFactory; -import org.apache.logging.log4j.plugins.Factory; import org.apache.logging.log4j.plugins.Named; import org.apache.logging.log4j.plugins.Namespace; import org.apache.logging.log4j.plugins.SingletonFactory; import org.apache.logging.log4j.plugins.condition.ConditionalOnMissingBinding; import org.apache.logging.log4j.plugins.di.ConfigurableInstanceFactory; import org.apache.logging.log4j.spi.CopyOnWrite; +import org.apache.logging.log4j.spi.LoggerContextFactory; import org.apache.logging.log4j.spi.LoggingSystem; +import org.apache.logging.log4j.spi.Provider; import org.apache.logging.log4j.spi.ReadOnlyThreadContextMap; import org.apache.logging.log4j.spi.recycler.RecyclerFactory; import org.apache.logging.log4j.status.StatusLogger; @@ -67,16 +71,31 @@ public class DefaultBundle { @SingletonFactory + @ConditionalOnMissingBinding + public Provider provider(final ConfigurableInstanceFactory instanceFactory) { + return new Log4jProvider(instanceFactory); + } + + @SingletonFactory + @ConditionalOnMissingBinding + public LoggerContextFactory loggerContextFactory(final ConfigurableInstanceFactory instanceFactory) { + return new Log4jContextFactory(instanceFactory); + } + + @SingletonFactory + @ConditionalOnMissingBinding public MessageFactory defaultMessageFactory() { return LoggingSystem.getMessageFactory(); } @SingletonFactory + @ConditionalOnMissingBinding public FlowMessageFactory defaultFlowMessageFactory() { return LoggingSystem.getFlowMessageFactory(); } @SingletonFactory + @ConditionalOnMissingBinding public RecyclerFactory defaultRecyclerFactory() { return LoggingSystem.getRecyclerFactory(); } @@ -99,20 +118,17 @@ public NanoClock defaultNanoClock() { return new DummyNanoClock(); } - @Factory + @SingletonFactory @ConditionalOnMissingBinding public ContextDataInjector defaultContextDataInjector() { final ReadOnlyThreadContextMap threadContextMap = ThreadContext.getThreadContextMap(); - - // note: map may be null (if legacy custom ThreadContextMap was installed by user) - if (threadContextMap == null) { - // for non StringMap-based context maps - return new ThreadContextDataInjector.ForDefaultThreadContextMap(); - } - if (threadContextMap instanceof CopyOnWrite) { - return new ThreadContextDataInjector.ForCopyOnWriteThreadContextMap(); + if (threadContextMap != null) { + return threadContextMap instanceof CopyOnWrite + ? new ThreadContextDataInjector.ForCopyOnWriteThreadContextMap() + : new ThreadContextDataInjector.ForGarbageFreeThreadContextMap(); } - return new ThreadContextDataInjector.ForGarbageFreeThreadContextMap(); + // for non StringMap-based context maps + return new ThreadContextDataInjector.ForDefaultThreadContextMap(); } @SingletonFactory @@ -134,15 +150,29 @@ public InterpolatorFactory interpolatorFactory( @SingletonFactory @ConditionalOnMissingBinding - public StrSubstitutor strSubstitutor(final InterpolatorFactory factory) { - return new StrSubstitutor(factory.newInterpolator(null)); + public ConfigurationStrSubstitutor configurationStrSubstitutor(final InterpolatorFactory factory) { + return new ConfigurationStrSubstitutor(factory.newInterpolator(null)); + } + + @SingletonFactory + @ConditionalOnMissingBinding + public RuntimeStrSubstitutor runtimeStrSubstitutor(final InterpolatorFactory factory) { + return new RuntimeStrSubstitutor(factory.newInterpolator(null)); + } + + /** + * Spring Boot needs a ConfigurationFactory for compatibility. + */ + @SingletonFactory + @ConditionalOnMissingBinding + public ConfigurationFactory configurationFactory() { + return new DefaultConfigurationFactory(); } @SingletonFactory @ConditionalOnMissingBinding - public ConfigurationFactory configurationFactory( - final ConfigurableInstanceFactory instanceFactory, final StrSubstitutor substitutor) { - return new DefaultConfigurationFactory(instanceFactory, substitutor); + public URIConfigurationFactory configurationFactory(final ConfigurationFactory factory) { + return factory; } @SingletonFactory diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/Log4jContextFactory.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/Log4jContextFactory.java index dae847bf8bc..2d1900cf940 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/Log4jContextFactory.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/Log4jContextFactory.java @@ -29,7 +29,6 @@ import org.apache.logging.log4j.core.config.ConfigurationSource; import org.apache.logging.log4j.core.config.DefaultConfiguration; import org.apache.logging.log4j.core.config.composite.CompositeConfiguration; -import org.apache.logging.log4j.core.impl.internal.InternalLoggerContext; import org.apache.logging.log4j.core.selector.ContextSelector; import org.apache.logging.log4j.core.util.Cancellable; import org.apache.logging.log4j.core.util.ShutdownCallbackRegistry; @@ -38,6 +37,7 @@ import org.apache.logging.log4j.plugins.Singleton; import org.apache.logging.log4j.plugins.di.ConfigurableInstanceFactory; import org.apache.logging.log4j.plugins.di.DI; +import org.apache.logging.log4j.plugins.di.Key; import org.apache.logging.log4j.spi.LoggerContextFactory; import org.apache.logging.log4j.status.StatusLogger; import org.apache.logging.log4j.util.PropertiesUtil; @@ -50,12 +50,17 @@ public class Log4jContextFactory implements LoggerContextFactory, ShutdownCallbackRegistry { private static final StatusLogger LOGGER = StatusLogger.getLogger(); + private static final Key KEY = + Key.builder(LoggerContextFactory.class).get(); private final ContextSelector selector; private final ShutdownCallbackRegistry shutdownCallbackRegistry; /** * Initializes the ContextSelector from system property {@link Log4jPropertyKey#CONTEXT_SELECTOR_CLASS_NAME}. + *

+ * Only used if no {@link org.apache.logging.log4j.spi.Provider} is present. + *

*/ public Log4jContextFactory() { this(DI.createInitializedFactory()); @@ -108,18 +113,8 @@ public Log4jContextFactory( @Inject @NullMarked - public Log4jContextFactory( - final ConfigurableInstanceFactory instanceFactory, - final ContextSelector selector, - final ShutdownCallbackRegistry registry) { - this.selector = selector; - this.shutdownCallbackRegistry = registry; - LOGGER.debug("Using ShutdownCallbackRegistry {}", this.shutdownCallbackRegistry.getClass()); - initializeShutdownCallbackRegistry(); - } - - @NullMarked - private Log4jContextFactory(final ConfigurableInstanceFactory instanceFactory) { + public Log4jContextFactory(final ConfigurableInstanceFactory instanceFactory) { + instanceFactory.registerBinding(KEY, () -> this); selector = instanceFactory.getInstance(ContextSelector.KEY); shutdownCallbackRegistry = instanceFactory.getInstance(ShutdownCallbackRegistry.KEY); LOGGER.debug("Using ShutdownCallbackRegistry {}", shutdownCallbackRegistry.getClass()); @@ -188,21 +183,11 @@ public LoggerContext getContext( if (ctx.getState() == LifeCycle.State.INITIALIZED) { if (source != null) { ContextAnchor.THREAD_CONTEXT.set(ctx); - boolean setProperties = false; try { - if (ctx.getProperties() == null) { - final PropertiesUtil props = PropertiesUtil.getContextProperties(classLoader, ctx.getName()); - ctx.setProperties(props); - PropertiesUtil.setThreadProperties(props); - setProperties = true; - } final Configuration config = ctx.getConfiguration(source); LOGGER.debug("Starting {} from configuration {}", ctx, source); ctx.start(config); } finally { - if (setProperties) { - PropertiesUtil.clearThreadProperties(); - } ContextAnchor.THREAD_CONTEXT.remove(); } } else { @@ -236,19 +221,9 @@ public LoggerContext getContext( } if (ctx.getState() == LifeCycle.State.INITIALIZED) { ContextAnchor.THREAD_CONTEXT.set(ctx); - boolean setProperties = false; try { - if (ctx.getProperties() == null) { - final PropertiesUtil props = PropertiesUtil.getContextProperties(classLoader, ctx.getName()); - ctx.setProperties(props); - PropertiesUtil.setThreadProperties(props); - setProperties = true; - } ctx.start(configuration); } finally { - if (setProperties) { - PropertiesUtil.clearThreadProperties(); - } ContextAnchor.THREAD_CONTEXT.remove(); } } @@ -285,21 +260,11 @@ public LoggerContext getContext( if (ctx.getState() == LifeCycle.State.INITIALIZED) { if (configLocation != null || name != null) { ContextAnchor.THREAD_CONTEXT.set(ctx); - boolean setProperties = false; try { - if (ctx.getProperties() == null) { - final PropertiesUtil props = PropertiesUtil.getContextProperties(classLoader, ctx.getName()); - ctx.setProperties(props); - PropertiesUtil.setThreadProperties(props); - setProperties = true; - } final Configuration config = ctx.getConfiguration(name, configLocation); LOGGER.debug("Starting {} from configuration at {}", ctx, configLocation); ctx.start(config); } finally { - if (setProperties) { - PropertiesUtil.clearThreadProperties(); - } ContextAnchor.THREAD_CONTEXT.remove(); } } else { @@ -324,22 +289,12 @@ public LoggerContext getContext( } if (ctx.getState() == LifeCycle.State.INITIALIZED) { if (configLocation != null || name != null) { - boolean setProperties = false; try { - if (ctx.getProperties() == null) { - final PropertiesUtil props = PropertiesUtil.getContextProperties(classLoader, ctx.getName()); - ctx.setProperties(props); - PropertiesUtil.setThreadProperties(props); - setProperties = true; - } ContextAnchor.THREAD_CONTEXT.set(ctx); final Configuration config = ctx.getConfiguration(name, configLocation); LOGGER.debug("Starting {} from configuration at {}", ctx, configLocation); ctx.start(config); } finally { - if (setProperties) { - PropertiesUtil.clearThreadProperties(); - } ContextAnchor.THREAD_CONTEXT.remove(); } } else { @@ -369,15 +324,8 @@ public LoggerContext getContext( if (ctx.getState() == LifeCycle.State.INITIALIZED) { if ((configLocations != null && !configLocations.isEmpty())) { ContextAnchor.THREAD_CONTEXT.set(ctx); - boolean setProperties = false; try { final List configurations = new ArrayList<>(configLocations.size()); - if (ctx.getProperties() == null) { - final PropertiesUtil props = PropertiesUtil.getContextProperties(classLoader, ctx.getName()); - ctx.setProperties(props); - PropertiesUtil.setThreadProperties(props); - setProperties = true; - } for (final URI configLocation : configLocations) { final Configuration currentReadConfiguration = ctx.getConfiguration(name, configLocation); if (currentReadConfiguration != null) { @@ -399,14 +347,9 @@ public LoggerContext getContext( } else if (configurations.size() == 1) { ctx.start(configurations.get(0)); } else { - final CompositeConfiguration compositeConfiguration = - new CompositeConfiguration(configurations); - ctx.start(compositeConfiguration); + ctx.start(new CompositeConfiguration(ctx, configurations)); } } finally { - if (setProperties) { - PropertiesUtil.clearThreadProperties(); - } ContextAnchor.THREAD_CONTEXT.remove(); } } else { @@ -417,24 +360,11 @@ public LoggerContext getContext( } private void startContext(final LoggerContext ctx, final ClassLoader classLoader) { - boolean setProperties = false; - try { - if (ctx.getProperties() == null) { - final PropertiesUtil props = PropertiesUtil.getContextProperties(classLoader, ctx.getName()); - ctx.setProperties(props); - PropertiesUtil.setThreadProperties(props); - setProperties = true; - } - final Configuration config = ctx.getConfiguration(ctx.getName(), null); - if (config != null) { - ctx.start(config); - } else { - ctx.start(); - } - } finally { - if (setProperties) { - PropertiesUtil.clearThreadProperties(); - } + final Configuration config = ctx.getConfiguration(ctx.getName(), null); + if (config != null) { + ctx.start(config); + } else { + ctx.start(); } } @@ -508,6 +438,6 @@ public boolean isShutdownHookEnabled() { @Override public org.apache.logging.log4j.spi.LoggerContext wrapLoggerContext( org.apache.logging.log4j.spi.LoggerContext loggerContext) { - return new InternalLoggerContext(loggerContext); + return loggerContext; } } diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/Log4jProvider.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/Log4jProvider.java index 378c419bd24..e0f91dbb649 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/Log4jProvider.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/Log4jProvider.java @@ -18,6 +18,11 @@ import aQute.bnd.annotation.Resolution; import aQute.bnd.annotation.spi.ServiceProvider; +import org.apache.logging.log4j.plugins.Inject; +import org.apache.logging.log4j.plugins.di.ConfigurableInstanceFactory; +import org.apache.logging.log4j.plugins.di.DI; +import org.apache.logging.log4j.plugins.di.Key; +import org.apache.logging.log4j.spi.LoggerContextFactory; import org.apache.logging.log4j.spi.Provider; /** @@ -25,7 +30,23 @@ */ @ServiceProvider(value = Provider.class, resolution = Resolution.OPTIONAL) public class Log4jProvider extends Provider { + + private final ConfigurableInstanceFactory instanceFactory; + public Log4jProvider() { + this(DI.createInitializedFactory()); + } + + @Inject + public Log4jProvider(final ConfigurableInstanceFactory instanceFactory) { super(10, "3.0.0", Log4jContextFactory.class); + this.instanceFactory = instanceFactory; + instanceFactory.registerBinding(Key.forClass(Provider.class), () -> this); + instanceFactory.registerBinding(Key.forClass(Log4jProvider.class), () -> this); + } + + @Override + public LoggerContextFactory getLoggerContextFactory() { + return instanceFactory.getInstance(Key.forClass(LoggerContextFactory.class)); } } diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/SystemPropertyBundle.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/SystemPropertyBundle.java index 92635b09000..fc9fba3fb37 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/SystemPropertyBundle.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/SystemPropertyBundle.java @@ -28,6 +28,7 @@ import org.apache.logging.log4j.plugins.Named; import org.apache.logging.log4j.plugins.Ordered; import org.apache.logging.log4j.plugins.SingletonFactory; +import org.apache.logging.log4j.plugins.condition.ConditionalOnMissingBinding; import org.apache.logging.log4j.plugins.di.InjectException; import org.apache.logging.log4j.plugins.di.InstanceFactory; import org.apache.logging.log4j.util.PropertyEnvironment; @@ -61,12 +62,14 @@ public SystemPropertyBundle( } @ConditionalOnPropertyKey(key = Log4jPropertyKey.CONTEXT_SELECTOR_CLASS_NAME) + @ConditionalOnMissingBinding @SingletonFactory public ContextSelector systemPropertyContextSelector() throws ClassNotFoundException { return newInstanceOfProperty(Log4jPropertyKey.CONTEXT_SELECTOR_CLASS_NAME, ContextSelector.class); } @ConditionalOnPropertyKey(key = Log4jPropertyKey.SHUTDOWN_CALLBACK_REGISTRY) + @ConditionalOnMissingBinding @SingletonFactory @Ordered(100) public ShutdownCallbackRegistry systemPropertyShutdownCallbackRegistry() throws ClassNotFoundException { @@ -74,6 +77,7 @@ public ShutdownCallbackRegistry systemPropertyShutdownCallbackRegistry() throws } @ConditionalOnPropertyKey(key = Log4jPropertyKey.THREAD_CONTEXT_DATA_INJECTOR_CLASS_NAME) + @ConditionalOnMissingBinding @Factory public ContextDataInjector systemPropertyContextDataInjector() throws ClassNotFoundException { return newInstanceOfProperty( @@ -81,18 +85,21 @@ public ContextDataInjector systemPropertyContextDataInjector() throws ClassNotFo } @ConditionalOnPropertyKey(key = Log4jPropertyKey.LOG_EVENT_FACTORY_CLASS_NAME) + @ConditionalOnMissingBinding @SingletonFactory public LogEventFactory systemPropertyLogEventFactory() throws ClassNotFoundException { return newInstanceOfProperty(Log4jPropertyKey.LOG_EVENT_FACTORY_CLASS_NAME, LogEventFactory.class); } @ConditionalOnPropertyKey(key = Log4jPropertyKey.CONFIG_MERGE_STRATEGY) + @ConditionalOnMissingBinding @SingletonFactory public MergeStrategy systemPropertyMergeStrategy() throws ClassNotFoundException { return newInstanceOfProperty(Log4jPropertyKey.CONFIG_MERGE_STRATEGY, MergeStrategy.class); } @ConditionalOnPropertyKey(key = Log4jPropertyKey.STATUS_DEFAULT_LEVEL) + @ConditionalOnMissingBinding @SingletonFactory @Named("StatusLogger") public Level systemPropertyDefaultStatusLevel() { diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/internal/InternalLoggerContext.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/internal/InternalLoggerContext.java deleted file mode 100644 index 5e02eb89b40..00000000000 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/internal/InternalLoggerContext.java +++ /dev/null @@ -1,311 +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.impl.internal; - -import java.util.Collections; -import java.util.Iterator; -import java.util.Map; -import java.util.concurrent.TimeUnit; -import org.apache.logging.log4j.Level; -import org.apache.logging.log4j.LogBuilder; -import org.apache.logging.log4j.Marker; -import org.apache.logging.log4j.core.Appender; -import org.apache.logging.log4j.core.Filter; -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.LoggerConfig; -import org.apache.logging.log4j.message.FlowMessageFactory; -import org.apache.logging.log4j.message.Message; -import org.apache.logging.log4j.message.MessageFactory; -import org.apache.logging.log4j.spi.ExtendedLogger; -import org.apache.logging.log4j.spi.LoggingSystem; -import org.apache.logging.log4j.spi.recycler.RecyclerFactory; -import org.apache.logging.log4j.status.StatusLogger; -import org.jspecify.annotations.NullMarked; -import org.jspecify.annotations.Nullable; - -/** - * Creates a SimpleLoggerContext compatible with log4j-core. This class is internal to Log4j. - */ -@NullMarked -public class InternalLoggerContext extends LoggerContext { - - private static final LoggerConfig LOGGER_CONFIG = new LoggerConfig.RootLogger(); - - private final org.apache.logging.log4j.spi.LoggerContext parentLoggerContext; - private final MessageFactory defaultMessageFactory; - private final FlowMessageFactory defaultFlowMessageFactory; - private final RecyclerFactory recyclerFactory; - - public InternalLoggerContext(org.apache.logging.log4j.spi.LoggerContext loggerContext) { - this.parentLoggerContext = loggerContext; - this.defaultMessageFactory = LoggingSystem.getMessageFactory(); - this.defaultFlowMessageFactory = LoggingSystem.getFlowMessageFactory(); - this.recyclerFactory = LoggingSystem.getRecyclerFactory(); - setStarted(); - } - - @Override - protected Logger newInstance( - final LoggerContext ctx, - final String name, - final MessageFactory messageFactory, - final FlowMessageFactory flowMessageFactory, - final RecyclerFactory recyclerFactory, - final org.apache.logging.log4j.Logger statusLogger) { - return new InternalLogger(this, name); - } - - @Override - public boolean stop(final long timeout, final TimeUnit timeUnit) { - return false; - } - - private class InternalLogger extends Logger { - private final ExtendedLogger logger; - private final InternalLoggerContext loggerContext; - - public InternalLogger(final InternalLoggerContext loggerContext, final String name) { - super( - loggerContext, - name, - defaultMessageFactory, - defaultFlowMessageFactory, - recyclerFactory, - StatusLogger.getLogger()); - this.loggerContext = loggerContext; - this.logger = parentLoggerContext.getLogger(name); - } - - @Override - public Logger getParent() { - return null; - } - - @Override - public LoggerContext getContext() { - return loggerContext; - } - - @Override - public void setLevel(final Level level) {} - - @Override - public LoggerConfig get() { - return LOGGER_CONFIG; - } - - @Override - protected boolean requiresLocation() { - return false; - } - - @Override - protected void doLog( - final String fqcn, - @Nullable final StackTraceElement location, - final Level level, - @Nullable final Marker marker, - final Message message, - @Nullable final Throwable throwable) { - logger.log(level, marker, message, throwable); - } - - @Override - public boolean isEnabled(Level level, Marker marker, String message, Throwable t) { - return logger.isEnabled(level, marker, message, t); - } - - @Override - public boolean isEnabled(Level level, Marker marker, String message) { - return logger.isEnabled(level, marker, message); - } - - @Override - public boolean isEnabled(Level level, Marker marker, String message, Object... params) { - return logger.isEnabled(level, marker, message, params); - } - - @Override - public boolean isEnabled(Level level, Marker marker, String message, Object p0) { - return logger.isEnabled(level, marker, message, p0); - } - - @Override - public boolean isEnabled(Level level, Marker marker, String message, Object p0, Object p1) { - return logger.isEnabled(level, marker, message, p0, p1); - } - - @Override - public boolean isEnabled(Level level, Marker marker, String message, Object p0, Object p1, Object p2) { - return logger.isEnabled(level, marker, message, p0, p1, p2); - } - - @Override - public boolean isEnabled( - Level level, Marker marker, String message, Object p0, Object p1, Object p2, Object p3) { - return logger.isEnabled(level, marker, message, p0, p1, p2, p3); - } - - @Override - public boolean isEnabled( - Level level, Marker marker, String message, Object p0, Object p1, Object p2, Object p3, Object p4) { - return logger.isEnabled(level, marker, message, p0, p1, p2, p3, p4); - } - - @Override - public boolean isEnabled( - Level level, - Marker marker, - String message, - Object p0, - Object p1, - Object p2, - Object p3, - Object p4, - Object p5) { - return logger.isEnabled(level, marker, message, p0, p1, p2, p3, p4, p5); - } - - @Override - public boolean isEnabled( - Level level, - Marker marker, - String message, - Object p0, - Object p1, - Object p2, - Object p3, - Object p4, - Object p5, - Object p6) { - return logger.isEnabled(level, marker, message, p0, p1, p2, p3, p4, p5, p6); - } - - @Override - public boolean isEnabled( - Level level, - Marker marker, - String message, - Object p0, - Object p1, - Object p2, - Object p3, - Object p4, - Object p5, - Object p6, - Object p7) { - return logger.isEnabled(level, marker, message, p0, p1, p2, p3, p4, p5, p6, p7); - } - - @Override - public boolean isEnabled( - Level level, - Marker marker, - String message, - Object p0, - Object p1, - Object p2, - Object p3, - Object p4, - Object p5, - Object p6, - Object p7, - Object p8) { - return logger.isEnabled(level, marker, message, p0, p1, p2, p3, p4, p5, p6, p7, p8); - } - - @Override - public boolean isEnabled( - Level level, - Marker marker, - String message, - Object p0, - Object p1, - Object p2, - Object p3, - Object p4, - Object p5, - Object p6, - Object p7, - Object p8, - Object p9) { - return logger.isEnabled(level, marker, message, p0, p1, p2, p3, p4, p5, p6, p7, p8, p9); - } - - @Override - public boolean isEnabled(Level level, Marker marker, CharSequence message, Throwable t) { - return logger.isEnabled(level, marker, message, t); - } - - @Override - public boolean isEnabled(Level level, Marker marker, Object message, Throwable t) { - return logger.isEnabled(level, marker, message, t); - } - - @Override - public boolean isEnabled(Level level, Marker marker, Message message, Throwable t) { - return logger.isEnabled(level, marker, message, t); - } - - @Override - public void addAppender(Appender appender) {} - - @Override - public void removeAppender(Appender appender) {} - - @Override - public Map getAppenders() { - return Collections.emptyMap(); - } - - @Override - public Iterator getFilters() { - return Collections.emptyIterator(); - } - - @Override - public Level getLevel() { - return logger.getLevel(); - } - - @Override - public int filterCount() { - return 0; - } - - @Override - public void addFilter(Filter filter) {} - - @Override - public boolean isAdditive() { - return false; - } - - @Override - public void setAdditive(boolean additive) {} - - @Override - public LogBuilder atLevel(Level level) { - return logger.atLevel(level); - } - - @Override - protected void updateConfiguration(Configuration newConfig) {} - } -} diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/layout/HtmlLayout.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/layout/HtmlLayout.java index 42fdbd10eb9..6459e57cd9f 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/layout/HtmlLayout.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/layout/HtmlLayout.java @@ -32,7 +32,6 @@ import org.apache.logging.log4j.core.Layout; import org.apache.logging.log4j.core.LogEvent; import org.apache.logging.log4j.core.config.Configuration; -import org.apache.logging.log4j.core.config.DefaultConfiguration; import org.apache.logging.log4j.core.config.LoggerConfig; import org.apache.logging.log4j.core.config.plugins.PluginConfiguration; import org.apache.logging.log4j.core.pattern.DatePatternConverter; @@ -377,15 +376,6 @@ public byte[] getFooter() { } } - /** - * Creates an HTML Layout using the default settings. - * - * @return an HTML Layout. - */ - public static HtmlLayout createDefaultLayout() { - return newBuilder().setConfiguration(new DefaultConfiguration()).build(); - } - @PluginFactory public static Builder newBuilder() { return new Builder(); 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 7d8171b3e2c..4686d8a3f4a 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 @@ -27,8 +27,6 @@ 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.di.ConfigurableInstanceFactory; -import org.apache.logging.log4j.plugins.di.DI; import org.apache.logging.log4j.status.StatusLogger; /** @@ -60,49 +58,12 @@ public class Interpolator extends AbstractConfigurationAwareLookup implements Lo private WeakReference loggerContext = null; /** - * 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 - * @since 2.1 - */ - public Interpolator(final StrLookup defaultLookup) { - this.defaultLookup = defaultLookup == null ? new PropertiesLookup(Map.of()) : defaultLookup; - final ConfigurableInstanceFactory instanceFactory = DI.createInitializedFactory(); - // TODO(ms): this should use plugin map injection - instanceFactory.getInstance(PLUGIN_CATEGORY_KEY).forEach((key, value) -> { - try { - strLookups.put( - key, instanceFactory.getFactory(value.getPluginClass().asSubclass(StrLookup.class))); - } catch (final Throwable t) { - handleError(key, t); - } - }); - } - - /** - * Used by interpolatrorFactory. - * - * @param defaultLookup The default Lookup. - * @param strLookupPlugins The Lookup Plugins. + * @param defaultLookup The default {@link StrLookup}. + * @param additionalLookups A map associating a prefix with a secondary {@link StrLookup}. */ - public Interpolator(final StrLookup defaultLookup, final Map> strLookupPlugins) { + public Interpolator(final StrLookup defaultLookup, final Map> additionalLookups) { this.defaultLookup = defaultLookup; - strLookups.putAll(strLookupPlugins); - } - - /** - * Create the default Interpolator. - */ - public Interpolator() { - this(Map.of()); - } - - /** - * Creates the default Interpolator with the provided properties. - */ - public Interpolator(final Map properties) { - this(new PropertiesLookup(properties)); + strLookups.putAll(additionalLookups); } public StrLookup getDefaultLookup() { diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/PatternParser.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/PatternParser.java index 81ffe42769e..b9ac456161f 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/PatternParser.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/PatternParser.java @@ -123,6 +123,7 @@ public PatternParser( final String converterKey, final Class expectedClass, final Class filterClass) { + // Only null in tests config = configuration != null ? configuration : new NullConfiguration(); final Key pluginCategoryKey = PLUGIN_CATEGORY_KEY.withNamespace(converterKey); final PluginNamespace plugins = config.getComponent(pluginCategoryKey); diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/selector/AbstractContextSelector.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/selector/AbstractContextSelector.java new file mode 100644 index 00000000000..a215325f48f --- /dev/null +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/selector/AbstractContextSelector.java @@ -0,0 +1,44 @@ +/* + * 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.selector; + +import java.net.URI; +import org.apache.logging.log4j.core.LoggerContext; +import org.apache.logging.log4j.plugins.di.ConfigurableInstanceFactory; +import org.jspecify.annotations.Nullable; + +public abstract class AbstractContextSelector implements ContextSelector { + + protected final ConfigurableInstanceFactory instanceFactory; + + public AbstractContextSelector(final ConfigurableInstanceFactory instanceFactory) { + this.instanceFactory = instanceFactory; + } + + protected LoggerContext.Builder newBuilder() { + return instanceFactory.getInstance(LoggerContext.Builder.class); + } + + protected final LoggerContext createContext( + final String contextName, final @Nullable URI configLocation, final ClassLoader loader) { + return newBuilder() + .setContextName(contextName) + .setConfigLocation(configLocation) + .setLoader(loader) + .build(); + } +} diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/selector/BasicContextSelector.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/selector/BasicContextSelector.java index ef49ecde745..791116b92b8 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/selector/BasicContextSelector.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/selector/BasicContextSelector.java @@ -33,16 +33,16 @@ * Returns either this Thread's context or the default LoggerContext. */ @Singleton -public class BasicContextSelector implements ContextSelector, LoggerContextShutdownAware { +public class BasicContextSelector extends AbstractContextSelector + implements ContextSelector, LoggerContextShutdownAware { private static final Logger LOGGER = StatusLogger.getLogger(); protected final Lazy context = Lazy.lazy(this::createContext); - protected final ConfigurableInstanceFactory instanceFactory; @Inject public BasicContextSelector(final ConfigurableInstanceFactory instanceFactory) { - this.instanceFactory = instanceFactory; + super(instanceFactory); } @Override @@ -114,7 +114,7 @@ public List getLoggerContexts() { } protected LoggerContext createContext() { - return new LoggerContext("Default", null, (URI) null, instanceFactory); + return createContext("Default", null, getClass().getClassLoader()); } @Override 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 acae3a3dbda..4e7990f9e56 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 @@ -48,18 +48,18 @@ * This ContextSelector should not be used with a Servlet Filter such as the Log4jServletFilter. */ @Singleton -public class ClassLoaderContextSelector implements ContextSelector, LoggerContextShutdownAware { +public class ClassLoaderContextSelector extends AbstractContextSelector + implements ContextSelector, LoggerContextShutdownAware { protected static final StatusLogger LOGGER = StatusLogger.getLogger(); - protected final Lazy defaultContext = Lazy.lazy(() -> createContext(defaultContextName(), null)); + protected final Lazy defaultContext = + Lazy.lazy(() -> createContext(defaultContextName(), null, getClass().getClassLoader())); protected final Map>> contextMap = new ConcurrentHashMap<>(); - protected final ConfigurableInstanceFactory instanceFactory; - @Inject public ClassLoaderContextSelector(final ConfigurableInstanceFactory instanceFactory) { - this.instanceFactory = instanceFactory; + super(instanceFactory); } @Override @@ -188,16 +188,6 @@ public List getLoggerContexts() { return Collections.unmodifiableList(list); } - private ConfigurableInstanceFactory getConfigurableInstanceFactory(final Map.Entry entry) { - if (entry != null) { - final Object value = entry.getValue(); - if (value instanceof ConfigurableInstanceFactory) { - return (ConfigurableInstanceFactory) value; - } - } - return instanceFactory; - } - private LoggerContext locateContext( final ClassLoader loaderOrNull, final Map.Entry entry, final URI configLocation) { // LOG4J2-477: class loader may be null @@ -237,7 +227,7 @@ private LoggerContext locateContext( } */ } } - final LoggerContext ctx = createContext(name, configLocation, getConfigurableInstanceFactory(entry)); + final LoggerContext ctx = createContext(name, configLocation, loader); if (entry != null) { ctx.putObject(entry.getKey(), entry.getValue()); } @@ -269,7 +259,7 @@ private LoggerContext locateContext( } return ctx; } - ctx = createContext(name, configLocation, getConfigurableInstanceFactory(entry)); + ctx = createContext(name, configLocation, loader); if (entry != null) { ctx.putObject(entry.getKey(), entry.getValue()); } @@ -279,16 +269,6 @@ private LoggerContext locateContext( return ctx; } - protected LoggerContext createContext(final String name, final URI configLocation) { - final ConfigurableInstanceFactory instanceFactory = this.instanceFactory; - return createContext(name, configLocation, instanceFactory); - } - - protected LoggerContext createContext( - final String name, final URI configLocation, final ConfigurableInstanceFactory instanceFactory) { - return new LoggerContext(name, null, configLocation, instanceFactory); - } - protected String toContextMapKey(final ClassLoader loader) { return Integer.toHexString(System.identityHashCode(loader)); } diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/tools/Generate.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/tools/Generate.java index 15fe08b3639..5c3d34e85fc 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/tools/Generate.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/tools/Generate.java @@ -62,7 +62,7 @@ String imports() { + "import org.apache.logging.log4j.Marker;%n" + "import org.apache.logging.log4j.message.Message;%n" + "import org.apache.logging.log4j.message.MessageFactory;%n" - + "import org.apache.logging.log4j.spi.AbstractLogger;%n" + + "import org.apache.logging.log4j.spi.ExtendedLogger;%n" + "import org.apache.logging.log4j.spi.ExtendedLoggerWrapper;%n" + "import org.apache.logging.log4j.util.MessageSupplier;%n" + "import org.apache.logging.log4j.util.Supplier;%n" @@ -91,7 +91,7 @@ String constructor() { return "" + "%n" + " private %s(final Logger logger) {%n" - + " this.logger = new ExtendedLoggerWrapper((AbstractLogger) logger, logger.getName(), " + + " this.logger = new ExtendedLoggerWrapper((ExtendedLogger) logger, logger.getName(), " + "logger.getMessageFactory());%n" + " }%n"; // @formatter:on @@ -113,7 +113,7 @@ String imports() { + "import org.apache.logging.log4j.Marker;%n" + "import org.apache.logging.log4j.message.Message;%n" + "import org.apache.logging.log4j.message.MessageFactory;%n" - + "import org.apache.logging.log4j.spi.AbstractLogger;%n" + + "import org.apache.logging.log4j.spi.ExtendedLogger;%n" + "import org.apache.logging.log4j.spi.ExtendedLoggerWrapper;%n" + "import org.apache.logging.log4j.util.MessageSupplier;%n" + "import org.apache.logging.log4j.util.Supplier;%n" @@ -143,7 +143,7 @@ String constructor() { return "" + "%n" + " private %s(final Logger logger) {%n" - + " super((AbstractLogger) logger, logger.getName(), logger.getMessageFactory());%n" + + " super((ExtendedLogger) logger, logger.getName(), logger.getMessageFactory());%n" + " this.logger = this;%n" + " }%n"; // @formatter:on diff --git a/log4j-csv/src/main/java/org/apache/logging/log4j/csv/layout/CsvLogEventLayout.java b/log4j-csv/src/main/java/org/apache/logging/log4j/csv/layout/CsvLogEventLayout.java index 5f8a440b793..37501158c69 100644 --- a/log4j-csv/src/main/java/org/apache/logging/log4j/csv/layout/CsvLogEventLayout.java +++ b/log4j-csv/src/main/java/org/apache/logging/log4j/csv/layout/CsvLogEventLayout.java @@ -42,7 +42,7 @@ @Plugin public class CsvLogEventLayout extends AbstractCsvLayout { - public static CsvLogEventLayout createDefaultLayout() { + static CsvLogEventLayout createDefaultLayout() { return new CsvLogEventLayout( new DefaultConfiguration(), Charset.forName(DEFAULT_CHARSET), diff --git a/log4j-csv/src/main/java/org/apache/logging/log4j/csv/layout/CsvParameterLayout.java b/log4j-csv/src/main/java/org/apache/logging/log4j/csv/layout/CsvParameterLayout.java index 1270771a93a..484001c4e02 100644 --- a/log4j-csv/src/main/java/org/apache/logging/log4j/csv/layout/CsvParameterLayout.java +++ b/log4j-csv/src/main/java/org/apache/logging/log4j/csv/layout/CsvParameterLayout.java @@ -60,7 +60,7 @@ public static AbstractCsvLayout createDefaultLayout() { null); } - public static AbstractCsvLayout createLayout(final CSVFormat format) { + static AbstractCsvLayout createLayout(final CSVFormat format) { return new CsvParameterLayout(new DefaultConfiguration(), Charset.forName(DEFAULT_CHARSET), format, null, null); } diff --git a/log4j-flume-ng/src/main/java/org/apache/logging/log4j/flume/appender/FlumeAppender.java b/log4j-flume-ng/src/main/java/org/apache/logging/log4j/flume/appender/FlumeAppender.java index 784d411c02c..c393b4bab13 100644 --- a/log4j-flume-ng/src/main/java/org/apache/logging/log4j/flume/appender/FlumeAppender.java +++ b/log4j-flume-ng/src/main/java/org/apache/logging/log4j/flume/appender/FlumeAppender.java @@ -24,7 +24,7 @@ import org.apache.logging.log4j.core.Layout; import org.apache.logging.log4j.core.LogEvent; import org.apache.logging.log4j.core.appender.AbstractAppender; -import org.apache.logging.log4j.core.config.DefaultConfiguration; +import org.apache.logging.log4j.core.config.Configuration; import org.apache.logging.log4j.core.config.Property; import org.apache.logging.log4j.core.layout.Rfc5424Layout; import org.apache.logging.log4j.core.net.Facility; @@ -37,6 +37,7 @@ import org.apache.logging.log4j.plugins.PluginElement; import org.apache.logging.log4j.plugins.PluginFactory; import org.apache.logging.log4j.plugins.di.ConfigurableInstanceFactory; +import org.apache.logging.log4j.plugins.di.Key; /** * An Appender that uses the Avro protocol to route events to Flume. @@ -211,7 +212,7 @@ public static FlumeAppender createAppender( @PluginElement final FlumeEventFactory factory, @PluginElement Layout layout, @PluginElement final Filter filter, - final ConfigurableInstanceFactory instanceFactory) { + final Configuration configuration) { final boolean embed = embedded != null ? Boolean.parseBoolean(embedded) @@ -255,7 +256,7 @@ public static FlumeAppender createAppender( if (layout == null) { final int enterpriseNumber = Rfc5424Layout.DEFAULT_ENTERPRISE_NUMBER; layout = new Rfc5424Layout.Rfc5424LayoutBuilder() - .setConfig(new DefaultConfiguration()) + .setConfig(configuration) .setFacility(Facility.LOCAL0) .setEin(String.valueOf(enterpriseNumber)) .setIncludeMDC(true) @@ -303,7 +304,7 @@ public static FlumeAppender createAppender( delayMillis, lockTimeoutRetryCount, dataDir, - instanceFactory); + configuration.getComponent(Key.forClass(ConfigurableInstanceFactory.class))); break; default: LOGGER.debug("No manager type specified. Defaulting to AVRO"); diff --git a/log4j-flume-ng/src/test/java/org/apache/logging/log4j/flume/appender/FlumeAppenderTest.java b/log4j-flume-ng/src/test/java/org/apache/logging/log4j/flume/appender/FlumeAppenderTest.java index 9571a0d6834..8b24237b7c9 100644 --- a/log4j-flume-ng/src/test/java/org/apache/logging/log4j/flume/appender/FlumeAppenderTest.java +++ b/log4j-flume-ng/src/test/java/org/apache/logging/log4j/flume/appender/FlumeAppenderTest.java @@ -44,10 +44,10 @@ import org.apache.logging.log4j.ThreadContext; import org.apache.logging.log4j.core.Appender; import org.apache.logging.log4j.core.Logger; +import org.apache.logging.log4j.core.config.Configuration; +import org.apache.logging.log4j.core.config.DefaultConfiguration; import org.apache.logging.log4j.core.test.AvailablePortFinder; import org.apache.logging.log4j.message.StructuredDataMessage; -import org.apache.logging.log4j.plugins.di.ConfigurableInstanceFactory; -import org.apache.logging.log4j.plugins.di.DI; import org.apache.logging.log4j.status.StatusLogger; import org.junit.After; import org.junit.Assert; @@ -64,7 +64,7 @@ public class FlumeAppenderTest { private Channel channel; private Logger avroLogger; private String testPort; - private ConfigurableInstanceFactory instanceFactory; + private Configuration configuration; @BeforeClass public static void setupClass() { @@ -73,7 +73,7 @@ public static void setupClass() { @Before public void setUp() throws Exception { - instanceFactory = DI.createInitializedFactory(); + configuration = new DefaultConfiguration(); eventSource = new AvroSource(); channel = new MemoryChannel(); @@ -142,7 +142,7 @@ public void testLog4jAvroAppender() throws IOException { null, null, null, - instanceFactory); + configuration); avroAppender.start(); avroLogger.addAppender(avroAppender); avroLogger.setLevel(Level.ALL); @@ -192,7 +192,7 @@ public void testLog4jAvroAppenderWithHostsParam() throws IOException { null, null, null, - instanceFactory); + configuration); avroAppender.start(); avroLogger.addAppender(avroAppender); avroLogger.setLevel(Level.ALL); @@ -242,7 +242,7 @@ public void testStructured() throws IOException { null, null, null, - instanceFactory); + configuration); avroAppender.start(); final Logger eventLogger = (Logger) LogManager.getLogger("EventLogger"); Assert.assertNotNull(eventLogger); @@ -302,7 +302,7 @@ public void testMultiple() throws IOException { null, null, null, - instanceFactory); + configuration); avroAppender.start(); avroLogger.addAppender(avroAppender); avroLogger.setLevel(Level.ALL); @@ -357,7 +357,7 @@ public void testIncompleteBatch() throws IOException { null, null, null, - instanceFactory); + configuration); avroAppender.start(); avroLogger.addAppender(avroAppender); avroLogger.setLevel(Level.ALL); @@ -418,7 +418,7 @@ public void testIncompleteBatch2() throws IOException { null, null, null, - instanceFactory); + configuration); avroAppender.start(); avroLogger.addAppender(avroAppender); avroLogger.setLevel(Level.ALL); @@ -473,7 +473,7 @@ public void testBatch() throws IOException { null, null, null, - instanceFactory); + configuration); avroAppender.start(); avroLogger.addAppender(avroAppender); avroLogger.setLevel(Level.ALL); @@ -527,7 +527,7 @@ public void testConnectionRefused() { null, null, null, - instanceFactory); + configuration); avroAppender.start(); avroLogger.addAppender(avroAppender); avroLogger.setLevel(Level.ALL); @@ -577,7 +577,7 @@ public void testNotConnected() throws Exception { null, null, null, - instanceFactory); + configuration); avroAppender.start(); Assert.assertTrue("Appender Not started", avroAppender.isStarted()); avroLogger.addAppender(avroAppender); @@ -645,7 +645,7 @@ public void testReconnect() throws Exception { null, null, null, - instanceFactory); + configuration); avroAppender.start(); avroLogger.addAppender(avroAppender); avroLogger.setLevel(Level.ALL); diff --git a/log4j-jndi-test/src/test/java/org/apache/logging/log4j/jndi/lookup/InterpolatorTest.java b/log4j-jndi-test/src/test/java/org/apache/logging/log4j/jndi/lookup/InterpolatorTest.java index 58b68c088ad..8762d0351aa 100644 --- a/log4j-jndi-test/src/test/java/org/apache/logging/log4j/jndi/lookup/InterpolatorTest.java +++ b/log4j-jndi-test/src/test/java/org/apache/logging/log4j/jndi/lookup/InterpolatorTest.java @@ -28,6 +28,8 @@ import java.util.Date; import java.util.HashMap; import java.util.Map; +import java.util.function.Supplier; +import java.util.stream.Collectors; import org.apache.logging.log4j.Level; import org.apache.logging.log4j.ThreadContext; import org.apache.logging.log4j.core.LogEvent; @@ -35,9 +37,13 @@ import org.apache.logging.log4j.core.impl.Log4jLogEvent; import org.apache.logging.log4j.core.lookup.Interpolator; import org.apache.logging.log4j.core.lookup.MapLookup; +import org.apache.logging.log4j.core.lookup.PropertiesLookup; import org.apache.logging.log4j.core.lookup.StrLookup; import org.apache.logging.log4j.jndi.test.junit.JndiRule; import org.apache.logging.log4j.message.StringMapMessage; +import org.apache.logging.log4j.plugins.di.ConfigurableInstanceFactory; +import org.apache.logging.log4j.plugins.di.DI; +import org.apache.logging.log4j.plugins.model.PluginType; import org.junit.ClassRule; import org.junit.Test; import org.junit.rules.ExternalResource; @@ -48,6 +54,14 @@ */ public class InterpolatorTest { + static final ConfigurableInstanceFactory INSTANCE_FACTORY = DI.createInitializedFactory(); + static final Map> LOOKUP_PLUGINS = + INSTANCE_FACTORY.getInstance(StrLookup.PLUGIN_CATEGORY_KEY).stream() + .collect(Collectors.toMap( + PluginType::getKey, + value -> INSTANCE_FACTORY.getFactory( + value.getPluginClass().asSubclass(StrLookup.class)))); + private static final String TESTKEY = "TestKey"; private static final String TESTKEY2 = "TestKey2"; private static final String TESTVAL = "TestValue"; @@ -79,7 +93,7 @@ public void testGetDefaultLookup() { final Map map = new HashMap<>(); map.put(TESTKEY, TESTVAL); final MapLookup defaultLookup = new MapLookup(map); - final Interpolator interpolator = new Interpolator(defaultLookup); + final Interpolator interpolator = new Interpolator(defaultLookup, LOOKUP_PLUGINS); assertEquals(getLookupMap(defaultLookup), getLookupMap((MapLookup) interpolator.getDefaultLookup())); assertSame(defaultLookup, interpolator.getDefaultLookup()); } @@ -100,7 +114,7 @@ private static Map getLookupMap(final MapLookup lookup) { public void testLookup() { final Map map = new HashMap<>(); map.put(TESTKEY, TESTVAL); - final StrLookup lookup = new Interpolator(new MapLookup(map)); + final StrLookup lookup = new Interpolator(new MapLookup(map), LOOKUP_PLUGINS); ThreadContext.put(TESTKEY, TESTVAL); String value = lookup.lookup(TESTKEY); assertEquals(TESTVAL, value); @@ -128,7 +142,7 @@ private void assertLookupNotEmpty(final StrLookup lookup, final String key) { @Test public void testLookupWithDefaultInterpolator() { - final StrLookup lookup = new Interpolator(); + final StrLookup lookup = new Interpolator(new PropertiesLookup(Map.of()), LOOKUP_PLUGINS); String value = lookup.lookup("sys:" + TESTKEY); assertEquals(TESTVAL, value); value = lookup.lookup("env:PATH"); @@ -152,7 +166,7 @@ public void testLookupWithDefaultInterpolator() { public void testInterpolatorMapMessageWithNoPrefix() { final HashMap configProperties = new HashMap<>(); configProperties.put("key", "configProperties"); - final Interpolator interpolator = new Interpolator(configProperties); + final Interpolator interpolator = new Interpolator(new PropertiesLookup(configProperties), LOOKUP_PLUGINS); final HashMap map = new HashMap<>(); map.put("key", "mapMessage"); final LogEvent event = Log4jLogEvent.newBuilder() @@ -166,7 +180,8 @@ public void testInterpolatorMapMessageWithNoPrefix() { @Test public void testInterpolatorMapMessageWithNoPrefixConfigDoesntMatch() { - final Interpolator interpolator = new Interpolator(Collections.emptyMap()); + final Interpolator interpolator = + new Interpolator(new PropertiesLookup(Collections.emptyMap()), LOOKUP_PLUGINS); final HashMap map = new HashMap<>(); map.put("key", "mapMessage"); final LogEvent event = Log4jLogEvent.newBuilder() @@ -182,7 +197,7 @@ public void testInterpolatorMapMessageWithNoPrefixConfigDoesntMatch() { public void testInterpolatorMapMessageWithMapPrefix() { final HashMap configProperties = new HashMap<>(); configProperties.put("key", "configProperties"); - final Interpolator interpolator = new Interpolator(configProperties); + final Interpolator interpolator = new Interpolator(new PropertiesLookup(configProperties), LOOKUP_PLUGINS); final HashMap map = new HashMap<>(); map.put("key", "mapMessage"); final LogEvent event = Log4jLogEvent.newBuilder() diff --git a/log4j-jndi/src/main/java/org/apache/logging/log4j/jndi/selector/JndiContextSelector.java b/log4j-jndi/src/main/java/org/apache/logging/log4j/jndi/selector/JndiContextSelector.java index b23ed36c224..025e78567a4 100644 --- a/log4j-jndi/src/main/java/org/apache/logging/log4j/jndi/selector/JndiContextSelector.java +++ b/log4j-jndi/src/main/java/org/apache/logging/log4j/jndi/selector/JndiContextSelector.java @@ -27,11 +27,15 @@ import javax.naming.NamingException; import org.apache.logging.log4j.core.LoggerContext; import org.apache.logging.log4j.core.impl.ContextAnchor; +import org.apache.logging.log4j.core.selector.AbstractContextSelector; import org.apache.logging.log4j.core.selector.NamedContextSelector; import org.apache.logging.log4j.core.util.Constants; import org.apache.logging.log4j.jndi.JndiManager; +import org.apache.logging.log4j.plugins.Inject; import org.apache.logging.log4j.plugins.Singleton; +import org.apache.logging.log4j.plugins.di.ConfigurableInstanceFactory; import org.apache.logging.log4j.status.StatusLogger; +import org.apache.logging.log4j.util.Lazy; /** * This class can be used to define a custom logger repository. It makes use of the fact that in J2EE environments, each @@ -87,15 +91,18 @@ *

*/ @Singleton -public class JndiContextSelector implements NamedContextSelector { +public class JndiContextSelector extends AbstractContextSelector implements NamedContextSelector { - private static final LoggerContext CONTEXT = new LoggerContext("Default"); + private final Lazy defaultContextLazy = + Lazy.lazy(() -> createContext("Default", null, JndiContextSelector.class.getClassLoader())); - private static final ConcurrentMap CONTEXT_MAP = new ConcurrentHashMap<>(); + private final ConcurrentMap CONTEXT_MAP = new ConcurrentHashMap<>(); private static final StatusLogger LOGGER = StatusLogger.getLogger(); - public JndiContextSelector() { + @Inject + public JndiContextSelector(final ConfigurableInstanceFactory instanceFactory) { + super(instanceFactory); if (!JndiManager.isJndiContextSelectorEnabled()) { throw new IllegalStateException("JNDI must be enabled by setting log4j2.enableJndiContextSelector=true"); } @@ -145,7 +152,9 @@ public LoggerContext getContext( final String loggingContextName = getContextName(); - return loggingContextName == null ? CONTEXT : locateContext(loggingContextName, null, configLocation); + return loggingContextName == null + ? defaultContextLazy.get() + : locateContext(loggingContextName, null, configLocation); } private static String getContextName() { @@ -166,7 +175,7 @@ public LoggerContext locateContext(final String name, final Object externalConte return null; } if (!CONTEXT_MAP.containsKey(name)) { - final LoggerContext ctx = new LoggerContext(name, externalContext, configLocation); + final LoggerContext ctx = createContext(name, configLocation, JndiContextSelector.class.getClassLoader()); CONTEXT_MAP.putIfAbsent(name, ctx); } return CONTEXT_MAP.get(name); diff --git a/log4j-osgi-test/src/test/java/org/apache/logging/log4j/osgi/tests/CustomConfigurationFactory.java b/log4j-osgi-test/src/test/java/org/apache/logging/log4j/osgi/tests/CustomConfigurationFactory.java index e58d2c81ed5..6ce34c37bc8 100644 --- a/log4j-osgi-test/src/test/java/org/apache/logging/log4j/osgi/tests/CustomConfigurationFactory.java +++ b/log4j-osgi-test/src/test/java/org/apache/logging/log4j/osgi/tests/CustomConfigurationFactory.java @@ -59,7 +59,7 @@ public Configuration getConfiguration( * @return An array of File extensions. */ @Override - public String[] getSupportedTypes() { + protected String[] getSupportedTypes() { return SUFFIXES; } } diff --git a/log4j-plugins-test/src/test/java/org/apache/logging/log4j/plugins/condition/OnPresentBindingsConditionTest.java b/log4j-plugins-test/src/test/java/org/apache/logging/log4j/plugins/condition/OnPresentBindingsConditionTest.java new file mode 100644 index 00000000000..6bb52d6778d --- /dev/null +++ b/log4j-plugins-test/src/test/java/org/apache/logging/log4j/plugins/condition/OnPresentBindingsConditionTest.java @@ -0,0 +1,71 @@ +/* + * 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.plugins.condition; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.apache.logging.log4j.plugins.Factory; +import org.apache.logging.log4j.plugins.di.ConfigurableInstanceFactory; +import org.apache.logging.log4j.plugins.di.DI; +import org.apache.logging.log4j.plugins.di.Key; +import org.junit.jupiter.api.Test; + +class OnPresentBindingsConditionTest { + + static class Dependency { + + Dependency() {} + } + + static class Dependent { + final Dependency dependency; + + Dependent(final Dependency dependency) { + this.dependency = dependency; + } + } + + static class DependencyFactory { + @Factory + Dependency defaultDependency() { + return new Dependency(); + } + } + + static class DependentFactory { + @Factory + @ConditionalOnPresentBindings(bindings = Dependency.class) + Dependent defaultDependent(final Dependency dependency) { + return new Dependent(dependency); + } + } + + final ConfigurableInstanceFactory instanceFactory = DI.createInitializedFactory(); + + @Test + void when_dependency_present_dependent_present() { + instanceFactory.registerBundle(DependencyFactory.class); + instanceFactory.registerBundle(DependentFactory.class); + assertThat(instanceFactory.hasBinding(Key.forClass(Dependent.class))).isTrue(); + } + + @Test + void when_dependency_absent_dependent_absent() { + instanceFactory.registerBundle(DependentFactory.class); + assertThat(instanceFactory.hasBinding(Key.forClass(Dependent.class))).isFalse(); + } +} diff --git a/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/condition/ConditionalOnPresentBindings.java b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/condition/ConditionalOnPresentBindings.java new file mode 100644 index 00000000000..c09f19ec3ea --- /dev/null +++ b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/condition/ConditionalOnPresentBindings.java @@ -0,0 +1,38 @@ +/* + * 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.plugins.condition; + +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; + +/** + * Only creates a binding if all the requested dependencies exist. + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.METHOD) +@Documented +@Conditional(OnPresentBindingsCondition.class) +public @interface ConditionalOnPresentBindings { + + /** + * @return The set of classes that must have a binding. + */ + Class[] bindings(); +} diff --git a/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/condition/OnPresentBindingsCondition.java b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/condition/OnPresentBindingsCondition.java new file mode 100644 index 00000000000..396beeb027c --- /dev/null +++ b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/condition/OnPresentBindingsCondition.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.plugins.condition; + +import java.lang.reflect.AnnotatedElement; +import org.apache.logging.log4j.plugins.di.InstanceFactory; +import org.apache.logging.log4j.plugins.di.Key; + +public class OnPresentBindingsCondition implements Condition { + @Override + public boolean matches(final ConditionContext context, final AnnotatedElement element) { + final InstanceFactory instanceFactory = context.getInstanceFactory(); + for (final Class requiredClass : + element.getAnnotation(ConditionalOnPresentBindings.class).bindings()) { + if (!instanceFactory.hasBinding(Key.forClass(requiredClass))) { + return false; + } + } + return true; + } +} diff --git a/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/di/ConfigurableInstanceFactory.java b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/di/ConfigurableInstanceFactory.java index fd165fa0912..60a0316a693 100644 --- a/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/di/ConfigurableInstanceFactory.java +++ b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/di/ConfigurableInstanceFactory.java @@ -33,10 +33,11 @@ import org.apache.logging.log4j.plugins.validation.ConstraintValidationException; import org.apache.logging.log4j.plugins.validation.ConstraintValidator; import org.apache.logging.log4j.util.Cast; +import org.apache.logging.log4j.util.PropertyEnvironment; /** * Configuration manager for the state of an instance factory. Configurable instance factories can form a - * hierarchy by {@linkplain #newChildInstanceFactory() creating children factories} to enable inheritance + * hierarchy by {@linkplain #newChildInstanceFactory} creating children factories} to enable inheritance * of bindings, scopes, factory resolvers, instance post-processors, and reflection agents. */ public interface ConfigurableInstanceFactory extends InstanceFactory { @@ -131,6 +132,15 @@ default void registerBundles(final Object... bundles) { */ ConfigurableInstanceFactory newChildInstanceFactory(); + /** + * Creates a new child instance factory from this factory which uses bindings from this factory as fallback + * bindings. + * + * @return new child instance factory + */ + ConfigurableInstanceFactory newChildInstanceFactory( + Supplier environment, Supplier loader); + /** * Sets the {@link ReflectionAgent} used for invoking {@link java.lang.reflect.AccessibleObject#setAccessible(boolean)} * from an appropriate caller class. Customizing this allows for changing the base module that other modules should diff --git a/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/di/DefaultInstanceFactory.java b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/di/DefaultInstanceFactory.java index f0d95ba9bc5..1a7ddb90e87 100644 --- a/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/di/DefaultInstanceFactory.java +++ b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/di/DefaultInstanceFactory.java @@ -73,15 +73,26 @@ public class DefaultInstanceFactory implements ConfigurableInstanceFactory { private ReflectionAgent agent = object -> object.setAccessible(true); protected DefaultInstanceFactory() { - this(BindingMap.newRootMap(), HierarchicalMap.newRootMap(), new ArrayList<>(), List.of()); + this( + BindingMap.newRootMap(), + HierarchicalMap.newRootMap(), + new ArrayList<>(), + List.of(), + PropertiesUtil::getProperties, + LoaderUtil::getClassLoader); } - protected DefaultInstanceFactory(final DefaultInstanceFactory parent) { + private DefaultInstanceFactory( + final DefaultInstanceFactory parent, + final Supplier environment, + final Supplier loader) { this( parent.bindings.newChildMap(), parent.scopes.newChildMap(), parent.factoryResolvers, - parent.instancePostProcessors); + parent.instancePostProcessors, + environment, + loader); this.agent = parent.agent; } @@ -89,7 +100,9 @@ private DefaultInstanceFactory( final BindingMap bindings, final HierarchicalMap, Scope> scopes, final List> factoryResolvers, - final Collection instancePostProcessors) { + final Collection instancePostProcessors, + final Supplier environment, + final Supplier loader) { this.bindings = bindings; this.scopes = scopes; this.factoryResolvers = factoryResolvers; @@ -97,8 +110,8 @@ private DefaultInstanceFactory( this.bindings.put(InjectionPoint.CURRENT_INJECTION_POINT, currentInjectionPoint::get); this.bindings.put(Key.forClass(ConfigurableInstanceFactory.class), () -> this); this.bindings.put(Key.forClass(InstanceFactory.class), () -> this); - this.bindings.put(Key.forClass(PropertyEnvironment.class), PropertiesUtil::getProperties); - this.bindings.put(Key.forClass(ClassLoader.class), LoaderUtil::getClassLoader); + this.bindings.put(Key.forClass(PropertyEnvironment.class), environment); + this.bindings.put(Key.forClass(ClassLoader.class), loader); } @Override @@ -336,7 +349,16 @@ public void registerInstancePostProcessor(final InstancePostProcessor instancePo @Override public ConfigurableInstanceFactory newChildInstanceFactory() { - return new DefaultInstanceFactory(this); + return new DefaultInstanceFactory( + this, + bindings.get(Key.forClass(PropertyEnvironment.class), List.of()), + bindings.get(Key.forClass(ClassLoader.class), List.of())); + } + + @Override + public ConfigurableInstanceFactory newChildInstanceFactory( + final Supplier environment, final Supplier loader) { + return new DefaultInstanceFactory(this, environment, loader); } @Override