diff --git a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/async/AsyncLoggerConfigTest.java b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/async/AsyncLoggerConfigTest.java index f168565ecb9..3dba066194f 100644 --- a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/async/AsyncLoggerConfigTest.java +++ b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/async/AsyncLoggerConfigTest.java @@ -101,12 +101,14 @@ public void testSingleFilterInvocation() { .setLevel(Level.INFO) .setFilter(filter) .build(); + config.initialize(); final Appender appender = mock(Appender.class); when(appender.isStarted()).thenReturn(true); when(appender.getName()).thenReturn("test"); config.addAppender(appender, null, null); + final DisruptorConfiguration disruptorConfig = configuration.getExtension(DisruptorConfiguration.class); final AsyncLoggerConfigDisruptor disruptor = - (AsyncLoggerConfigDisruptor) configuration.getAsyncLoggerConfigDelegate(); + (AsyncLoggerConfigDisruptor) disruptorConfig.getAsyncLoggerConfigDelegate(); disruptor.start(); try { config.log(FQCN, FQCN, null, Level.INFO, new SimpleMessage(), null); diff --git a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/async/AsyncWaitStrategyFactoryIncorrectConfigGlobalLoggersTest.java b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/async/DisruptorConfigurationInvalidTest.java similarity index 59% rename from log4j-core-test/src/test/java/org/apache/logging/log4j/core/async/AsyncWaitStrategyFactoryIncorrectConfigGlobalLoggersTest.java rename to log4j-core-test/src/test/java/org/apache/logging/log4j/core/async/DisruptorConfigurationInvalidTest.java index bc2acaf5eab..aec3757813b 100644 --- a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/async/AsyncWaitStrategyFactoryIncorrectConfigGlobalLoggersTest.java +++ b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/async/DisruptorConfigurationInvalidTest.java @@ -16,40 +16,34 @@ */ package org.apache.logging.log4j.core.async; +import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.assertTrue; -import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.core.LoggerContext; -import org.apache.logging.log4j.core.impl.Log4jPropertyKey; import org.apache.logging.log4j.core.test.junit.ContextSelectorType; +import org.apache.logging.log4j.core.test.junit.LoggerContextSource; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; -import org.junitpioneer.jupiter.SetSystemProperty; @Tag("async") @ContextSelectorType(AsyncLoggerContextSelector.class) -@SetSystemProperty( - key = Log4jPropertyKey.Constant.CONFIG_LOCATION, - value = "AsyncWaitStrategyIncorrectFactoryConfigGlobalLoggerTest.xml") -public class AsyncWaitStrategyFactoryIncorrectConfigGlobalLoggersTest { +@LoggerContextSource +public class DisruptorConfigurationInvalidTest { @Test - public void testIncorrectConfigWaitStrategyFactory() throws Exception { - final LoggerContext context = (LoggerContext) LogManager.getContext(false); - assertTrue(context instanceof AsyncLoggerContext, "context is AsyncLoggerContext"); + public void testIncorrectConfigWaitStrategyFactory(final LoggerContext context) { + assertThat(context).isInstanceOf(AsyncLoggerContext.class); - final AsyncWaitStrategyFactory asyncWaitStrategyFactory = - context.getConfiguration().getAsyncWaitStrategyFactory(); - assertNull(asyncWaitStrategyFactory); + final DisruptorConfiguration disruptorConfig = + context.getConfiguration().getExtension(DisruptorConfiguration.class); + assertThat(disruptorConfig).isNotNull(); + final AsyncWaitStrategyFactory asyncWaitStrategyFactory = disruptorConfig.getWaitStrategyFactory(); + assertThat(asyncWaitStrategyFactory).isNull(); final AsyncLogger logger = (AsyncLogger) context.getRootLogger(); final AsyncLoggerDisruptor delegate = logger.getAsyncLoggerDisruptor(); assertEquals( TimeoutBlockingWaitStrategy.class, delegate.getWaitStrategy().getClass()); - assertTrue( - delegate.getWaitStrategy() instanceof TimeoutBlockingWaitStrategy, - "waitstrategy is TimeoutBlockingWaitStrategy"); + assertThat(delegate.getWaitStrategy()).isInstanceOf(TimeoutBlockingWaitStrategy.class); } } diff --git a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/async/AsyncWaitStrategyFactoryConfigTest.java b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/async/DisruptorConfigurationTest.java similarity index 80% rename from log4j-core-test/src/test/java/org/apache/logging/log4j/core/async/AsyncWaitStrategyFactoryConfigTest.java rename to log4j-core-test/src/test/java/org/apache/logging/log4j/core/async/DisruptorConfigurationTest.java index 914236c0a70..16dedf40372 100644 --- a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/async/AsyncWaitStrategyFactoryConfigTest.java +++ b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/async/DisruptorConfigurationTest.java @@ -28,19 +28,29 @@ import org.junit.jupiter.api.Test; @Tag("async") -public class AsyncWaitStrategyFactoryConfigTest { +public class DisruptorConfigurationTest { @Test - @LoggerContextSource("AsyncWaitStrategyFactoryConfigTest.xml") + void testAttributePriority() { + final DisruptorConfiguration disruptorConfig = DisruptorConfiguration.newBuilder() + .setFactoryClassName(DefaultAsyncWaitStrategyFactory.class.getName()) + .setWaitFactory(YieldingWaitStrategyFactory.class.getName()) + .build(); + assertThat(disruptorConfig.getWaitStrategyFactory()).isInstanceOf(YieldingWaitStrategyFactory.class); + } + + @Test + @LoggerContextSource public void testConfigWaitStrategyFactory(final LoggerContext context) throws Exception { - final AsyncWaitStrategyFactory asyncWaitStrategyFactory = - context.getConfiguration().getAsyncWaitStrategyFactory(); + final DisruptorConfiguration disruptorConfig = + context.getConfiguration().getExtension(DisruptorConfiguration.class); + final AsyncWaitStrategyFactory asyncWaitStrategyFactory = disruptorConfig.getWaitStrategyFactory(); assertThat(asyncWaitStrategyFactory.getClass()).isEqualTo(YieldingWaitStrategyFactory.class); assertThat(asyncWaitStrategyFactory).isInstanceOf(YieldingWaitStrategyFactory.class); } @Test - @LoggerContextSource("AsyncWaitStrategyFactoryConfigTest.xml") + @LoggerContextSource public void testWaitStrategy(final LoggerContext context) throws Exception { final org.apache.logging.log4j.Logger logger = context.getRootLogger(); @@ -56,8 +66,9 @@ public void testWaitStrategy(final LoggerContext context) throws Exception { @Test @LoggerContextSource("AsyncWaitStrategyIncorrectFactoryConfigTest.xml") public void testIncorrectConfigWaitStrategyFactory(final LoggerContext context) throws Exception { - final AsyncWaitStrategyFactory asyncWaitStrategyFactory = - context.getConfiguration().getAsyncWaitStrategyFactory(); + final DisruptorConfiguration disruptorConfig = + context.getConfiguration().getExtension(DisruptorConfiguration.class); + final AsyncWaitStrategyFactory asyncWaitStrategyFactory = disruptorConfig.getWaitStrategyFactory(); assertThat(asyncWaitStrategyFactory).isNull(); // because invalid configuration } diff --git a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/async/QueueFullAbstractTest.java b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/async/QueueFullAbstractTest.java index 54da28a5db2..2dc85a0974f 100644 --- a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/async/QueueFullAbstractTest.java +++ b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/async/QueueFullAbstractTest.java @@ -294,7 +294,9 @@ protected static void assertAsyncLoggerConfig(final LoggerContext ctx, final int final Configuration config = ctx.getConfiguration(); assertThat(config).isNotNull(); assertThat(config.getRootLogger()).isInstanceOf(AsyncLoggerConfig.class); - final AsyncLoggerConfigDisruptor disruptor = (AsyncLoggerConfigDisruptor) config.getAsyncLoggerConfigDelegate(); + final DisruptorConfiguration disruptorConfig = config.getExtension(DisruptorConfiguration.class); + final AsyncLoggerConfigDisruptor disruptor = + (AsyncLoggerConfigDisruptor) disruptorConfig.getAsyncLoggerConfigDelegate(); assertThat(disruptor.getRingBuffer().getBufferSize()).isEqualTo(expectedBufferSize); } diff --git a/log4j-core-test/src/test/resources/AsyncWaitStrategyIncorrectFactoryConfigGlobalLoggerTest.xml b/log4j-core-test/src/test/resources/org/apache/logging/log4j/core/async/DisruptorConfigurationInvalidTest.xml similarity index 93% rename from log4j-core-test/src/test/resources/AsyncWaitStrategyIncorrectFactoryConfigGlobalLoggerTest.xml rename to log4j-core-test/src/test/resources/org/apache/logging/log4j/core/async/DisruptorConfigurationInvalidTest.xml index 28ece5ffa93..d698dbf7425 100644 --- a/log4j-core-test/src/test/resources/AsyncWaitStrategyIncorrectFactoryConfigGlobalLoggerTest.xml +++ b/log4j-core-test/src/test/resources/org/apache/logging/log4j/core/async/DisruptorConfigurationInvalidTest.xml @@ -15,7 +15,7 @@ ~ See the License for the specific language governing permissions and ~ limitations under the License. --> - + diff --git a/log4j-core-test/src/test/resources/AsyncWaitStrategyFactoryConfigTest.xml b/log4j-core-test/src/test/resources/org/apache/logging/log4j/core/async/DisruptorConfigurationTest.xml similarity index 86% rename from log4j-core-test/src/test/resources/AsyncWaitStrategyFactoryConfigTest.xml rename to log4j-core-test/src/test/resources/org/apache/logging/log4j/core/async/DisruptorConfigurationTest.xml index 5f82233848d..f52b0d58c3b 100644 --- a/log4j-core-test/src/test/resources/AsyncWaitStrategyFactoryConfigTest.xml +++ b/log4j-core-test/src/test/resources/org/apache/logging/log4j/core/async/DisruptorConfigurationTest.xml @@ -15,11 +15,7 @@ ~ See the License for the specific language governing permissions and ~ limitations under the License. --> - - - - + @@ -29,4 +25,7 @@ + + diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/async/AsyncLoggerConfig.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/async/AsyncLoggerConfig.java index 73920e0cd8c..83295de6350 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/async/AsyncLoggerConfig.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/async/AsyncLoggerConfig.java @@ -65,7 +65,7 @@ public class AsyncLoggerConfig extends LoggerConfig { private static final ThreadLocal ASYNC_LOGGER_ENTERED = ThreadLocal.withInitial(() -> Boolean.FALSE); - private final AsyncLoggerConfigDelegate delegate; + private AsyncLoggerConfigDelegate delegate; @PluginFactory public static > B newAsyncBuilder() { @@ -103,8 +103,17 @@ protected AsyncLoggerConfig( final boolean includeLocation, final LogEventFactory logEventFactory) { super(name, appenders, filter, level, additive, properties, config, includeLocation, logEventFactory); - delegate = config.getAsyncLoggerConfigDelegate(); + } + + @Override + public void initialize() { + final Configuration configuration = getConfiguration(); + final DisruptorConfiguration disruptorConfig = configuration.addExtensionIfAbsent( + DisruptorConfiguration.class, + () -> DisruptorConfiguration.newBuilder().build()); + delegate = disruptorConfig.getAsyncLoggerConfigDelegate(); delegate.setLogEventFactory(getLogEventFactory()); + super.initialize(); } protected void log(final LogEvent event, final LoggerConfigPredicate predicate) { diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/async/AsyncLoggerContext.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/async/AsyncLoggerContext.java index 0125c4e86ee..12cd555b7f5 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/async/AsyncLoggerContext.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/async/AsyncLoggerContext.java @@ -35,20 +35,17 @@ public class AsyncLoggerContext extends LoggerContext { public AsyncLoggerContext(final String name) { super(name); - loggerDisruptor = - new AsyncLoggerDisruptor(name, () -> getConfiguration().getAsyncWaitStrategyFactory()); + loggerDisruptor = new AsyncLoggerDisruptor(name, this::createAsyncWaitStrategyFactory); } public AsyncLoggerContext(final String name, final Object externalContext) { super(name, externalContext); - loggerDisruptor = - new AsyncLoggerDisruptor(name, () -> getConfiguration().getAsyncWaitStrategyFactory()); + 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, () -> getConfiguration().getAsyncWaitStrategyFactory()); + loggerDisruptor = new AsyncLoggerDisruptor(name, this::createAsyncWaitStrategyFactory); } public AsyncLoggerContext( @@ -57,14 +54,12 @@ public AsyncLoggerContext( final URI configLocn, final ConfigurableInstanceFactory instanceFactory) { super(name, externalContext, configLocn, instanceFactory); - loggerDisruptor = - new AsyncLoggerDisruptor(name, () -> getConfiguration().getAsyncWaitStrategyFactory()); + 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, () -> getConfiguration().getAsyncWaitStrategyFactory()); + loggerDisruptor = new AsyncLoggerDisruptor(name, this::createAsyncWaitStrategyFactory); } public AsyncLoggerContext( @@ -73,8 +68,13 @@ public AsyncLoggerContext( final String configLocn, final ConfigurableInstanceFactory instanceFactory) { super(name, externalContext, configLocn, instanceFactory); - loggerDisruptor = - new AsyncLoggerDisruptor(name, () -> getConfiguration().getAsyncWaitStrategyFactory()); + loggerDisruptor = new AsyncLoggerDisruptor(name, this::createAsyncWaitStrategyFactory); + } + + private AsyncWaitStrategyFactory createAsyncWaitStrategyFactory() { + final DisruptorConfiguration disruptorConfiguration = + getConfiguration().getExtension(DisruptorConfiguration.class); + return disruptorConfiguration != null ? disruptorConfiguration.getWaitStrategyFactory() : null; } @Override diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/async/AsyncWaitStrategyFactoryConfig.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/async/AsyncWaitStrategyFactoryConfig.java deleted file mode 100644 index e27090d5577..00000000000 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/async/AsyncWaitStrategyFactoryConfig.java +++ /dev/null @@ -1,102 +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.async; - -import java.util.Objects; -import org.apache.logging.log4j.Logger; -import org.apache.logging.log4j.plugins.Configurable; -import org.apache.logging.log4j.plugins.Plugin; -import org.apache.logging.log4j.plugins.PluginBuilderAttribute; -import org.apache.logging.log4j.plugins.PluginFactory; -import org.apache.logging.log4j.plugins.validation.constraints.Required; -import org.apache.logging.log4j.status.StatusLogger; -import org.apache.logging.log4j.util.Cast; -import org.apache.logging.log4j.util.LoaderUtil; - -/** - * This class allows users to configure the factory used to create - * an instance of the LMAX disruptor WaitStrategy - * used by Async Loggers in the log4j configuration. - */ -@Configurable(printObject = true) -@Plugin("AsyncWaitStrategyFactory") -public class AsyncWaitStrategyFactoryConfig { - - /** - * Status logger for internal logging. - */ - protected static final Logger LOGGER = StatusLogger.getLogger(); - - private final String factoryClassName; - - public AsyncWaitStrategyFactoryConfig(final String factoryClassName) { - this.factoryClassName = Objects.requireNonNull(factoryClassName, "factoryClassName"); - } - - @PluginFactory - public static > B newBuilder() { - return new AsyncWaitStrategyFactoryConfig.Builder().asBuilder(); - } - - /** - * Builds AsyncWaitStrategyFactoryConfig instances. - * - * @param - * The type to build - */ - public static class Builder> - implements org.apache.logging.log4j.plugins.util.Builder { - - @PluginBuilderAttribute("class") - @Required(message = "AsyncWaitStrategyFactory cannot be configured without a factory class name") - private String factoryClassName; - - public String getFactoryClassName() { - return factoryClassName; - } - - public B setFactoryClassName(final String className) { - this.factoryClassName = className; - return asBuilder(); - } - - @Override - public AsyncWaitStrategyFactoryConfig build() { - return new AsyncWaitStrategyFactoryConfig(factoryClassName); - } - - public B asBuilder() { - return Cast.cast(this); - } - } - - public AsyncWaitStrategyFactory createWaitStrategyFactory() { - try { - return LoaderUtil.newCheckedInstanceOf(factoryClassName, AsyncWaitStrategyFactory.class); - } catch (final ClassCastException e) { - LOGGER.error("Ignoring factory '{}': it is not assignable to AsyncWaitStrategyFactory", factoryClassName); - return null; - } catch (ReflectiveOperationException | LinkageError e) { - LOGGER.info( - "Invalid implementation class name value: error creating AsyncWaitStrategyFactory {}: {}", - factoryClassName, - e.getMessage(), - e); - return null; - } - } -} diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/async/DisruptorConfiguration.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/async/DisruptorConfiguration.java new file mode 100644 index 00000000000..035aa4c445d --- /dev/null +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/async/DisruptorConfiguration.java @@ -0,0 +1,126 @@ +/* + * 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.async; + +import java.util.Objects; +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.ConfigurationExtension; +import org.apache.logging.log4j.plugins.Configurable; +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.status.StatusLogger; +import org.apache.logging.log4j.util.Lazy; +import org.apache.logging.log4j.util.LoaderUtil; + +@Configurable(printObject = true) +@Plugin("Disruptor") +@PluginAliases("AsyncWaitStrategyFactory") +public final class DisruptorConfiguration extends AbstractLifeCycle implements ConfigurationExtension { + + private static final Logger LOGGER = StatusLogger.getLogger(); + + private final AsyncWaitStrategyFactory waitStrategyFactory; + private final Lazy loggerConfigDisruptor = + Lazy.lazy(() -> new AsyncLoggerConfigDisruptor(getWaitStrategyFactory())); + + private DisruptorConfiguration(final AsyncWaitStrategyFactory waitStrategyFactory) { + this.waitStrategyFactory = waitStrategyFactory; + } + + public AsyncWaitStrategyFactory getWaitStrategyFactory() { + return waitStrategyFactory; + } + + public AsyncLoggerConfigDelegate getAsyncLoggerConfigDelegate() { + return loggerConfigDisruptor.get(); + } + + @Override + public void start() { + if (loggerConfigDisruptor.isInitialized()) { + 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); + } + return super.stop(timeout, timeUnit); + } + + @PluginFactory + public static Builder newBuilder() { + return new Builder(); + } + + public static final class Builder implements org.apache.logging.log4j.plugins.util.Builder { + + @PluginBuilderAttribute("class") + private String factoryClassName; + + @PluginBuilderAttribute + private String waitFactory; + + public Builder setFactoryClassName(final String factoryClassName) { + this.factoryClassName = factoryClassName; + return this; + } + + public Builder setWaitFactory(final String waitFactory) { + this.waitFactory = waitFactory; + return this; + } + + @Override + public DisruptorConfiguration build() { + return new DisruptorConfiguration( + createWaitStrategyFactory(Objects.toString(waitFactory, factoryClassName))); + } + + private static AsyncWaitStrategyFactory createWaitStrategyFactory(final String factoryClassName) { + if (factoryClassName != null) { + try { + final AsyncWaitStrategyFactory asyncWaitStrategyFactory = + LoaderUtil.newCheckedInstanceOf(factoryClassName, AsyncWaitStrategyFactory.class); + LOGGER.info("Using configured AsyncWaitStrategy factory {}.", factoryClassName); + return asyncWaitStrategyFactory; + } catch (final ClassCastException e) { + LOGGER.error( + "Ignoring factory '{}': it is not assignable to AsyncWaitStrategyFactory", + factoryClassName); + } catch (final ReflectiveOperationException | LinkageError e) { + LOGGER.warn( + "Invalid implementation class name value: error creating AsyncWaitStrategyFactory {}: {}", + factoryClassName, + e.getMessage(), + e); + } + } + LOGGER.info("Using default AsyncWaitStrategy factory."); + return null; + } + } +} 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 27a62f9d40f..189a1b8e5c9 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 @@ -21,7 +21,6 @@ import aQute.bnd.annotation.spi.ServiceConsumer; import java.lang.ref.WeakReference; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collections; import java.util.HashSet; import java.util.List; @@ -42,16 +41,12 @@ import org.apache.logging.log4j.core.Core; import org.apache.logging.log4j.core.Filter; import org.apache.logging.log4j.core.Layout; +import org.apache.logging.log4j.core.LifeCycle; import org.apache.logging.log4j.core.LogEvent; import org.apache.logging.log4j.core.LoggerContext; import org.apache.logging.log4j.core.Version; import org.apache.logging.log4j.core.appender.AsyncAppender; import org.apache.logging.log4j.core.appender.ConsoleAppender; -import org.apache.logging.log4j.core.async.AsyncLoggerConfig; -import org.apache.logging.log4j.core.async.AsyncLoggerConfigDelegate; -import org.apache.logging.log4j.core.async.AsyncLoggerConfigDisruptor; -import org.apache.logging.log4j.core.async.AsyncWaitStrategyFactory; -import org.apache.logging.log4j.core.async.AsyncWaitStrategyFactoryConfig; import org.apache.logging.log4j.core.config.arbiters.Arbiter; import org.apache.logging.log4j.core.config.arbiters.SelectArbiter; import org.apache.logging.log4j.core.filter.AbstractFilterable; @@ -95,6 +90,9 @@ @ServiceConsumer(value = ScriptManagerFactory.class, cardinality = Cardinality.SINGLE, resolution = Resolution.OPTIONAL) public abstract class AbstractConfiguration extends AbstractFilterable implements Configuration { + private static final List EXPECTED_ELEMENTS = + List.of("\"Appenders\"", "\"Loggers\"", "\"Properties\"", "\"Scripts\"", "\"CustomLevels\""); + /** * The instance factory for this configuration. This may be a child factory to a LoggerContext * in most cases, though this might be a root level factory for null configurations. @@ -158,11 +156,10 @@ public abstract class AbstractConfiguration extends AbstractFilterable implement private final ConfigurationSource configurationSource; private final ConfigurationScheduler configurationScheduler; private final WatchManager watchManager; - private AsyncLoggerConfigDisruptor asyncLoggerConfigDisruptor; - private AsyncWaitStrategyFactory asyncWaitStrategyFactory; private final WeakReference loggerContext; private final PropertyEnvironment contextProperties; private final Lock configLock = new ReentrantLock(); + private final List extensions = new CopyOnWriteArrayList<>(); /** * Constructor. @@ -245,21 +242,6 @@ public Node getRootNode() { return rootNode; } - @Override - public AsyncLoggerConfigDelegate getAsyncLoggerConfigDelegate() { - // lazily instantiate only when requested by AsyncLoggers: - // loading AsyncLoggerConfigDisruptor requires LMAX Disruptor jar on classpath - if (asyncLoggerConfigDisruptor == null) { - asyncLoggerConfigDisruptor = new AsyncLoggerConfigDisruptor(asyncWaitStrategyFactory); - } - return asyncLoggerConfigDisruptor; - } - - @Override - public AsyncWaitStrategyFactory getAsyncWaitStrategyFactory() { - return asyncWaitStrategyFactory; - } - /** * Initialize the configuration. */ @@ -358,8 +340,10 @@ public void start() { if (watchManager.getIntervalSeconds() >= 0) { watchManager.start(); } - if (hasAsyncLoggers()) { - asyncLoggerConfigDisruptor.start(); + for (final ConfigurationExtension extension : extensions) { + if (extension instanceof LifeCycle lifecycle) { + lifecycle.start(); + } } final Set alreadyStarted = new HashSet<>(); for (final LoggerConfig logger : loggerConfigs.values()) { @@ -376,18 +360,6 @@ public void start() { LOGGER.debug("Started configuration {} OK.", this); } - private boolean hasAsyncLoggers() { - if (root instanceof AsyncLoggerConfig) { - return true; - } - for (final LoggerConfig logger : loggerConfigs.values()) { - if (logger instanceof AsyncLoggerConfig) { - return true; - } - } - return false; - } - /** * Tear down the configuration. */ @@ -430,9 +402,10 @@ public boolean stop(final long timeout, final TimeUnit timeUnit) { root.stop(timeout, timeUnit); } - if (hasAsyncLoggers()) { - LOGGER.trace("{} stopping AsyncLoggerConfigDisruptor.", cls); - asyncLoggerConfigDisruptor.stop(timeout, timeUnit); + for (final ConfigurationExtension extension : extensions) { + if (extension instanceof LifeCycle lifecycle) { + lifecycle.stop(timeout, timeUnit); + } } LOGGER.trace("{} notifying ReliabilityStrategies that appenders will be stopped.", cls); @@ -686,7 +659,7 @@ protected void doConfigure() { if (!hasProperties) { final Map map = this.getComponent(CONTEXT_PROPERTIES); final StrLookup lookup = map == null ? null : new PropertiesLookup(map); - Interpolator interpolator = interpolatorFactory.newInterpolator(lookup); + final Interpolator interpolator = interpolatorFactory.newInterpolator(lookup); instanceFactory.injectMembers(interpolator); runtimeStrSubstitutor.setVariableResolver(interpolator); configurationStrSubstitutor.setVariableResolver(interpolator); @@ -703,6 +676,10 @@ protected void doConfigure() { } createConfiguration(child, null); if (child.getObject() == null) { + LOGGER.warn( + "Configuration element \"{}\" is ignored: try nesting it inside one of: {}.", + child.getName(), + EXPECTED_ELEMENTS); continue; } if ("Scripts".equalsIgnoreCase(child.getName())) { @@ -713,40 +690,28 @@ protected void doConfigure() { appenders = child.getObject(); } else if (child.isInstanceOf(Filter.class)) { addFilter(child.getObject(Filter.class)); - } else if ("Loggers".equalsIgnoreCase(child.getName())) { - final Loggers l = child.getObject(); + } else if (child.isInstanceOf(Loggers.class)) { + final Loggers l = child.getObject(Loggers.class); loggerConfigs = l.getMap(); setLoggers = true; if (l.getRoot() != null) { root = l.getRoot(); setRoot = true; } - } else if ("CustomLevels".equalsIgnoreCase(child.getName())) { - final CustomLevels levels = child.getObject(CustomLevels.class); - if (levels == null) { - LOGGER.error("Unable to load CustomLevels plugin"); - } else { - customLevels = levels.getCustomLevels(); - } + } else if (child.isInstanceOf(CustomLevels.class)) { + customLevels = child.getObject(CustomLevels.class).getCustomLevels(); } else if (child.isInstanceOf(CustomLevelConfig.class)) { final List copy = new ArrayList<>(customLevels); copy.add(child.getObject(CustomLevelConfig.class)); customLevels = copy; - } else if (child.isInstanceOf(AsyncWaitStrategyFactoryConfig.class)) { - AsyncWaitStrategyFactoryConfig awsfc = child.getObject(AsyncWaitStrategyFactoryConfig.class); - if (awsfc == null) { - LOGGER.error("Unable to load AsyncWaitStrategyFactoryConfig"); - } else { - asyncWaitStrategyFactory = awsfc.createWaitStrategyFactory(); - } + } else if (child.isInstanceOf(ConfigurationExtension.class)) { + addExtension(child.getObject(ConfigurationExtension.class)); } else { - final List expected = Arrays.asList( - "\"Appenders\"", "\"Loggers\"", "\"Properties\"", "\"Scripts\"", "\"CustomLevels\""); LOGGER.error( "Unknown object \"{}\" of type {} is ignored: try nesting it inside one of: {}.", child.getName(), child.getObject().getClass().getName(), - expected); + EXPECTED_ELEMENTS); } } @@ -772,6 +737,7 @@ protected void doConfigure() { "Unable to locate appender \"{}\" for logger config \"{}\"", ref.getRef(), loggerConfig); } } + loggerConfig.initialize(); } setParents(); @@ -1178,4 +1144,37 @@ public NanoClock getNanoClock() { public void setNanoClock(final NanoClock nanoClock) { instanceFactory.registerBinding(NanoClock.KEY, () -> nanoClock); } + + @Override + public T addExtensionIfAbsent( + final Class extensionType, final Supplier supplier) { + for (final ConfigurationExtension extension : extensions) { + if (extensionType.isInstance(extension)) { + return extensionType.cast(extension); + } + } + return addExtension(supplier.get()); + } + + private T addExtension(final T extension) { + extensions.add(Objects.requireNonNull(extension)); + return extension; + } + + @Override + public T getExtension(final Class extensionType) { + T result = null; + for (final ConfigurationExtension extension : extensions) { + if (extensionType.isInstance(extension)) { + if (result == null) { + result = extensionType.cast(extension); + } else { + LOGGER.warn( + "Multiple configuration elements found for type {}. Only the first will be used.", + extensionType.getName()); + } + } + } + return result; + } } 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 0d934615122..c082d5c2bc4 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 @@ -26,8 +26,6 @@ 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.async.AsyncLoggerConfigDelegate; -import org.apache.logging.log4j.core.async.AsyncWaitStrategyFactory; import org.apache.logging.log4j.core.filter.Filterable; import org.apache.logging.log4j.core.impl.LogEventFactory; import org.apache.logging.log4j.core.lookup.ConfigurationStrSubstitutor; @@ -193,24 +191,6 @@ default T getComponent(Key key) { ScriptManager getScriptManager(); - /** - * Returns the {@code AsyncLoggerConfigDelegate} shared by all - * {@code AsyncLoggerConfig} instances defined in this Configuration. - * - * @return the {@code AsyncLoggerConfigDelegate} - */ - AsyncLoggerConfigDelegate getAsyncLoggerConfigDelegate(); - - /** - * Returns the {@code AsyncWaitStrategyFactory} defined in this Configuration; - * this factory is used to create the LMAX disruptor {@code WaitStrategy} used - * by the disruptor ringbuffer for Async Loggers. - * - * @return the {@code AsyncWaitStrategyFactory} - * @since 2.17.3 - */ - AsyncWaitStrategyFactory getAsyncWaitStrategyFactory(); - /** * Return the WatchManager. * @@ -256,4 +236,29 @@ default LogEventFactory getLogEventFactory() { default RecyclerFactory getRecyclerFactory() { return getComponent(Key.forClass(RecyclerFactory.class)); } + + /** + * Registers a new configuration extension, if it doesn't exist. + *

+ * To preventing polluting the main configuration element, + * each JAR that wishes to extend the {@link Configuration} should use a single child element. + *

+ * @param extensionType the concrete type of the extension, + * @param supplier a factory to create a new extension element, + * @return the current extension if present or a newly generated one. + * @since 3.0 + * @see #getExtension(Class) + */ + T addExtensionIfAbsent(Class extensionType, Supplier supplier); + + /** + * Returns an extension of the given type. + *

+ * Only the first extension of the given type is returned. + *

+ * @param extensionType a concrete type of the extension, + * @return an extension the matches the given type. + * @since 3.0 + */ + T getExtension(Class extensionType); } diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/ConfigurationExtension.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/ConfigurationExtension.java new file mode 100644 index 00000000000..62ee8914ee2 --- /dev/null +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/ConfigurationExtension.java @@ -0,0 +1,22 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.logging.log4j.core.config; + +/** + * Marker interface used by new child elements of a {@link Configuration}. + */ +public interface ConfigurationExtension {} 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 89829645d45..9859c0c9443 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 @@ -852,6 +852,10 @@ protected static LevelAndRefs getLevelAndRefs( return result; } + protected Configuration getConfiguration() { + return config; + } + protected static class LevelAndRefs { public Level level; public List refs; diff --git a/src/changelog/.3.x.x/add_configuration_extension.xml b/src/changelog/.3.x.x/add_configuration_extension.xml new file mode 100644 index 00000000000..cce30452183 --- /dev/null +++ b/src/changelog/.3.x.x/add_configuration_extension.xml @@ -0,0 +1,9 @@ + + + + Add a `ConfigurationExtension` mechanism to allow third-party JARs to extend the `<Configuration>` element. + +