You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@logging.apache.org by pk...@apache.org on 2024/03/14 20:32:55 UTC
(logging-log4j2) 03/03: Move `Recycler` to `log4j-kit`
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 0ac3d5e07d1e0d5d343e29248d7f86ae06ff8c9d
Author: Piotr P. Karwasz <pi...@karwasz.org>
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>