This is an automated email from the ASF dual-hosted git repository. pkarwasz pushed a commit to branch fix/apply-property-environment in repository https://gitbox.apache.org/repos/asf/logging-log4j2.git
commit 580f397f0b2a69f0e9a60ce74d9c676a756a39bb Author: Piotr P. Karwasz <[email protected]> AuthorDate: Thu Mar 14 18:46:58 2024 +0100 Move `Recycler` to `log4j-kit` --- .../services/org.apache.logging.log4j.spi.Provider | 1 - .../logging/log4j/async/logger/AsyncLogger.java | 4 +- .../java/org/apache/logging/log4j/core/Logger.java | 2 +- .../logging/log4j/core/config/Configuration.java | 2 +- .../log4j/core/filter/StructuredDataFilter.java | 4 +- .../logging/log4j/core/impl/DefaultBundle.java | 2 +- .../logging/log4j/core/impl/MutableLogEvent.java | 2 +- .../log4j/core/impl/ReusableLogEventFactory.java | 4 +- .../log4j/core/layout/AbstractStringLayout.java | 4 +- .../logging/log4j/core/layout/PatternLayout.java | 2 +- .../log4j/core/layout/StringBuilderEncoder.java | 2 +- .../log4j/core/pattern/DatePatternConverter.java | 2 +- .../apache/logging/log4j/core/util/JsonUtils.java | 2 +- .../jctools/JCToolsRecyclerFactoryProvider.java | 19 ++- .../JCToolsRecyclerFactoryProviderTest.java | 4 +- ...ing.log4j.kit.recycler.RecyclerFactoryProvider} | 0 .../logging/log4j/kit/logger/AbstractLogger.java | 17 +- .../kit/logger/internal/DefaultLogBuilder.java | 4 +- .../log4j/kit/message/RecyclingMessageFactory.java | 36 ++-- .../message/internal/ReusableMessageFactory.java | 189 +++++++++++++++++++++ .../logging/log4j/kit/message/package-info.java | 22 +++ .../logging/log4j/kit/recycler/PropertyKeys.java | 29 ++-- .../logging/log4j/kit/recycler/Recycler.java | 48 ++++++ .../logging/log4j/kit/recycler/RecyclerAware.java | 27 +-- .../log4j/kit/recycler/RecyclerFactory.java | 58 +++++++ .../kit/recycler/RecyclerFactoryProvider.java | 59 +++++++ .../log4j/kit/recycler/internal/ArrayQueue.java | 100 +++++++++++ .../internal/DummyRecyclerFactoryProvider.java | 81 +++++++++ .../internal/QueueingRecyclerFactoryProvider.java | 51 +++--- .../ThreadLocalRecyclerFactoryProvider.java | 123 ++++++++++++++ .../logging/log4j/kit/recycler/package-info.java | 26 +++ .../kit/recycler/support/AbstractRecycler.java | 48 ++++++ .../log4j/kit/recycler/support/package-info.java | 25 +++ .../log4j/kit/env/TestPropertyEnvironment.java | 34 ++-- .../PropertiesUtilPropertyEnvironmentTest.java | 2 - .../env/support/BasicPropertyEnvironmentTest.java | 22 +-- .../logging/log4j/kit/logger/TestListLogger.java | 4 +- .../kit/recycler/internal/ArrayQueueTest.java | 101 +++++++++++ .../internal/RecyclerFactoryRegistryTest.java | 84 +++++++++ .../recycler/internal/RecyclerFactoryTestUtil.java | 51 ++++++ .../ThreadLocalRecyclerFactoryProviderTest.java | 105 ++++++++++++ .../layout/template/json/JsonTemplateLayout.java | 2 +- .../template/json/resolver/CounterResolver.java | 2 +- .../json/resolver/MessageParameterResolver.java | 2 +- .../json/resolver/ReadOnlyStringMapResolver.java | 4 +- .../json/resolver/StackTraceStringResolver.java | 4 +- .../services/org.apache.logging.log4j.spi.Provider | 1 - pom.xml | 8 +- 48 files changed, 1259 insertions(+), 166 deletions(-) diff --git a/log4j-api-test/src/test/resources/META-INF/services/org.apache.logging.log4j.spi.Provider b/log4j-api-test/src/test/resources/META-INF/services/org.apache.logging.log4j.spi.Provider deleted file mode 100644 index 5ae649a3f9..0000000000 --- a/log4j-api-test/src/test/resources/META-INF/services/org.apache.logging.log4j.spi.Provider +++ /dev/null @@ -1 +0,0 @@ -org.apache.logging.log4j.TestProvider \ No newline at end of file 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 80578ee369..cd430f94c0 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 @@ -34,13 +34,13 @@ import org.apache.logging.log4j.core.impl.ContextDataFactory; import org.apache.logging.log4j.core.time.Clock; import org.apache.logging.log4j.core.time.NanoClock; import org.apache.logging.log4j.kit.logger.AbstractLogger; +import org.apache.logging.log4j.kit.recycler.Recycler; +import org.apache.logging.log4j.kit.recycler.RecyclerFactory; 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; import org.jspecify.annotations.NullMarked; import org.jspecify.annotations.Nullable; 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 09a6d89482..8964b642d9 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 @@ -29,12 +29,12 @@ import org.apache.logging.log4j.core.config.LoggerConfig; import org.apache.logging.log4j.core.config.ReliabilityStrategy; import org.apache.logging.log4j.core.filter.CompositeFilter; import org.apache.logging.log4j.kit.logger.AbstractLogger; +import org.apache.logging.log4j.kit.recycler.RecyclerFactory; 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; import org.jspecify.annotations.NullMarked; 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 933456f6b1..b0e063fb88 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 @@ -36,9 +36,9 @@ import org.apache.logging.log4j.core.time.NanoClock; import org.apache.logging.log4j.core.util.NetUtils; import org.apache.logging.log4j.core.util.WatchManager; import org.apache.logging.log4j.kit.env.PropertyEnvironment; +import org.apache.logging.log4j.kit.recycler.RecyclerFactory; import org.apache.logging.log4j.plugins.Node; import org.apache.logging.log4j.plugins.di.Key; -import org.apache.logging.log4j.spi.recycler.RecyclerFactory; /** * Interface that must be implemented to create a configuration. diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/filter/StructuredDataFilter.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/filter/StructuredDataFilter.java index 2a21b56b22..f9b66888fb 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/filter/StructuredDataFilter.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/filter/StructuredDataFilter.java @@ -28,6 +28,8 @@ import org.apache.logging.log4j.core.Logger; import org.apache.logging.log4j.core.config.Configuration; import org.apache.logging.log4j.core.config.plugins.PluginConfiguration; import org.apache.logging.log4j.core.util.KeyValuePair; +import org.apache.logging.log4j.kit.recycler.Recycler; +import org.apache.logging.log4j.kit.recycler.RecyclerFactory; import org.apache.logging.log4j.message.Message; import org.apache.logging.log4j.message.StructuredDataMessage; import org.apache.logging.log4j.plugins.Configurable; @@ -35,8 +37,6 @@ import org.apache.logging.log4j.plugins.Plugin; import org.apache.logging.log4j.plugins.PluginAttribute; import org.apache.logging.log4j.plugins.PluginElement; import org.apache.logging.log4j.plugins.PluginFactory; -import org.apache.logging.log4j.spi.recycler.Recycler; -import org.apache.logging.log4j.spi.recycler.RecyclerFactory; import org.apache.logging.log4j.util.IndexedReadOnlyStringMap; import org.apache.logging.log4j.util.PerformanceSensitive; import org.apache.logging.log4j.util.StringBuilders; 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 2f176ea286..206e8bb7dd 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 @@ -40,6 +40,7 @@ import org.apache.logging.log4j.core.time.NanoClock; import org.apache.logging.log4j.core.time.internal.DummyNanoClock; import org.apache.logging.log4j.core.util.DefaultShutdownCallbackRegistry; import org.apache.logging.log4j.core.util.ShutdownCallbackRegistry; +import org.apache.logging.log4j.kit.recycler.RecyclerFactory; import org.apache.logging.log4j.message.FlowMessageFactory; import org.apache.logging.log4j.message.MessageFactory; import org.apache.logging.log4j.plugins.Named; @@ -52,7 +53,6 @@ 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; /** diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/MutableLogEvent.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/MutableLogEvent.java index fa1f47d5a3..ab96103fec 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/MutableLogEvent.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/MutableLogEvent.java @@ -27,13 +27,13 @@ import org.apache.logging.log4j.core.time.Instant; import org.apache.logging.log4j.core.time.MutableInstant; import org.apache.logging.log4j.core.time.NanoClock; import org.apache.logging.log4j.core.util.Constants; +import org.apache.logging.log4j.kit.recycler.Recycler; import org.apache.logging.log4j.message.Message; import org.apache.logging.log4j.message.ParameterConsumer; import org.apache.logging.log4j.message.ParameterVisitable; import org.apache.logging.log4j.message.ReusableMessage; import org.apache.logging.log4j.message.SimpleMessage; import org.apache.logging.log4j.message.TimestampMessage; -import org.apache.logging.log4j.spi.recycler.Recycler; import org.apache.logging.log4j.util.StackLocatorUtil; import org.apache.logging.log4j.util.StringBuilders; import org.apache.logging.log4j.util.StringMap; diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/ReusableLogEventFactory.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/ReusableLogEventFactory.java index 7a19db87b3..e70d9faed2 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/ReusableLogEventFactory.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/ReusableLogEventFactory.java @@ -26,10 +26,10 @@ import org.apache.logging.log4j.core.ReusableLogEvent; import org.apache.logging.log4j.core.config.Property; import org.apache.logging.log4j.core.time.Clock; import org.apache.logging.log4j.core.time.NanoClock; +import org.apache.logging.log4j.kit.recycler.Recycler; +import org.apache.logging.log4j.kit.recycler.RecyclerFactory; import org.apache.logging.log4j.message.Message; import org.apache.logging.log4j.plugins.Inject; -import org.apache.logging.log4j.spi.recycler.Recycler; -import org.apache.logging.log4j.spi.recycler.RecyclerFactory; /** * Garbage-free LogEventFactory that recycles mutable {@link LogEvent} instances. diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/layout/AbstractStringLayout.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/layout/AbstractStringLayout.java index 4acb136788..93df4c6430 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/layout/AbstractStringLayout.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/layout/AbstractStringLayout.java @@ -24,9 +24,9 @@ import org.apache.logging.log4j.core.config.LoggerConfig; import org.apache.logging.log4j.core.impl.LogEventFactory; import org.apache.logging.log4j.core.impl.PropertyKeys; import org.apache.logging.log4j.kit.env.PropertyEnvironment; +import org.apache.logging.log4j.kit.recycler.Recycler; +import org.apache.logging.log4j.kit.recycler.RecyclerFactory; import org.apache.logging.log4j.plugins.PluginElement; -import org.apache.logging.log4j.spi.recycler.Recycler; -import org.apache.logging.log4j.spi.recycler.RecyclerFactory; import org.apache.logging.log4j.util.StringBuilders; /** diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/layout/PatternLayout.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/layout/PatternLayout.java index c1332afdae..8343082e34 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/layout/PatternLayout.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/layout/PatternLayout.java @@ -34,12 +34,12 @@ import org.apache.logging.log4j.core.pattern.PatternFormatter; import org.apache.logging.log4j.core.pattern.PatternParser; import org.apache.logging.log4j.core.pattern.RegexReplacement; import org.apache.logging.log4j.kit.env.PropertyEnvironment; +import org.apache.logging.log4j.kit.recycler.Recycler; 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.PluginElement; import org.apache.logging.log4j.plugins.PluginFactory; -import org.apache.logging.log4j.spi.recycler.Recycler; import org.apache.logging.log4j.util.PropertiesUtil; import org.apache.logging.log4j.util.Strings; diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/layout/StringBuilderEncoder.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/layout/StringBuilderEncoder.java index 360dd56e06..dc6f160751 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/layout/StringBuilderEncoder.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/layout/StringBuilderEncoder.java @@ -23,7 +23,7 @@ import java.nio.charset.CharsetEncoder; import java.nio.charset.CodingErrorAction; import java.util.Objects; import org.apache.logging.log4j.core.util.Constants; -import org.apache.logging.log4j.spi.recycler.RecyclerFactory; +import org.apache.logging.log4j.kit.recycler.RecyclerFactory; /** * {@link Encoder} for {@link StringBuilder}s. diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/DatePatternConverter.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/DatePatternConverter.java index d923c34f7c..09cc6c7ddd 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/DatePatternConverter.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/DatePatternConverter.java @@ -28,9 +28,9 @@ import org.apache.logging.log4j.core.time.MutableInstant; import org.apache.logging.log4j.core.time.internal.format.FastDateFormat; import org.apache.logging.log4j.core.time.internal.format.FixedDateFormat; import org.apache.logging.log4j.core.time.internal.format.FixedDateFormat.FixedFormat; +import org.apache.logging.log4j.kit.recycler.Recycler; import org.apache.logging.log4j.plugins.Namespace; import org.apache.logging.log4j.plugins.Plugin; -import org.apache.logging.log4j.spi.recycler.Recycler; import org.apache.logging.log4j.util.PerformanceSensitive; /** diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/util/JsonUtils.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/util/JsonUtils.java index a60d6e4c9c..ad86c88089 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/util/JsonUtils.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/util/JsonUtils.java @@ -16,8 +16,8 @@ */ package org.apache.logging.log4j.core.util; +import org.apache.logging.log4j.kit.recycler.Recycler; import org.apache.logging.log4j.spi.LoggingSystem; -import org.apache.logging.log4j.spi.recycler.Recycler; import org.apache.logging.log4j.util.Lazy; /** diff --git a/log4j-jctools/src/main/java/org/apache/logging/log4j/jctools/JCToolsRecyclerFactoryProvider.java b/log4j-jctools/src/main/java/org/apache/logging/log4j/jctools/JCToolsRecyclerFactoryProvider.java index 0cf6daa496..b5671d2fa7 100644 --- a/log4j-jctools/src/main/java/org/apache/logging/log4j/jctools/JCToolsRecyclerFactoryProvider.java +++ b/log4j-jctools/src/main/java/org/apache/logging/log4j/jctools/JCToolsRecyclerFactoryProvider.java @@ -17,18 +17,18 @@ package org.apache.logging.log4j.jctools; import static java.util.Objects.requireNonNull; -import static org.apache.logging.log4j.spi.recycler.Recycler.DEFAULT_CAPACITY; +import static org.apache.logging.log4j.kit.recycler.Recycler.DEFAULT_CAPACITY; import aQute.bnd.annotation.spi.ServiceProvider; import java.util.Queue; import java.util.function.Consumer; import java.util.function.Supplier; import org.apache.logging.log4j.kit.env.PropertyEnvironment; -import org.apache.logging.log4j.spi.LoggingSystemProperty; -import org.apache.logging.log4j.spi.recycler.AbstractRecycler; -import org.apache.logging.log4j.spi.recycler.Recycler; -import org.apache.logging.log4j.spi.recycler.RecyclerFactory; -import org.apache.logging.log4j.spi.recycler.RecyclerFactoryProvider; +import org.apache.logging.log4j.kit.recycler.PropertyKeys; +import org.apache.logging.log4j.kit.recycler.Recycler; +import org.apache.logging.log4j.kit.recycler.RecyclerFactory; +import org.apache.logging.log4j.kit.recycler.RecyclerFactoryProvider; +import org.apache.logging.log4j.kit.recycler.support.AbstractRecycler; import org.jctools.queues.MpmcArrayQueue; /** @@ -52,11 +52,12 @@ public final class JCToolsRecyclerFactoryProvider implements RecyclerFactoryProv @Override public RecyclerFactory createForEnvironment(final PropertyEnvironment environment) { requireNonNull(environment, "environment"); - final int capacity = environment.getIntegerProperty(LoggingSystemProperty.RECYCLER_CAPACITY, DEFAULT_CAPACITY); - if (capacity < 1) { + final Integer capacity = + environment.getProperty(PropertyKeys.Recycler.class).capacity(); + if (capacity != null && capacity < 1) { throw new IllegalArgumentException("was expecting a `capacity` greater than 1, found: " + capacity); } - return new JCToolsMpmcRecyclerFactory(capacity); + return new JCToolsMpmcRecyclerFactory(capacity != null ? capacity : DEFAULT_CAPACITY); } private static final class JCToolsMpmcRecyclerFactory implements RecyclerFactory { diff --git a/log4j-jctools/src/test/java/org/apache/logging/log4j/jctools/JCToolsRecyclerFactoryProviderTest.java b/log4j-jctools/src/test/java/org/apache/logging/log4j/jctools/JCToolsRecyclerFactoryProviderTest.java index 51e9d9e272..0a8cba1bbb 100644 --- a/log4j-jctools/src/test/java/org/apache/logging/log4j/jctools/JCToolsRecyclerFactoryProviderTest.java +++ b/log4j-jctools/src/test/java/org/apache/logging/log4j/jctools/JCToolsRecyclerFactoryProviderTest.java @@ -20,8 +20,8 @@ import static org.assertj.core.api.Assertions.assertThat; import java.util.Comparator; import java.util.List; -import org.apache.logging.log4j.spi.recycler.RecyclerFactoryProvider; -import org.apache.logging.log4j.spi.recycler.RecyclerFactoryRegistry; +import org.apache.logging.log4j.kit.recycler.RecyclerFactoryProvider; +import org.apache.logging.log4j.kit.recycler.RecyclerFactoryRegistry; import org.junit.jupiter.api.Test; class JCToolsRecyclerFactoryProviderTest { diff --git a/log4j-jctools/src/test/resources/META-INF/services/org.apache.logging.log4j.spi.recycler.RecyclerFactoryProvider b/log4j-jctools/src/test/resources/META-INF/services/org.apache.logging.log4j.kit.recycler.RecyclerFactoryProvider similarity index 100% rename from log4j-jctools/src/test/resources/META-INF/services/org.apache.logging.log4j.spi.recycler.RecyclerFactoryProvider rename to log4j-jctools/src/test/resources/META-INF/services/org.apache.logging.log4j.kit.recycler.RecyclerFactoryProvider diff --git a/log4j-kit/src/main/java/org/apache/logging/log4j/kit/logger/AbstractLogger.java b/log4j-kit/src/main/java/org/apache/logging/log4j/kit/logger/AbstractLogger.java index 45a2a9312c..3fbcfbd2bd 100644 --- a/log4j-kit/src/main/java/org/apache/logging/log4j/kit/logger/AbstractLogger.java +++ b/log4j-kit/src/main/java/org/apache/logging/log4j/kit/logger/AbstractLogger.java @@ -23,14 +23,17 @@ import org.apache.logging.log4j.LoggingException; import org.apache.logging.log4j.Marker; import org.apache.logging.log4j.MarkerManager; import org.apache.logging.log4j.kit.logger.internal.DefaultLogBuilder; +import org.apache.logging.log4j.kit.message.RecyclingMessageFactory; +import org.apache.logging.log4j.kit.recycler.Recycler; +import org.apache.logging.log4j.kit.recycler.RecyclerFactory; import org.apache.logging.log4j.message.EntryMessage; 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.message.MessageFactory2; import org.apache.logging.log4j.message.StringFormattedMessage; import org.apache.logging.log4j.spi.ExtendedLogger; -import org.apache.logging.log4j.spi.recycler.Recycler; -import org.apache.logging.log4j.spi.recycler.RecyclerFactory; +import org.apache.logging.log4j.spi.MessageFactory2Adapter; import org.apache.logging.log4j.util.LambdaUtil; import org.apache.logging.log4j.util.MessageSupplier; import org.apache.logging.log4j.util.PerformanceSensitive; @@ -107,7 +110,7 @@ public abstract class AbstractLogger implements ExtendedLogger { private static final ThreadLocal<int[]> recursionDepthHolder = new ThreadLocal<>(); // LOG4J2-1518, LOG4J2-2031 private final String name; - private final MessageFactory messageFactory; + private final MessageFactory2 messageFactory; private final FlowMessageFactory flowMessageFactory; private final Recycler<DefaultLogBuilder> recycler; private final Logger statusLogger; @@ -125,7 +128,9 @@ public abstract class AbstractLogger implements ExtendedLogger { final RecyclerFactory recyclerFactory, final Logger statusLogger) { this.name = name; - this.messageFactory = messageFactory; + this.messageFactory = messageFactory instanceof final MessageFactory2 messageFactory2 + ? messageFactory2 + : new MessageFactory2Adapter(messageFactory); this.flowMessageFactory = flowMessageFactory; this.recycler = recyclerFactory.create(DefaultLogBuilder::new); this.statusLogger = statusLogger; @@ -242,7 +247,9 @@ public abstract class AbstractLogger implements ExtendedLogger { // NOTE: This is a hot method. Current implementation compiles to 33 bytes of byte code. // This is within the 35 byte MaxInlineSize threshold. Modify with care! private void recycle(final @Nullable Message message) { - messageFactory.recycle(message); + if (messageFactory instanceof final RecyclingMessageFactory recyclingMessageFactory) { + recyclingMessageFactory.recycle(message); + } } @PerformanceSensitive diff --git a/log4j-kit/src/main/java/org/apache/logging/log4j/kit/logger/internal/DefaultLogBuilder.java b/log4j-kit/src/main/java/org/apache/logging/log4j/kit/logger/internal/DefaultLogBuilder.java index 47052fd14d..21a1cf92b3 100644 --- a/log4j-kit/src/main/java/org/apache/logging/log4j/kit/logger/internal/DefaultLogBuilder.java +++ b/log4j-kit/src/main/java/org/apache/logging/log4j/kit/logger/internal/DefaultLogBuilder.java @@ -21,11 +21,11 @@ import org.apache.logging.log4j.BridgeAware; import org.apache.logging.log4j.Level; import org.apache.logging.log4j.LogBuilder; import org.apache.logging.log4j.Marker; +import org.apache.logging.log4j.kit.recycler.Recycler; +import org.apache.logging.log4j.kit.recycler.RecyclerAware; import org.apache.logging.log4j.message.Message; import org.apache.logging.log4j.message.SimpleMessage; import org.apache.logging.log4j.spi.ExtendedLogger; -import org.apache.logging.log4j.spi.recycler.Recycler; -import org.apache.logging.log4j.spi.recycler.RecyclerAware; import org.apache.logging.log4j.util.LambdaUtil; import org.apache.logging.log4j.util.StackLocatorUtil; import org.apache.logging.log4j.util.Strings; diff --git a/log4j-jctools/src/test/java/org/apache/logging/log4j/jctools/JCToolsRecyclerFactoryProviderTest.java b/log4j-kit/src/main/java/org/apache/logging/log4j/kit/message/RecyclingMessageFactory.java similarity index 50% copy from log4j-jctools/src/test/java/org/apache/logging/log4j/jctools/JCToolsRecyclerFactoryProviderTest.java copy to log4j-kit/src/main/java/org/apache/logging/log4j/kit/message/RecyclingMessageFactory.java index 51e9d9e272..74c33b6f6d 100644 --- a/log4j-jctools/src/test/java/org/apache/logging/log4j/jctools/JCToolsRecyclerFactoryProviderTest.java +++ b/log4j-kit/src/main/java/org/apache/logging/log4j/kit/message/RecyclingMessageFactory.java @@ -14,24 +14,26 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.logging.log4j.jctools; +package org.apache.logging.log4j.kit.message; -import static org.assertj.core.api.Assertions.assertThat; +import org.apache.logging.log4j.kit.recycler.Recycler; +import org.apache.logging.log4j.message.Message; +import org.apache.logging.log4j.message.MessageFactory2; -import java.util.Comparator; -import java.util.List; -import org.apache.logging.log4j.spi.recycler.RecyclerFactoryProvider; -import org.apache.logging.log4j.spi.recycler.RecyclerFactoryRegistry; -import org.junit.jupiter.api.Test; - -class JCToolsRecyclerFactoryProviderTest { +/** + * A message factory backed by a {@link Recycler}. + * <p> + * Messages acquired from this factory <strong>must</strong> be released using the {@link #recycle} method. + * </p> + */ +public interface RecyclingMessageFactory extends MessageFactory2 { - @Test - void verify_is_the_first() { - final List<Class<?>> providerClasses = RecyclerFactoryRegistry.getRecyclerFactoryProviders().stream() - .sorted(Comparator.comparing(RecyclerFactoryProvider::getOrder)) - .<Class<?>>map(RecyclerFactoryProvider::getClass) - .toList(); - assertThat(providerClasses).startsWith(JCToolsRecyclerFactoryProvider.class); - } + /** + * Recycles a message back for potential reuse or cleanup. + * <p> + * + * </p> + * @see Recycler + */ + void recycle(Message message); } diff --git a/log4j-kit/src/main/java/org/apache/logging/log4j/kit/message/internal/ReusableMessageFactory.java b/log4j-kit/src/main/java/org/apache/logging/log4j/kit/message/internal/ReusableMessageFactory.java new file mode 100644 index 0000000000..7bdc19c01c --- /dev/null +++ b/log4j-kit/src/main/java/org/apache/logging/log4j/kit/message/internal/ReusableMessageFactory.java @@ -0,0 +1,189 @@ +/* + * 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.kit.message.internal; + +import org.apache.logging.log4j.kit.message.RecyclingMessageFactory; +import org.apache.logging.log4j.kit.recycler.Recycler; +import org.apache.logging.log4j.kit.recycler.RecyclerFactory; +import org.apache.logging.log4j.message.Message; +import org.apache.logging.log4j.message.ReusableMessage; +import org.apache.logging.log4j.message.ReusableObjectMessage; +import org.apache.logging.log4j.message.ReusableParameterizedMessage; +import org.apache.logging.log4j.message.ReusableSimpleMessage; +import org.apache.logging.log4j.util.PerformanceSensitive; + +/** + * Message factory that avoids allocating temporary objects where possible. + * <p> + * Message instances are cached in a {@link Recycler} and reused when a new message is requested. + * </p> + * Message instances are cached in a {@link Recycler} and reused when a new message is requested. + * @see Recycler + * @since 3.0.0 + */ +@PerformanceSensitive("allocation") +public final class ReusableMessageFactory implements RecyclingMessageFactory { + + private final Recycler<ReusableParameterizedMessage> parameterizedMessageRecycler; + private final Recycler<ReusableSimpleMessage> simpleMessageRecycler; + private final Recycler<ReusableObjectMessage> objectMessageRecycler; + + public ReusableMessageFactory(final RecyclerFactory recyclerFactory) { + parameterizedMessageRecycler = + recyclerFactory.create(ReusableParameterizedMessage::new, ReusableParameterizedMessage::clear); + simpleMessageRecycler = recyclerFactory.create(ReusableSimpleMessage::new, ReusableSimpleMessage::clear); + objectMessageRecycler = recyclerFactory.create(ReusableObjectMessage::new, ReusableObjectMessage::clear); + } + + @Override + public void recycle(final Message message) { + if (message instanceof final ReusableMessage reusable) { + reusable.clear(); + } + // related to LOG4J2-1583 and nested log messages clobbering each other. recycle messages today! + if (message instanceof final ReusableParameterizedMessage reusable) { + parameterizedMessageRecycler.release(reusable); + } else if (message instanceof final ReusableObjectMessage reusable) { + objectMessageRecycler.release(reusable); + } else if (message instanceof final ReusableSimpleMessage reusable) { + simpleMessageRecycler.release(reusable); + } + } + + @Override + public Message newMessage(final CharSequence charSequence) { + final ReusableSimpleMessage result = simpleMessageRecycler.acquire(); + result.set(charSequence); + return result; + } + + @Override + public Message newMessage(final String message, final Object... params) { + return parameterizedMessageRecycler.acquire().set(message, params); + } + + @Override + public Message newMessage(final String message, final Object p0) { + return parameterizedMessageRecycler.acquire().set(message, p0); + } + + @Override + public Message newMessage(final String message, final Object p0, final Object p1) { + return parameterizedMessageRecycler.acquire().set(message, p0, p1); + } + + @Override + public Message newMessage(final String message, final Object p0, final Object p1, final Object p2) { + return parameterizedMessageRecycler.acquire().set(message, p0, p1, p2); + } + + @Override + public Message newMessage( + final String message, final Object p0, final Object p1, final Object p2, final Object p3) { + return parameterizedMessageRecycler.acquire().set(message, p0, p1, p2, p3); + } + + @Override + public Message newMessage( + final String message, final Object p0, final Object p1, final Object p2, final Object p3, final Object p4) { + return parameterizedMessageRecycler.acquire().set(message, p0, p1, p2, p3, p4); + } + + @Override + public Message newMessage( + final String message, + final Object p0, + final Object p1, + final Object p2, + final Object p3, + final Object p4, + final Object p5) { + return parameterizedMessageRecycler.acquire().set(message, p0, p1, p2, p3, p4, p5); + } + + @Override + public Message newMessage( + final String message, + final Object p0, + final Object p1, + final Object p2, + final Object p3, + final Object p4, + final Object p5, + final Object p6) { + return parameterizedMessageRecycler.acquire().set(message, p0, p1, p2, p3, p4, p5, p6); + } + + @Override + public Message newMessage( + final String message, + final Object p0, + final Object p1, + final Object p2, + final Object p3, + final Object p4, + final Object p5, + final Object p6, + final Object p7) { + return parameterizedMessageRecycler.acquire().set(message, p0, p1, p2, p3, p4, p5, p6, p7); + } + + @Override + public Message newMessage( + final String message, + final Object p0, + final Object p1, + final Object p2, + final Object p3, + final Object p4, + final Object p5, + final Object p6, + final Object p7, + final Object p8) { + return parameterizedMessageRecycler.acquire().set(message, p0, p1, p2, p3, p4, p5, p6, p7, p8); + } + + @Override + public Message newMessage( + final String message, + final Object p0, + final Object p1, + final Object p2, + final Object p3, + final Object p4, + final Object p5, + final Object p6, + final Object p7, + final Object p8, + final Object p9) { + return parameterizedMessageRecycler.acquire().set(message, p0, p1, p2, p3, p4, p5, p6, p7, p8, p9); + } + + @Override + public Message newMessage(final String message) { + final ReusableSimpleMessage result = simpleMessageRecycler.acquire(); + result.set(message); + return result; + } + + @Override + public Message newMessage(final Object message) { + final ReusableObjectMessage result = objectMessageRecycler.acquire(); + result.set(message); + return result; + } +} diff --git a/log4j-kit/src/main/java/org/apache/logging/log4j/kit/message/package-info.java b/log4j-kit/src/main/java/org/apache/logging/log4j/kit/message/package-info.java new file mode 100644 index 0000000000..a8f822b176 --- /dev/null +++ b/log4j-kit/src/main/java/org/apache/logging/log4j/kit/message/package-info.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. + */ +@Export +@Version("3.0.0") +package org.apache.logging.log4j.kit.message; + +import org.osgi.annotation.bundle.Export; +import org.osgi.annotation.versioning.Version; diff --git a/log4j-jctools/src/test/java/org/apache/logging/log4j/jctools/JCToolsRecyclerFactoryProviderTest.java b/log4j-kit/src/main/java/org/apache/logging/log4j/kit/recycler/PropertyKeys.java similarity index 50% copy from log4j-jctools/src/test/java/org/apache/logging/log4j/jctools/JCToolsRecyclerFactoryProviderTest.java copy to log4j-kit/src/main/java/org/apache/logging/log4j/kit/recycler/PropertyKeys.java index 51e9d9e272..77b1d06939 100644 --- a/log4j-jctools/src/test/java/org/apache/logging/log4j/jctools/JCToolsRecyclerFactoryProviderTest.java +++ b/log4j-kit/src/main/java/org/apache/logging/log4j/kit/recycler/PropertyKeys.java @@ -14,24 +14,19 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.logging.log4j.jctools; +package org.apache.logging.log4j.kit.recycler; -import static org.assertj.core.api.Assertions.assertThat; +import org.apache.logging.log4j.kit.env.Log4jProperty; +import org.jspecify.annotations.Nullable; -import java.util.Comparator; -import java.util.List; -import org.apache.logging.log4j.spi.recycler.RecyclerFactoryProvider; -import org.apache.logging.log4j.spi.recycler.RecyclerFactoryRegistry; -import org.junit.jupiter.api.Test; +public class PropertyKeys { -class JCToolsRecyclerFactoryProviderTest { - - @Test - void verify_is_the_first() { - final List<Class<?>> providerClasses = RecyclerFactoryRegistry.getRecyclerFactoryProviders().stream() - .sorted(Comparator.comparing(RecyclerFactoryProvider::getOrder)) - .<Class<?>>map(RecyclerFactoryProvider::getClass) - .toList(); - assertThat(providerClasses).startsWith(JCToolsRecyclerFactoryProvider.class); - } + /** + * A set of common configuration options for recyclers + * + * @param factory The name of the recycler factory to use (cf. {@link RecyclerFactoryProvider#getName()}), + * @param capacity The capacity of the recycler. + */ + @Log4jProperty + public record Recycler(@Nullable String factory, @Nullable Integer capacity) {} } diff --git a/log4j-kit/src/main/java/org/apache/logging/log4j/kit/recycler/Recycler.java b/log4j-kit/src/main/java/org/apache/logging/log4j/kit/recycler/Recycler.java new file mode 100644 index 0000000000..99384388bb --- /dev/null +++ b/log4j-kit/src/main/java/org/apache/logging/log4j/kit/recycler/Recycler.java @@ -0,0 +1,48 @@ +/* + * 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.kit.recycler; + +/** + * Contract for recycling strategies. + * This is the primary building block for logging components striving for garbage-free operation. + * + * @param <V> the recyclable type + * @since 3.0.0 + */ +public interface Recycler<V> { + + /** + * The default recycler capacity: {@code max(2C+1, 8)}, {@code C} denoting the number of available processors + */ + int DEFAULT_CAPACITY = Math.max(2 * Runtime.getRuntime().availableProcessors() + 1, 8); + + /** + * Acquires an instance of V. This may either be a fresh instance of V or a recycled instance of V. + * Recycled instances will be modified by their cleanup function before being returned. + * + * @return an instance of V to be used + */ + V acquire(); + + /** + * Releases an instance of V. This allows the instance to be recycled and later reacquired for new + * purposes. + * + * @param value an instance of V no longer being used + */ + void release(V value); +} diff --git a/log4j-jctools/src/test/java/org/apache/logging/log4j/jctools/JCToolsRecyclerFactoryProviderTest.java b/log4j-kit/src/main/java/org/apache/logging/log4j/kit/recycler/RecyclerAware.java similarity index 50% copy from log4j-jctools/src/test/java/org/apache/logging/log4j/jctools/JCToolsRecyclerFactoryProviderTest.java copy to log4j-kit/src/main/java/org/apache/logging/log4j/kit/recycler/RecyclerAware.java index 51e9d9e272..aaaa6ce17a 100644 --- a/log4j-jctools/src/test/java/org/apache/logging/log4j/jctools/JCToolsRecyclerFactoryProviderTest.java +++ b/log4j-kit/src/main/java/org/apache/logging/log4j/kit/recycler/RecyclerAware.java @@ -14,24 +14,15 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.logging.log4j.jctools; +package org.apache.logging.log4j.kit.recycler; -import static org.assertj.core.api.Assertions.assertThat; - -import java.util.Comparator; -import java.util.List; -import org.apache.logging.log4j.spi.recycler.RecyclerFactoryProvider; -import org.apache.logging.log4j.spi.recycler.RecyclerFactoryRegistry; -import org.junit.jupiter.api.Test; - -class JCToolsRecyclerFactoryProviderTest { +/** + * Interface implemented by classes that need to interact with the {@link Recycler} that created them. + * + * @since 3.0.0 + */ +@FunctionalInterface +public interface RecyclerAware<V> { - @Test - void verify_is_the_first() { - final List<Class<?>> providerClasses = RecyclerFactoryRegistry.getRecyclerFactoryProviders().stream() - .sorted(Comparator.comparing(RecyclerFactoryProvider::getOrder)) - .<Class<?>>map(RecyclerFactoryProvider::getClass) - .toList(); - assertThat(providerClasses).startsWith(JCToolsRecyclerFactoryProvider.class); - } + void setRecycler(Recycler<V> recycler); } diff --git a/log4j-kit/src/main/java/org/apache/logging/log4j/kit/recycler/RecyclerFactory.java b/log4j-kit/src/main/java/org/apache/logging/log4j/kit/recycler/RecyclerFactory.java new file mode 100644 index 0000000000..e209ef6497 --- /dev/null +++ b/log4j-kit/src/main/java/org/apache/logging/log4j/kit/recycler/RecyclerFactory.java @@ -0,0 +1,58 @@ +/* + * 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.kit.recycler; + +import java.util.function.Consumer; +import java.util.function.Supplier; + +/** + * Contract for {@link Recycler} factories. + * + * @since 3.0.0 + */ +public interface RecyclerFactory { + + /** + * Creates a new recycler using the given supplier function for initial instances. + * + * @param supplier a function to provide initial instances + * @param <V> the recyclable type + * @return a new recycler + */ + default <V> Recycler<V> create(final Supplier<V> supplier) { + return create(supplier, ignored -> {}); + } + + /** + * Creates a new recycler using the given supplier and cleaner functions. + * <p> + * The provided supplier needs to make sure that generated instances are always clean. + * </p> + * <p> + * Recycled instances are always guaranteed to be clean. + * The cleaning of an instance can take place either just before acquisition or prior to admitting it back into the reusable instances pool. + * The moment when the cleaning will be carried out is implementation dependent. + * Though a released instance should ideally be cleaned immediately to avoid keeping references to unused objects. + * </p> + * + * @param supplier a function to provide initial (and clean!) instances + * @param cleaner function to reset an instance before reuse + * @param <V> the recyclable type + * @return a new recycler + */ + <V> Recycler<V> create(Supplier<V> supplier, Consumer<V> cleaner); +} diff --git a/log4j-kit/src/main/java/org/apache/logging/log4j/kit/recycler/RecyclerFactoryProvider.java b/log4j-kit/src/main/java/org/apache/logging/log4j/kit/recycler/RecyclerFactoryProvider.java new file mode 100644 index 0000000000..ae8e140134 --- /dev/null +++ b/log4j-kit/src/main/java/org/apache/logging/log4j/kit/recycler/RecyclerFactoryProvider.java @@ -0,0 +1,59 @@ +/* + * 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.kit.recycler; + +import edu.umd.cs.findbugs.annotations.Nullable; +import org.apache.logging.log4j.kit.env.PropertyEnvironment; + +/** + * Contract for providing {@link RecyclerFactory} instances. + * + * @since 3.0.0 + */ +public interface RecyclerFactoryProvider { + + /** + * Denotes the value to be used while sorting recycler factory providers to determine the precedence order. + * Values will be sorted naturally, that is, lower values will imply higher precedence. + * + * @return the value to be used while sorting + */ + default int getOrder() { + return 0; + } + + /** + * The name of this recycler factory provider. + * Recycler factory providers are required to have unique names. + * + * @return the name of this recycler factory provider + */ + String getName(); + + /** + * Creates a recycler factory for the provided environment. + * <p> + * The return value can be null indicating that the recycler factory is not available for the provided environment. + * For instance, the provider of a {@link ThreadLocal}-based recycler factory can return null if the environment is of a web application. + * </p> + * + * @param environment an environment + * @return either a recycler factory instance, or null, if the associated recycler factory is not available for the given environment + */ + @Nullable + RecyclerFactory createForEnvironment(PropertyEnvironment environment); +} diff --git a/log4j-kit/src/main/java/org/apache/logging/log4j/kit/recycler/internal/ArrayQueue.java b/log4j-kit/src/main/java/org/apache/logging/log4j/kit/recycler/internal/ArrayQueue.java new file mode 100644 index 0000000000..973bed589b --- /dev/null +++ b/log4j-kit/src/main/java/org/apache/logging/log4j/kit/recycler/internal/ArrayQueue.java @@ -0,0 +1,100 @@ +/* + * 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.kit.recycler.internal; + +import java.util.AbstractQueue; +import java.util.Iterator; +import java.util.stream.IntStream; +import javax.annotation.concurrent.NotThreadSafe; +import org.apache.logging.log4j.util.InternalApi; + +/** + * An array-backed, fixed-length, not-thread-safe {@link java.util.Queue} implementation. + * + * @param <E> the element type + */ +@InternalApi +@NotThreadSafe +final class ArrayQueue<E> extends AbstractQueue<E> { + + private final E[] buffer; + + private int head; + + private int tail; + + private int size; + + @SuppressWarnings("unchecked") + ArrayQueue(final int capacity) { + if (capacity < 1) { + throw new IllegalArgumentException("invalid capacity: " + capacity); + } + buffer = (E[]) new Object[capacity]; + head = 0; + tail = -1; + size = 0; + } + + @Override + public Iterator<E> iterator() { + int[] i = {head}; + return IntStream.range(0, size) + .mapToObj(ignored -> { + final E item = buffer[i[0]]; + i[0] = (i[0] + 1) % buffer.length; + return item; + }) + .iterator(); + } + + @Override + public boolean offer(final E item) { + if (size == buffer.length) { + return false; + } + tail = (tail + 1) % buffer.length; + buffer[tail] = item; + size++; + return true; + } + + @Override + public E poll() { + if (isEmpty()) { + return null; + } + final E item = buffer[head]; + buffer[head] = null; // Clear refs for GC + head = (head + 1) % buffer.length; + size--; + return item; + } + + @Override + public E peek() { + if (isEmpty()) { + return null; + } + return buffer[head]; + } + + @Override + public int size() { + return size; + } +} diff --git a/log4j-kit/src/main/java/org/apache/logging/log4j/kit/recycler/internal/DummyRecyclerFactoryProvider.java b/log4j-kit/src/main/java/org/apache/logging/log4j/kit/recycler/internal/DummyRecyclerFactoryProvider.java new file mode 100644 index 0000000000..0922bb2d03 --- /dev/null +++ b/log4j-kit/src/main/java/org/apache/logging/log4j/kit/recycler/internal/DummyRecyclerFactoryProvider.java @@ -0,0 +1,81 @@ +/* + * 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.kit.recycler.internal; + +import static java.util.Objects.requireNonNull; + +import aQute.bnd.annotation.spi.ServiceProvider; +import java.util.function.Consumer; +import java.util.function.Supplier; +import org.apache.logging.log4j.kit.env.PropertyEnvironment; +import org.apache.logging.log4j.kit.recycler.Recycler; +import org.apache.logging.log4j.kit.recycler.RecyclerFactory; +import org.apache.logging.log4j.kit.recycler.RecyclerFactoryProvider; +import org.apache.logging.log4j.kit.recycler.support.AbstractRecycler; + +/** + * A {@link Recycler} factory provider such that the recycler does not recycle anything; all instances are freshly created. + * + * @since 3.0.0 + */ +@ServiceProvider(RecyclerFactoryProvider.class) +public final class DummyRecyclerFactoryProvider implements RecyclerFactoryProvider { + + @Override + public int getOrder() { + return 900; + } + + @Override + public String getName() { + return "dummy"; + } + + @Override + public RecyclerFactory createForEnvironment(final PropertyEnvironment environment) { + return DummyRecyclerFactory.INSTANCE; + } + + // Visible for testing + static final class DummyRecyclerFactory implements RecyclerFactory { + + private static final DummyRecyclerFactory INSTANCE = new DummyRecyclerFactory(); + + private DummyRecyclerFactory() {} + + @Override + public <V> Recycler<V> create(final Supplier<V> supplier, final Consumer<V> cleaner) { + requireNonNull(supplier, "supplier"); + return new DummyRecycler<>(supplier); + } + + private static final class DummyRecycler<V> extends AbstractRecycler<V> { + + private DummyRecycler(final Supplier<V> supplier) { + super(supplier); + } + + @Override + public V acquire() { + return createInstance(); + } + + @Override + public void release(final V value) {} + } + } +} diff --git a/log4j-jctools/src/main/java/org/apache/logging/log4j/jctools/JCToolsRecyclerFactoryProvider.java b/log4j-kit/src/main/java/org/apache/logging/log4j/kit/recycler/internal/QueueingRecyclerFactoryProvider.java similarity index 59% copy from log4j-jctools/src/main/java/org/apache/logging/log4j/jctools/JCToolsRecyclerFactoryProvider.java copy to log4j-kit/src/main/java/org/apache/logging/log4j/kit/recycler/internal/QueueingRecyclerFactoryProvider.java index 0cf6daa496..fab04ed718 100644 --- a/log4j-jctools/src/main/java/org/apache/logging/log4j/jctools/JCToolsRecyclerFactoryProvider.java +++ b/log4j-kit/src/main/java/org/apache/logging/log4j/kit/recycler/internal/QueueingRecyclerFactoryProvider.java @@ -14,74 +14,75 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.logging.log4j.jctools; +package org.apache.logging.log4j.kit.recycler.internal; import static java.util.Objects.requireNonNull; -import static org.apache.logging.log4j.spi.recycler.Recycler.DEFAULT_CAPACITY; +import static org.apache.logging.log4j.kit.recycler.Recycler.DEFAULT_CAPACITY; import aQute.bnd.annotation.spi.ServiceProvider; import java.util.Queue; +import java.util.concurrent.ArrayBlockingQueue; import java.util.function.Consumer; import java.util.function.Supplier; import org.apache.logging.log4j.kit.env.PropertyEnvironment; -import org.apache.logging.log4j.spi.LoggingSystemProperty; -import org.apache.logging.log4j.spi.recycler.AbstractRecycler; -import org.apache.logging.log4j.spi.recycler.Recycler; -import org.apache.logging.log4j.spi.recycler.RecyclerFactory; -import org.apache.logging.log4j.spi.recycler.RecyclerFactoryProvider; -import org.jctools.queues.MpmcArrayQueue; +import org.apache.logging.log4j.kit.recycler.Recycler; +import org.apache.logging.log4j.kit.recycler.RecyclerFactory; +import org.apache.logging.log4j.kit.recycler.RecyclerFactoryProvider; +import org.apache.logging.log4j.kit.recycler.support.AbstractRecycler; /** - * A {@link Recycler} factory provider implementation based on <a href="https://jctools.github.io/JCTools/">JCTools</a>. - * - * @since 3.0.0 + * A {@link Recycler} factory provider such that the recycler pools objects in a fixed-size queue. */ @ServiceProvider(RecyclerFactoryProvider.class) -public final class JCToolsRecyclerFactoryProvider implements RecyclerFactoryProvider { +public final class QueueingRecyclerFactoryProvider implements RecyclerFactoryProvider { @Override public int getOrder() { - return 600; + return 800; } @Override public String getName() { - return "jctools-mpmc"; + return "queue"; } @Override public RecyclerFactory createForEnvironment(final PropertyEnvironment environment) { requireNonNull(environment, "environment"); - final int capacity = environment.getIntegerProperty(LoggingSystemProperty.RECYCLER_CAPACITY, DEFAULT_CAPACITY); + final int capacity = environment.getIntegerProperty("Recycler.capacity", DEFAULT_CAPACITY); if (capacity < 1) { throw new IllegalArgumentException("was expecting a `capacity` greater than 1, found: " + capacity); } - return new JCToolsMpmcRecyclerFactory(capacity); + return new QueueingRecyclerFactory(capacity); } - private static final class JCToolsMpmcRecyclerFactory implements RecyclerFactory { + // Visible for testing + static final class QueueingRecyclerFactory implements RecyclerFactory { - private final int capacity; + // Visible for testing + final int capacity; - private JCToolsMpmcRecyclerFactory(final int capacity) { + private QueueingRecyclerFactory(int capacity) { this.capacity = capacity; } @Override - public <V> Recycler<V> create(Supplier<V> supplier, Consumer<V> cleaner) { + public <V> Recycler<V> create(final Supplier<V> supplier, final Consumer<V> cleaner) { requireNonNull(supplier, "supplier"); requireNonNull(cleaner, "cleaner"); - final MpmcArrayQueue<V> queue = new MpmcArrayQueue<>(capacity); - return new JCToolsMpmcRecycler<>(supplier, cleaner, queue); + final Queue<V> queue = new ArrayBlockingQueue<>(capacity); + return new QueueingRecycler<>(supplier, cleaner, queue); } - private static final class JCToolsMpmcRecycler<V> extends AbstractRecycler<V> { + // Visible for testing + static final class QueueingRecycler<V> extends AbstractRecycler<V> { private final Consumer<V> cleaner; - private final Queue<V> queue; + // Visible for testing + final Queue<V> queue; - private JCToolsMpmcRecycler(final Supplier<V> supplier, final Consumer<V> cleaner, final Queue<V> queue) { + private QueueingRecycler(final Supplier<V> supplier, final Consumer<V> cleaner, final Queue<V> queue) { super(supplier); this.cleaner = cleaner; this.queue = queue; diff --git a/log4j-kit/src/main/java/org/apache/logging/log4j/kit/recycler/internal/ThreadLocalRecyclerFactoryProvider.java b/log4j-kit/src/main/java/org/apache/logging/log4j/kit/recycler/internal/ThreadLocalRecyclerFactoryProvider.java new file mode 100644 index 0000000000..23af67bec1 --- /dev/null +++ b/log4j-kit/src/main/java/org/apache/logging/log4j/kit/recycler/internal/ThreadLocalRecyclerFactoryProvider.java @@ -0,0 +1,123 @@ +/* + * 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.kit.recycler.internal; + +import static java.util.Objects.requireNonNull; +import static org.apache.logging.log4j.kit.recycler.Recycler.DEFAULT_CAPACITY; +import static org.apache.logging.log4j.util.LoaderUtil.isClassAvailable; + +import aQute.bnd.annotation.spi.ServiceProvider; +import java.util.Queue; +import java.util.function.Consumer; +import java.util.function.Supplier; +import org.apache.logging.log4j.kit.env.PropertyEnvironment; +import org.apache.logging.log4j.kit.recycler.PropertyKeys; +import org.apache.logging.log4j.kit.recycler.Recycler; +import org.apache.logging.log4j.kit.recycler.RecyclerFactory; +import org.apache.logging.log4j.kit.recycler.RecyclerFactoryProvider; +import org.apache.logging.log4j.kit.recycler.support.AbstractRecycler; + +/** + * A {@link Recycler} factory provider such that the recycler pools objects in a fixed-size queue stored in a {@link ThreadLocal}. + * <p> + * This strategy may not be appropriate in workloads where units of work are independent of operating system threads such as reactive streams, coroutines, or virtual threads. + * For such use cases, see {@link QueueingRecyclerFactoryProvider}. + * </p> + * + * @since 3.0.0 + */ +@ServiceProvider(RecyclerFactoryProvider.class) +public final class ThreadLocalRecyclerFactoryProvider implements RecyclerFactoryProvider { + + private static final boolean SERVLET_API_PRESENT = + isClassAvailable("javax.servlet.Servlet") || isClassAvailable("jakarta.servlet.Servlet"); + + @Override + public int getOrder() { + return SERVLET_API_PRESENT ? Integer.MAX_VALUE : 700; + } + + @Override + public String getName() { + return "threadLocal"; + } + + @Override + public RecyclerFactory createForEnvironment(final PropertyEnvironment environment) { + requireNonNull(environment, "environment"); + final Integer capacity = + environment.getProperty(PropertyKeys.Recycler.class).capacity(); + if (capacity != null && capacity < 1) { + throw new IllegalArgumentException("was expecting a `capacity` greater than 1, found: " + capacity); + } + return new ThreadLocalRecyclerFactory(capacity != null ? capacity : DEFAULT_CAPACITY); + } + + // Visible for testing + static final class ThreadLocalRecyclerFactory implements RecyclerFactory { + + /** + * Maximum number of objects retained per thread. + * <p> + * This allows to acquire objects in recursive method calls and maintain minimal overhead in the scenarios where the active instance count goes far beyond this for a brief moment. + * </p> + */ + // Visible for testing + final int capacity; + + private ThreadLocalRecyclerFactory(int capacity) { + this.capacity = capacity; + } + + @Override + public <V> Recycler<V> create(final Supplier<V> supplier, final Consumer<V> cleaner) { + requireNonNull(supplier, "supplier"); + requireNonNull(cleaner, "cleaner"); + return new ThreadLocalRecycler<>(supplier, cleaner, capacity); + } + + // Visible for testing + static final class ThreadLocalRecycler<V> extends AbstractRecycler<V> { + + private final Consumer<V> cleaner; + + // Visible for testing + final ThreadLocal<Queue<V>> queueRef; + + private ThreadLocalRecycler(final Supplier<V> supplier, final Consumer<V> cleaner, final int capacity) { + super(supplier); + this.queueRef = ThreadLocal.withInitial(() -> new ArrayQueue<>(capacity)); + this.cleaner = cleaner; + } + + @Override + public V acquire() { + final Queue<V> queue = queueRef.get(); + final V value = queue.poll(); + return value != null ? value : createInstance(); + } + + @Override + public void release(final V value) { + requireNonNull(value, "value"); + cleaner.accept(value); + final Queue<V> queue = queueRef.get(); + queue.offer(value); + } + } + } +} diff --git a/log4j-kit/src/main/java/org/apache/logging/log4j/kit/recycler/package-info.java b/log4j-kit/src/main/java/org/apache/logging/log4j/kit/recycler/package-info.java new file mode 100644 index 0000000000..8d28b85cb9 --- /dev/null +++ b/log4j-kit/src/main/java/org/apache/logging/log4j/kit/recycler/package-info.java @@ -0,0 +1,26 @@ +/* + * 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. + */ +/** + * Internal interfaces and classes to be used by authors of logging implementations or for internal use by + * API classes. + */ +@Export +@Version("3.0.0") +package org.apache.logging.log4j.kit.recycler; + +import org.osgi.annotation.bundle.Export; +import org.osgi.annotation.versioning.Version; diff --git a/log4j-kit/src/main/java/org/apache/logging/log4j/kit/recycler/support/AbstractRecycler.java b/log4j-kit/src/main/java/org/apache/logging/log4j/kit/recycler/support/AbstractRecycler.java new file mode 100644 index 0000000000..764d7ec8d7 --- /dev/null +++ b/log4j-kit/src/main/java/org/apache/logging/log4j/kit/recycler/support/AbstractRecycler.java @@ -0,0 +1,48 @@ +/* + * 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.kit.recycler.support; + +import static java.util.Objects.requireNonNull; + +import java.util.function.Supplier; +import org.apache.logging.log4j.kit.recycler.Recycler; +import org.apache.logging.log4j.kit.recycler.RecyclerAware; + +/** + * Abstract implementation of {@link Recycler} that properly handles {@link RecyclerAware} objects + * + * @param <V> The type of recycled object. + * @since 3.0.0 + */ +public abstract class AbstractRecycler<V> implements Recycler<V> { + + private final Supplier<V> supplier; + + protected AbstractRecycler(final Supplier<V> supplier) { + this.supplier = requireNonNull(supplier, "supplier"); + } + + protected final V createInstance() { + final V instance = supplier.get(); + if (instance instanceof RecyclerAware) { + @SuppressWarnings("unchecked") + final RecyclerAware<V> recyclerAware = (RecyclerAware<V>) instance; + recyclerAware.setRecycler(this); + } + return instance; + } +} diff --git a/log4j-kit/src/main/java/org/apache/logging/log4j/kit/recycler/support/package-info.java b/log4j-kit/src/main/java/org/apache/logging/log4j/kit/recycler/support/package-info.java new file mode 100644 index 0000000000..215ceeb792 --- /dev/null +++ b/log4j-kit/src/main/java/org/apache/logging/log4j/kit/recycler/support/package-info.java @@ -0,0 +1,25 @@ +/* + * 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. + */ +/** + * Support classes for the creation of recyclers. + */ +@Export +@Version("3.0.0") +package org.apache.logging.log4j.kit.recycler.support; + +import org.osgi.annotation.bundle.Export; +import org.osgi.annotation.versioning.Version; diff --git a/log4j-jctools/src/test/java/org/apache/logging/log4j/jctools/JCToolsRecyclerFactoryProviderTest.java b/log4j-kit/src/test/java/org/apache/logging/log4j/kit/env/TestPropertyEnvironment.java similarity index 51% copy from log4j-jctools/src/test/java/org/apache/logging/log4j/jctools/JCToolsRecyclerFactoryProviderTest.java copy to log4j-kit/src/test/java/org/apache/logging/log4j/kit/env/TestPropertyEnvironment.java index 51e9d9e272..7c1c63be58 100644 --- a/log4j-jctools/src/test/java/org/apache/logging/log4j/jctools/JCToolsRecyclerFactoryProviderTest.java +++ b/log4j-kit/src/test/java/org/apache/logging/log4j/kit/env/TestPropertyEnvironment.java @@ -14,24 +14,28 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.logging.log4j.jctools; +package org.apache.logging.log4j.kit.env; -import static org.assertj.core.api.Assertions.assertThat; +import java.util.Map; +import org.apache.logging.log4j.Logger; +import org.apache.logging.log4j.kit.env.support.BasicPropertyEnvironment; +import org.apache.logging.log4j.status.StatusLogger; -import java.util.Comparator; -import java.util.List; -import org.apache.logging.log4j.spi.recycler.RecyclerFactoryProvider; -import org.apache.logging.log4j.spi.recycler.RecyclerFactoryRegistry; -import org.junit.jupiter.api.Test; +public class TestPropertyEnvironment extends BasicPropertyEnvironment { -class JCToolsRecyclerFactoryProviderTest { + private final Map<String, String> props; - @Test - void verify_is_the_first() { - final List<Class<?>> providerClasses = RecyclerFactoryRegistry.getRecyclerFactoryProviders().stream() - .sorted(Comparator.comparing(RecyclerFactoryProvider::getOrder)) - .<Class<?>>map(RecyclerFactoryProvider::getClass) - .toList(); - assertThat(providerClasses).startsWith(JCToolsRecyclerFactoryProvider.class); + public TestPropertyEnvironment(final Map<String, String> props) { + this(props, StatusLogger.getLogger()); + } + + public TestPropertyEnvironment(final Map<String, String> props, final Logger logger) { + super(logger); + this.props = props; + } + + @Override + public String getStringProperty(final String name) { + return props.get(name); } } diff --git a/log4j-kit/src/test/java/org/apache/logging/log4j/kit/env/internal/PropertiesUtilPropertyEnvironmentTest.java b/log4j-kit/src/test/java/org/apache/logging/log4j/kit/env/internal/PropertiesUtilPropertyEnvironmentTest.java index 2c9b8b9c1d..3ec5d184e0 100644 --- a/log4j-kit/src/test/java/org/apache/logging/log4j/kit/env/internal/PropertiesUtilPropertyEnvironmentTest.java +++ b/log4j-kit/src/test/java/org/apache/logging/log4j/kit/env/internal/PropertiesUtilPropertyEnvironmentTest.java @@ -18,11 +18,9 @@ package org.apache.logging.log4j.kit.env.internal; import org.apache.logging.log4j.kit.env.PropertyEnvironment; import org.junit.jupiter.api.Test; -import org.junitpioneer.jupiter.DisabledUntil; import org.junitpioneer.jupiter.SetEnvironmentVariable; import org.junitpioneer.jupiter.SetSystemProperty; -@DisabledUntil(date = "2024-04-01", reason = "Disable until `log4j-api` 3.x is removed") class PropertiesUtilPropertyEnvironmentTest extends AbstractPropertyNamesTest { @Test diff --git a/log4j-kit/src/test/java/org/apache/logging/log4j/kit/env/support/BasicPropertyEnvironmentTest.java b/log4j-kit/src/test/java/org/apache/logging/log4j/kit/env/support/BasicPropertyEnvironmentTest.java index 4716ef565c..0894e72625 100644 --- a/log4j-kit/src/test/java/org/apache/logging/log4j/kit/env/support/BasicPropertyEnvironmentTest.java +++ b/log4j-kit/src/test/java/org/apache/logging/log4j/kit/env/support/BasicPropertyEnvironmentTest.java @@ -26,12 +26,11 @@ import java.util.List; import java.util.Map; import java.util.stream.Stream; import org.apache.logging.log4j.Level; -import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.kit.env.Log4jProperty; import org.apache.logging.log4j.kit.env.PropertyEnvironment; +import org.apache.logging.log4j.kit.env.TestPropertyEnvironment; import org.apache.logging.log4j.kit.logger.TestListLogger; import org.apache.logging.log4j.spi.StandardLevel; -import org.apache.logging.log4j.status.StatusLogger; import org.assertj.core.api.Assertions; import org.jspecify.annotations.Nullable; import org.junit.jupiter.api.Test; @@ -204,23 +203,4 @@ class BasicPropertyEnvironmentTest { assertThat(actual).isEqualTo(expected); assertThat(logger.getMessages()).isEmpty(); } - - private static class TestPropertyEnvironment extends BasicPropertyEnvironment { - - private final Map<String, String> props; - - public TestPropertyEnvironment(final Map<String, String> props) { - this(props, StatusLogger.getLogger()); - } - - public TestPropertyEnvironment(final Map<String, String> props, final Logger logger) { - super(logger); - this.props = props; - } - - @Override - public String getStringProperty(final String name) { - return props.get(name); - } - } } diff --git a/log4j-kit/src/test/java/org/apache/logging/log4j/kit/logger/TestListLogger.java b/log4j-kit/src/test/java/org/apache/logging/log4j/kit/logger/TestListLogger.java index acf6f3689d..9595c9bfa2 100644 --- a/log4j-kit/src/test/java/org/apache/logging/log4j/kit/logger/TestListLogger.java +++ b/log4j-kit/src/test/java/org/apache/logging/log4j/kit/logger/TestListLogger.java @@ -21,13 +21,13 @@ import java.util.Collections; import java.util.List; import org.apache.logging.log4j.Level; import org.apache.logging.log4j.Marker; -import org.apache.logging.log4j.internal.recycler.DummyRecyclerFactoryProvider; +import org.apache.logging.log4j.kit.recycler.RecyclerFactory; +import org.apache.logging.log4j.kit.recycler.internal.DummyRecyclerFactoryProvider; import org.apache.logging.log4j.message.DefaultFlowMessageFactory; 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.message.ParameterizedNoReferenceMessageFactory; -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; diff --git a/log4j-kit/src/test/java/org/apache/logging/log4j/kit/recycler/internal/ArrayQueueTest.java b/log4j-kit/src/test/java/org/apache/logging/log4j/kit/recycler/internal/ArrayQueueTest.java new file mode 100644 index 0000000000..40e6ac557f --- /dev/null +++ b/log4j-kit/src/test/java/org/apache/logging/log4j/kit/recycler/internal/ArrayQueueTest.java @@ -0,0 +1,101 @@ +/* + * 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.kit.recycler.internal; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import java.util.Queue; +import java.util.Random; +import java.util.concurrent.ArrayBlockingQueue; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; +import org.junit.jupiter.params.provider.ValueSource; + +public class ArrayQueueTest { + + @ParameterizedTest + @ValueSource(ints = {-1, 0}) + void invalid_capacity_should_not_be_allowed(final int invalidCapacity) { + assertThatThrownBy(() -> new ArrayQueue<>(invalidCapacity)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("invalid capacity: " + invalidCapacity); + } + + @Test + void should_work_with_capacity_1() { + + // Verify initials + final Queue<String> queue = new ArrayQueue<>(1); + assertThat(queue.size()).isEqualTo(0); + assertThat(queue.peek()).isNull(); + assertThat(queue.poll()).isNull(); + assertThat(queue).isEmpty(); + + // Verify enqueue & deque + assertThat(queue.offer("foo")).isTrue(); + assertThat(queue.offer("bar")).isFalse(); + assertThat(queue.size()).isEqualTo(1); + assertThat(queue).containsOnly("foo"); + assertThat(queue.peek()).isEqualTo("foo"); + assertThat(queue.poll()).isEqualTo("foo"); + + // Verify final state + assertThat(queue.size()).isEqualTo(0); + assertThat(queue.peek()).isNull(); + assertThat(queue.poll()).isNull(); + assertThat(queue).isEmpty(); + } + + @ParameterizedTest + @CsvSource({ + "1,0.3", "1,0.5", "1,0.8", "2,0.3", "2,0.5", "2,0.8", "3,0.3", "3,0.5", "3,0.8", "4,0.3", "4,0.5", "4,0.8" + }) + void ops_should_match_with_std_lib(final int capacity, final double pollRatio) { + + // Set the stage + final Random random = new Random(0); + final int opCount = random.nextInt(100); + final Queue<String> queueRef = new ArrayBlockingQueue<>(capacity); + final Queue<String> queueTarget = new ArrayQueue<>(capacity); + + for (int opIndex = 0; opIndex < opCount; opIndex++) { + + // Verify entry + assertThat(queueTarget.size()).isEqualTo(queueRef.size()); + assertThat(queueTarget.peek()).isEqualTo(queueRef.peek()); + assertThat(queueTarget).containsExactlyElementsOf(queueRef); + + // Is this a `poll()`? + if (pollRatio >= random.nextDouble()) { + assertThat(queueTarget.poll()).isEqualTo(queueRef.poll()); + } + + // Then this is an `offer()` + else { + final String item = "op@" + opIndex; + assertThat(queueTarget.offer(item)).isEqualTo(queueRef.offer(item)); + } + + // Verify exit + assertThat(queueTarget.size()).isEqualTo(queueRef.size()); + assertThat(queueTarget.peek()).isEqualTo(queueRef.peek()); + assertThat(queueTarget).containsExactlyElementsOf(queueRef); + } + } +} diff --git a/log4j-kit/src/test/java/org/apache/logging/log4j/kit/recycler/internal/RecyclerFactoryRegistryTest.java b/log4j-kit/src/test/java/org/apache/logging/log4j/kit/recycler/internal/RecyclerFactoryRegistryTest.java new file mode 100644 index 0000000000..745e626395 --- /dev/null +++ b/log4j-kit/src/test/java/org/apache/logging/log4j/kit/recycler/internal/RecyclerFactoryRegistryTest.java @@ -0,0 +1,84 @@ +/* + * 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.kit.recycler.internal; + +import static org.apache.logging.log4j.kit.recycler.Recycler.DEFAULT_CAPACITY; +import static org.assertj.core.api.Assertions.assertThat; + +import org.apache.logging.log4j.kit.recycler.RecyclerFactory; +import org.apache.logging.log4j.kit.recycler.RecyclerFactoryProvider; +import org.apache.logging.log4j.kit.recycler.internal.DummyRecyclerFactoryProvider.DummyRecyclerFactory; +import org.apache.logging.log4j.kit.recycler.internal.QueueingRecyclerFactoryProvider.QueueingRecyclerFactory; +import org.apache.logging.log4j.kit.recycler.internal.ThreadLocalRecyclerFactoryProvider.ThreadLocalRecyclerFactory; +import org.assertj.core.api.InstanceOfAssertFactories; +import org.junit.jupiter.api.Test; + +public class RecyclerFactoryRegistryTest { + + @Test + void DummyRecyclerFactory_should_work() { + final RecyclerFactory factory = RecyclerFactoryTestUtil.createForEnvironment("dummy", null); + assertThat(factory).isInstanceOf(DummyRecyclerFactory.class); + } + + @Test + void ThreadLocalRecyclerFactory_should_work() { + final RecyclerFactory factory = RecyclerFactoryTestUtil.createForEnvironment("threadLocal", null); + assertThat(factory) + .asInstanceOf(InstanceOfAssertFactories.type(ThreadLocalRecyclerFactory.class)) + .extracting(factory_ -> factory_.capacity) + .isEqualTo(DEFAULT_CAPACITY); + } + + @Test + void ThreadLocalRecyclerFactory_should_work_with_capacity() { + final int capacity = 13; + final RecyclerFactory factory = RecyclerFactoryTestUtil.createForEnvironment("threadLocal", capacity); + assertThat(factory) + .asInstanceOf(InstanceOfAssertFactories.type(ThreadLocalRecyclerFactory.class)) + .extracting(factory_ -> factory_.capacity) + .isEqualTo(capacity); + } + + @Test + void QueueingRecyclerFactory_should_work() { + final RecyclerFactory factory = RecyclerFactoryTestUtil.createForEnvironment("queue", null); + assertThat(factory) + .asInstanceOf(InstanceOfAssertFactories.type(QueueingRecyclerFactory.class)) + .extracting(factory_ -> factory_.capacity) + .isEqualTo(DEFAULT_CAPACITY); + } + + @Test + void QueueingRecyclerFactory_should_work_with_capacity() { + final int capacity = 100; + final RecyclerFactory factory = RecyclerFactoryTestUtil.createForEnvironment("queue", capacity); + assertThat(factory) + .asInstanceOf(InstanceOfAssertFactories.type(QueueingRecyclerFactory.class)) + .extracting(factory_ -> factory_.capacity) + .isEqualTo(capacity); + } + + @Test + void verify_order() { + final RecyclerFactoryProvider dummyProvider = new DummyRecyclerFactoryProvider(); + final RecyclerFactoryProvider threadLocalProvider = new ThreadLocalRecyclerFactoryProvider(); + final RecyclerFactoryProvider queueProvider = new QueueingRecyclerFactoryProvider(); + assertThat(dummyProvider.getOrder()).isGreaterThan(queueProvider.getOrder()); + assertThat(queueProvider.getOrder()).isGreaterThan(threadLocalProvider.getOrder()); + } +} diff --git a/log4j-kit/src/test/java/org/apache/logging/log4j/kit/recycler/internal/RecyclerFactoryTestUtil.java b/log4j-kit/src/test/java/org/apache/logging/log4j/kit/recycler/internal/RecyclerFactoryTestUtil.java new file mode 100644 index 0000000000..19bcb48457 --- /dev/null +++ b/log4j-kit/src/test/java/org/apache/logging/log4j/kit/recycler/internal/RecyclerFactoryTestUtil.java @@ -0,0 +1,51 @@ +/* + * 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.kit.recycler.internal; + +import java.util.HashMap; +import java.util.Map; +import java.util.ServiceLoader; +import org.apache.logging.log4j.kit.env.PropertyEnvironment; +import org.apache.logging.log4j.kit.env.TestPropertyEnvironment; +import org.apache.logging.log4j.kit.recycler.RecyclerFactory; +import org.apache.logging.log4j.kit.recycler.RecyclerFactoryProvider; +import org.apache.logging.log4j.status.StatusLogger; +import org.apache.logging.log4j.util.ServiceLoaderUtil; +import org.jspecify.annotations.Nullable; + +final class RecyclerFactoryTestUtil { + + private RecyclerFactoryTestUtil() {} + + static @Nullable RecyclerFactory createForEnvironment(final String factory, final @Nullable Integer capacity) { + final Map<String, String> properties = new HashMap<>(); + properties.put("Recycler.factory", factory); + if (capacity != null) { + properties.put("Recycler.capacity", capacity.toString()); + } + final PropertyEnvironment env = new TestPropertyEnvironment(properties); + return ServiceLoaderUtil.safeStream( + RecyclerFactoryProvider.class, + ServiceLoader.load( + RecyclerFactoryProvider.class, RecyclerFactoryTestUtil.class.getClassLoader()), + StatusLogger.getLogger()) + .filter(p -> factory.equals(p.getName())) + .findFirst() + .map(p -> p.createForEnvironment(env)) + .orElse(null); + } +} diff --git a/log4j-kit/src/test/java/org/apache/logging/log4j/kit/recycler/internal/ThreadLocalRecyclerFactoryProviderTest.java b/log4j-kit/src/test/java/org/apache/logging/log4j/kit/recycler/internal/ThreadLocalRecyclerFactoryProviderTest.java new file mode 100644 index 0000000000..f33afb1040 --- /dev/null +++ b/log4j-kit/src/test/java/org/apache/logging/log4j/kit/recycler/internal/ThreadLocalRecyclerFactoryProviderTest.java @@ -0,0 +1,105 @@ +/* + * 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.kit.recycler.internal; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.util.List; +import java.util.Queue; +import java.util.stream.Collectors; +import java.util.stream.IntStream; +import org.apache.logging.log4j.kit.recycler.RecyclerFactory; +import org.apache.logging.log4j.kit.recycler.internal.ThreadLocalRecyclerFactoryProvider.ThreadLocalRecyclerFactory; +import org.apache.logging.log4j.kit.recycler.internal.ThreadLocalRecyclerFactoryProvider.ThreadLocalRecyclerFactory.ThreadLocalRecycler; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junitpioneer.jupiter.params.IntRangeSource; + +class ThreadLocalRecyclerFactoryProviderTest { + + private static final int CAPACITY = 13; + + private static class RecyclableObject {} + + private ThreadLocalRecycler<RecyclableObject> recycler; + + private Queue<RecyclableObject> recyclerQueue; + + @BeforeEach + void setUp() { + final RecyclerFactory recyclerFactory = RecyclerFactoryTestUtil.createForEnvironment("threadLocal", CAPACITY); + assertThat(recyclerFactory).isInstanceOf(ThreadLocalRecyclerFactory.class); + assert recyclerFactory != null; + recycler = (ThreadLocalRecycler<RecyclableObject>) recyclerFactory.create(RecyclableObject::new); + recyclerQueue = recycler.queueRef.get(); + } + + @ParameterizedTest + @IntRangeSource(from = 1, to = CAPACITY, closed = true) + void nested_acquires_should_not_interfere(final int acquisitionCount) { + + // pool should start empty + assertThat(recyclerQueue).isEmpty(); + + final List<RecyclableObject> acquiredObjects = IntStream.range(0, acquisitionCount) + .mapToObj(i -> recycler.acquire()) + .collect(Collectors.toList()); + + // still nothing returned to pool + assertThat(recyclerQueue).isEmpty(); + + // don't want any duplicate instances + assertThat(acquiredObjects).containsOnlyOnceElementsOf(acquiredObjects); + acquiredObjects.forEach(recycler::release); + + // and now they should be back in the pool + assertThat(recyclerQueue).hasSize(acquisitionCount); + + // then reacquire them to see that they're still the same object as we've filled in + // the thread-local queue with returned objects + final List<RecyclableObject> reacquiredObjects = IntStream.range(0, acquisitionCount) + .mapToObj(i -> recycler.acquire()) + .collect(Collectors.toList()); + + assertThat(reacquiredObjects).containsExactlyElementsOf(acquiredObjects); + } + + @Test + void nested_acquires_past_max_queue_size_should_discard_extra_releases() { + + assertThat(recyclerQueue).isEmpty(); + + // Simulate a callstack with excessive logging + final int acquisitionCount = Math.addExact(CAPACITY, 1024); + final List<RecyclableObject> acquiredObjects = IntStream.range(0, acquisitionCount) + .mapToObj(i -> recycler.acquire()) + .toList(); + + // Verify collected instances are all new + assertThat(acquiredObjects).doesNotHaveDuplicates(); + + // Verify the pool is still empty + assertThat(recyclerQueue).isEmpty(); + + // Release all acquired instances + acquiredObjects.forEach(recycler::release); + + // Verify the queue size is capped + assertThat(recyclerQueue).hasSize(CAPACITY); + } +} diff --git a/log4j-layout-template-json/src/main/java/org/apache/logging/log4j/layout/template/json/JsonTemplateLayout.java b/log4j-layout-template-json/src/main/java/org/apache/logging/log4j/layout/template/json/JsonTemplateLayout.java index 18b23d96c3..b9f3fdd198 100644 --- a/log4j-layout-template-json/src/main/java/org/apache/logging/log4j/layout/template/json/JsonTemplateLayout.java +++ b/log4j-layout-template-json/src/main/java/org/apache/logging/log4j/layout/template/json/JsonTemplateLayout.java @@ -33,6 +33,7 @@ import org.apache.logging.log4j.core.config.plugins.PluginConfiguration; import org.apache.logging.log4j.core.layout.ByteBufferDestination; import org.apache.logging.log4j.core.layout.Encoder; import org.apache.logging.log4j.core.layout.StringBuilderEncoder; +import org.apache.logging.log4j.kit.recycler.Recycler; import org.apache.logging.log4j.layout.template.json.resolver.EventResolverContext; import org.apache.logging.log4j.layout.template.json.resolver.EventResolverFactory; import org.apache.logging.log4j.layout.template.json.resolver.EventResolverInterceptor; @@ -48,7 +49,6 @@ import org.apache.logging.log4j.plugins.Plugin; import org.apache.logging.log4j.plugins.PluginBuilderAttribute; import org.apache.logging.log4j.plugins.PluginElement; import org.apache.logging.log4j.plugins.di.Key; -import org.apache.logging.log4j.spi.recycler.Recycler; import org.apache.logging.log4j.util.Strings; @Configurable(elementType = Layout.ELEMENT_TYPE) diff --git a/log4j-layout-template-json/src/main/java/org/apache/logging/log4j/layout/template/json/resolver/CounterResolver.java b/log4j-layout-template-json/src/main/java/org/apache/logging/log4j/layout/template/json/resolver/CounterResolver.java index 77a80af09c..4ce28da6a7 100644 --- a/log4j-layout-template-json/src/main/java/org/apache/logging/log4j/layout/template/json/resolver/CounterResolver.java +++ b/log4j-layout-template-json/src/main/java/org/apache/logging/log4j/layout/template/json/resolver/CounterResolver.java @@ -22,8 +22,8 @@ import java.util.concurrent.atomic.AtomicReference; import java.util.concurrent.locks.LockSupport; import java.util.function.Consumer; import org.apache.logging.log4j.core.LogEvent; +import org.apache.logging.log4j.kit.recycler.Recycler; import org.apache.logging.log4j.layout.template.json.util.JsonWriter; -import org.apache.logging.log4j.spi.recycler.Recycler; /** * Resolves a number from an internal counter. diff --git a/log4j-layout-template-json/src/main/java/org/apache/logging/log4j/layout/template/json/resolver/MessageParameterResolver.java b/log4j-layout-template-json/src/main/java/org/apache/logging/log4j/layout/template/json/resolver/MessageParameterResolver.java index 0f7fc723cc..cda477d215 100644 --- a/log4j-layout-template-json/src/main/java/org/apache/logging/log4j/layout/template/json/resolver/MessageParameterResolver.java +++ b/log4j-layout-template-json/src/main/java/org/apache/logging/log4j/layout/template/json/resolver/MessageParameterResolver.java @@ -17,11 +17,11 @@ package org.apache.logging.log4j.layout.template.json.resolver; import org.apache.logging.log4j.core.LogEvent; +import org.apache.logging.log4j.kit.recycler.Recycler; import org.apache.logging.log4j.layout.template.json.util.JsonWriter; import org.apache.logging.log4j.message.Message; import org.apache.logging.log4j.message.ParameterConsumer; import org.apache.logging.log4j.message.ParameterVisitable; -import org.apache.logging.log4j.spi.recycler.Recycler; /** * {@link Message} parameter (i.e., {@link Message#getParameters()}) resolver. diff --git a/log4j-layout-template-json/src/main/java/org/apache/logging/log4j/layout/template/json/resolver/ReadOnlyStringMapResolver.java b/log4j-layout-template-json/src/main/java/org/apache/logging/log4j/layout/template/json/resolver/ReadOnlyStringMapResolver.java index 64356bfe2e..603692bf7c 100644 --- a/log4j-layout-template-json/src/main/java/org/apache/logging/log4j/layout/template/json/resolver/ReadOnlyStringMapResolver.java +++ b/log4j-layout-template-json/src/main/java/org/apache/logging/log4j/layout/template/json/resolver/ReadOnlyStringMapResolver.java @@ -21,9 +21,9 @@ import java.util.function.Function; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.apache.logging.log4j.core.LogEvent; +import org.apache.logging.log4j.kit.recycler.Recycler; +import org.apache.logging.log4j.kit.recycler.RecyclerFactory; import org.apache.logging.log4j.layout.template.json.util.JsonWriter; -import org.apache.logging.log4j.spi.recycler.Recycler; -import org.apache.logging.log4j.spi.recycler.RecyclerFactory; import org.apache.logging.log4j.util.ReadOnlyStringMap; import org.apache.logging.log4j.util.TriConsumer; diff --git a/log4j-layout-template-json/src/main/java/org/apache/logging/log4j/layout/template/json/resolver/StackTraceStringResolver.java b/log4j-layout-template-json/src/main/java/org/apache/logging/log4j/layout/template/json/resolver/StackTraceStringResolver.java index 8d505eff8f..dddf66f727 100644 --- a/log4j-layout-template-json/src/main/java/org/apache/logging/log4j/layout/template/json/resolver/StackTraceStringResolver.java +++ b/log4j-layout-template-json/src/main/java/org/apache/logging/log4j/layout/template/json/resolver/StackTraceStringResolver.java @@ -22,11 +22,11 @@ import java.util.function.Supplier; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.stream.Collectors; +import org.apache.logging.log4j.kit.recycler.Recycler; +import org.apache.logging.log4j.kit.recycler.RecyclerFactory; import org.apache.logging.log4j.layout.template.json.util.CharSequencePointer; import org.apache.logging.log4j.layout.template.json.util.JsonWriter; import org.apache.logging.log4j.layout.template.json.util.TruncatingBufferedPrintWriter; -import org.apache.logging.log4j.spi.recycler.Recycler; -import org.apache.logging.log4j.spi.recycler.RecyclerFactory; /** * Exception stack trace to JSON string resolver used by {@link ExceptionResolver}. diff --git a/log4j-to-slf4j/src/main/resources/META-INF/services/org.apache.logging.log4j.spi.Provider b/log4j-to-slf4j/src/main/resources/META-INF/services/org.apache.logging.log4j.spi.Provider deleted file mode 100644 index c66b5c946a..0000000000 --- a/log4j-to-slf4j/src/main/resources/META-INF/services/org.apache.logging.log4j.spi.Provider +++ /dev/null @@ -1 +0,0 @@ -org.apache.logging.slf4j.SLF4JProvider \ No newline at end of file diff --git a/pom.xml b/pom.xml index e2a6286227..ba974efa04 100644 --- a/pom.xml +++ b/pom.xml @@ -233,8 +233,6 @@ <!-- Last comes the rest of the modules in alphabetical order. Note that modules here must have a corresponding entry in `dependencyManagement > dependencies` block below! --> <module>log4j-1.2-api</module> - <module>log4j-api</module> - <module>log4j-api-test</module> <module>log4j-async-logger</module> <module>log4j-config-jackson</module> <module>log4j-config-properties</module> @@ -267,8 +265,6 @@ <module>log4j-slf4j2-impl</module> <module>log4j-slf4j-impl</module> <module>log4j-spring-cloud-config-client</module> - <module>log4j-to-jul</module> - <module>log4j-to-slf4j</module> </modules> @@ -359,13 +355,13 @@ <dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-api</artifactId> - <version>${project.version}</version> + <version>2.24.0-SNAPSHOT</version> </dependency> <dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-api-test</artifactId> - <version>${project.version}</version> + <version>2.24.0-SNAPSHOT</version> </dependency> <dependency>
