This is an automated email from the ASF dual-hosted git repository. vy pushed a commit to branch recycler-api-3.x in repository https://gitbox.apache.org/repos/asf/logging-log4j2.git
commit 7cdd07593178281d69afda32746a3452016f5a41 Author: Volkan Yazıcı <[email protected]> AuthorDate: Fri Mar 24 22:24:46 2023 +0100 More structural fixes --- .../logging/log4j/spi/RecyclerFactoriesTest.java | 101 +++++++++++++++ .../log4j/spi/ThreadLocalRecyclerFactoryTest.java | 36 +++--- .../log4j/util/StringParameterParserTest.java | 49 +++----- .../logging/log4j/message/MessageFactory.java | 4 +- .../message/ReusableParameterizedMessage.java | 5 +- .../apache/logging/log4j/spi/AbstractLogger.java | 5 +- .../logging/log4j/spi/QueueingRecyclerFactory.java | 14 ++- .../logging/log4j/spi/RecyclerFactories.java | 64 ++++------ .../log4j/spi/ThreadLocalRecyclerFactory.java | 16 +-- .../apache/logging/log4j/status/StatusLogger.java | 4 +- .../util/{Queues.java => QueueFactories.java} | 106 ++++++++++------ .../apache/logging/log4j/util/QueueFactory.java | 9 ++ .../logging/log4j/util/StringParameterParser.java | 15 +-- .../logging/log4j/core/impl/MutableLogEvent.java | 3 +- .../logging/log4j/spi/RecyclerFactoriesTest.java | 140 --------------------- .../java/org/apache/logging/slf4j/SLF4JLogger.java | 11 +- 16 files changed, 290 insertions(+), 292 deletions(-) diff --git a/log4j-api-test/src/test/java/org/apache/logging/log4j/spi/RecyclerFactoriesTest.java b/log4j-api-test/src/test/java/org/apache/logging/log4j/spi/RecyclerFactoriesTest.java new file mode 100644 index 0000000000..265b5cb16f --- /dev/null +++ b/log4j-api-test/src/test/java/org/apache/logging/log4j/spi/RecyclerFactoriesTest.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.spi; + +import java.util.ArrayDeque; +import java.util.concurrent.ArrayBlockingQueue; + +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.Test; + +public class RecyclerFactoriesTest { + + @Test + void DummyRecyclerFactory_should_work() { + final Object actualDummyRecyclerFactory = RecyclerFactories.ofSpec("dummy"); + Assertions + .assertThat(actualDummyRecyclerFactory) + .isSameAs(DummyRecyclerFactory.getInstance()); + } + + @Test + void ThreadLocalRecyclerFactory_should_work() { + final Object actualThreadLocalRecyclerFactory = RecyclerFactories.ofSpec("threadLocal"); + Assertions + .assertThat(actualThreadLocalRecyclerFactory) + .isSameAs(ThreadLocalRecyclerFactory.getInstance()); + } + + @Test + void QueueingRecyclerFactory_should_work() { + final Object actualQueueingRecyclerFactory = RecyclerFactories.ofSpec("queue"); + Assertions + .assertThat(actualQueueingRecyclerFactory) + .isInstanceOf(QueueingRecyclerFactory.class); + } + + @Test + void QueueingRecyclerFactory_should_work_with_supplier() { + final Object recyclerFactory = RecyclerFactories.ofSpec("queue:supplier=java.util.ArrayDeque.new"); + Assertions + .assertThat(recyclerFactory) + .isInstanceOf(QueueingRecyclerFactory.class); + final QueueingRecyclerFactory queueingRecyclerFactory = (QueueingRecyclerFactory) recyclerFactory; + final Recycler<Object> recycler = queueingRecyclerFactory.create(Object::new); + Assertions + .assertThat(recycler) + .isInstanceOf(QueueingRecyclerFactory.QueueingRecycler.class); + final QueueingRecyclerFactory.QueueingRecycler<Object> queueingRecycler = + (QueueingRecyclerFactory.QueueingRecycler<Object>) recycler; + Assertions + .assertThat(queueingRecycler.getQueue()) + .isInstanceOf(ArrayDeque.class); + } + + @Test + void QueueingRecyclerFactory_should_work_with_capacity() { + final Object actualQueueingRecyclerFactory = RecyclerFactories.ofSpec("queue:capacity=100"); + Assertions + .assertThat(actualQueueingRecyclerFactory) + .isInstanceOf(QueueingRecyclerFactory.class); + } + + @Test + void QueueingRecyclerFactory_should_work_with_supplier_and_capacity() { + final Object recyclerFactory = RecyclerFactories.ofSpec( + "queue:" + + "supplier=java.util.concurrent.ArrayBlockingQueue.new," + + "capacity=100"); + Assertions + .assertThat(recyclerFactory) + .isInstanceOf(QueueingRecyclerFactory.class); + final QueueingRecyclerFactory queueingRecyclerFactory = (QueueingRecyclerFactory) recyclerFactory; + final Recycler<Object> recycler = queueingRecyclerFactory.create(Object::new); + Assertions + .assertThat(recycler) + .isInstanceOf(QueueingRecyclerFactory.QueueingRecycler.class); + final QueueingRecyclerFactory.QueueingRecycler<Object> queueingRecycler = + (QueueingRecyclerFactory.QueueingRecycler<Object>) recycler; + Assertions + .assertThat(queueingRecycler.getQueue()) + .isInstanceOf(ArrayBlockingQueue.class); + final ArrayBlockingQueue<Object> queue = (ArrayBlockingQueue<Object>) queueingRecycler.getQueue(); + Assertions.assertThat(queue.remainingCapacity()).isEqualTo(100); + + } + +} diff --git a/log4j-api-test/src/test/java/org/apache/logging/log4j/spi/ThreadLocalRecyclerFactoryTest.java b/log4j-api-test/src/test/java/org/apache/logging/log4j/spi/ThreadLocalRecyclerFactoryTest.java index b7ab339375..71334fdb90 100644 --- a/log4j-api-test/src/test/java/org/apache/logging/log4j/spi/ThreadLocalRecyclerFactoryTest.java +++ b/log4j-api-test/src/test/java/org/apache/logging/log4j/spi/ThreadLocalRecyclerFactoryTest.java @@ -30,44 +30,38 @@ import static org.assertj.core.api.Assertions.assertThat; class ThreadLocalRecyclerFactoryTest { - static class RecyclableObject { - boolean using; - boolean returned; - } + private static class RecyclableObject {} private Recycler<RecyclableObject> recycler; - private Queue<RecyclableObject> getRecyclerQueue() { - return ((ThreadLocalRecyclerFactory.ThreadLocalRecycler<RecyclableObject>) recycler).getQueue(); - } + private Queue<RecyclableObject> recyclerQueue; @BeforeEach void setUp() { - recycler = ThreadLocalRecyclerFactory.getInstance().create(RecyclableObject::new, object -> { - object.using = true; - object.returned = false; - }); + recycler = ThreadLocalRecyclerFactory.getInstance().create(RecyclableObject::new); + recyclerQueue = ((ThreadLocalRecyclerFactory.ThreadLocalRecycler<RecyclableObject>) recycler).getQueue(); } @ParameterizedTest @IntRangeSource(from = 1, to = ThreadLocalRecyclerFactory.MAX_QUEUE_SIZE, closed = true) - void nestedAcquiresDoNotInterfere(int acquisitionCount) { + void nested_acquires_should_not_interfere(final int acquisitionCount) { + // pool should start empty - assertThat(getRecyclerQueue()).isEmpty(); + assertThat(recyclerQueue).isEmpty(); final List<RecyclableObject> acquiredObjects = IntStream.range(0, acquisitionCount) .mapToObj(i -> recycler.acquire()) .collect(Collectors.toList()); // still nothing returned to pool - assertThat(getRecyclerQueue()).isEmpty(); + 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(getRecyclerQueue()).hasSize(acquisitionCount); + 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 @@ -76,11 +70,13 @@ class ThreadLocalRecyclerFactoryTest { .collect(Collectors.toList()); assertThat(reacquiredObjects).containsExactlyElementsOf(acquiredObjects); + } @Test - void nestedAcquiresPastMaximumQueueSizeShouldDiscardExtraReleases() { - assertThat(getRecyclerQueue()).isEmpty(); + void nested_acquires_past_max_queue_size_should_discard_extra_releases() { + + assertThat(recyclerQueue).isEmpty(); // simulate a massively callstack with tons of logging final List<RecyclableObject> acquiredObjects = IntStream.range(0, 1024) @@ -88,13 +84,15 @@ class ThreadLocalRecyclerFactoryTest { .collect(Collectors.toList()); // still nothing returned to pool - assertThat(getRecyclerQueue()).isEmpty(); + assertThat(recyclerQueue).isEmpty(); // don't want any duplicate instances assertThat(acquiredObjects).containsOnlyOnceElementsOf(acquiredObjects); acquiredObjects.forEach(recycler::release); // upon return, we should only have ThreadLocalRecyclerFactory.MAX_QUEUE_SIZE retained for future use - assertThat(getRecyclerQueue()).hasSize(ThreadLocalRecyclerFactory.MAX_QUEUE_SIZE); + assertThat(recyclerQueue).hasSize(ThreadLocalRecyclerFactory.MAX_QUEUE_SIZE); + } + } diff --git a/log4j-layout-template-json-test/src/test/java/org/apache/logging/log4j/util/StringParameterParserTest.java b/log4j-api-test/src/test/java/org/apache/logging/log4j/util/StringParameterParserTest.java similarity index 88% rename from log4j-layout-template-json-test/src/test/java/org/apache/logging/log4j/util/StringParameterParserTest.java rename to log4j-api-test/src/test/java/org/apache/logging/log4j/util/StringParameterParserTest.java index 9e17e08e12..e37dc0a159 100644 --- a/log4j-layout-template-json-test/src/test/java/org/apache/logging/log4j/util/StringParameterParserTest.java +++ b/log4j-api-test/src/test/java/org/apache/logging/log4j/util/StringParameterParserTest.java @@ -16,18 +16,9 @@ */ package org.apache.logging.log4j.util; -import java.util.Collections; -import java.util.LinkedHashMap; -import java.util.LinkedHashSet; -import java.util.Map; -import java.util.Set; - -import org.apache.logging.log4j.util.StringParameterParser; -import org.apache.logging.log4j.util.StringParameterParser.DoubleQuotedStringValue; -import org.apache.logging.log4j.util.StringParameterParser.NullValue; -import org.apache.logging.log4j.util.StringParameterParser.StringValue; -import org.apache.logging.log4j.util.StringParameterParser.Value; -import org.apache.logging.log4j.util.StringParameterParser.Values; +import java.util.*; + +import org.apache.logging.log4j.util.StringParameterParser.*; import org.assertj.core.api.Assertions; import org.junit.jupiter.api.Test; @@ -94,7 +85,7 @@ class StringParameterParserTest { void test_null_value_2() { testSuccess( "a,b=c,d=", - new LinkedHashMap<String, Value>() {{ + new LinkedHashMap<>() {{ put("a", Values.nullValue()); put("b", Values.stringValue("c")); put("d", Values.nullValue()); @@ -105,7 +96,7 @@ class StringParameterParserTest { void test_null_value_3() { testSuccess( "a,b=c,d", - new LinkedHashMap<String, Value>() {{ + new LinkedHashMap<>() {{ put("a", Values.nullValue()); put("b", Values.stringValue("c")); put("d", Values.nullValue()); @@ -116,7 +107,7 @@ class StringParameterParserTest { void test_null_value_4() { testSuccess( "a,b=\"c,=\\\"\",d=,e=f", - new LinkedHashMap<String, Value>() {{ + new LinkedHashMap<>() {{ put("a", Values.nullValue()); put("b", Values.doubleQuotedStringValue("c,=\"")); put("d", Values.nullValue()); @@ -128,7 +119,7 @@ class StringParameterParserTest { void test_two_pairs() { testSuccess( "a=b,c=d", - new LinkedHashMap<String, Value>() {{ + new LinkedHashMap<>() {{ put("a", Values.stringValue("b")); put("c", Values.stringValue("d")); }}); @@ -145,7 +136,7 @@ class StringParameterParserTest { void test_quoted_string_02() { testSuccess( "a=\"b\",c=d", - new LinkedHashMap<String, Value>() {{ + new LinkedHashMap<>() {{ put("a", Values.doubleQuotedStringValue("b")); put("c", Values.stringValue("d")); }}); @@ -155,7 +146,7 @@ class StringParameterParserTest { void test_quoted_string_03() { testSuccess( "a=b,c=\"d\"", - new LinkedHashMap<String, Value>() {{ + new LinkedHashMap<>() {{ put("a", Values.stringValue("b")); put("c", Values.doubleQuotedStringValue("d")); }}); @@ -165,7 +156,7 @@ class StringParameterParserTest { void test_quoted_string_04() { testSuccess( "a=\"b\",c=\"d\"", - new LinkedHashMap<String, Value>() {{ + new LinkedHashMap<>() {{ put("a", Values.doubleQuotedStringValue("b")); put("c", Values.doubleQuotedStringValue("d")); }}); @@ -189,7 +180,7 @@ class StringParameterParserTest { void test_quoted_string_07() { testSuccess( "a=\"\\\"b\",c=d", - new LinkedHashMap<String, Value>() {{ + new LinkedHashMap<>() {{ put("a", Values.doubleQuotedStringValue("\"b")); put("c", Values.stringValue("d")); }}); @@ -209,7 +200,7 @@ class StringParameterParserTest { void test_quoted_string_09() { testSuccess( "a=\"\\\"b,\",c=d", - new LinkedHashMap<String, Value>() {{ + new LinkedHashMap<>() {{ put("a", Values.doubleQuotedStringValue("\"b,")); put("c", Values.stringValue("d")); }}); @@ -219,7 +210,7 @@ class StringParameterParserTest { void test_quoted_string_10() { testSuccess( "a=\"\\\"b\\\",\",c=d", - new LinkedHashMap<String, Value>() {{ + new LinkedHashMap<>() {{ put("a", Values.doubleQuotedStringValue("\"b\",")); put("c", Values.stringValue("d")); }}); @@ -229,7 +220,7 @@ class StringParameterParserTest { void test_quoted_string_11() { testSuccess( "a=\"\\\"b\",c=\"d\"", - new LinkedHashMap<String, Value>() {{ + new LinkedHashMap<>() {{ put("a", Values.doubleQuotedStringValue("\"b")); put("c", Values.doubleQuotedStringValue("d")); }}); @@ -239,7 +230,7 @@ class StringParameterParserTest { void test_quoted_string_12() { testSuccess( "a=\"\\\"b\\\"\",c=\"d\"", - new LinkedHashMap<String, Value>() {{ + new LinkedHashMap<>() {{ put("a", Values.doubleQuotedStringValue("\"b\"")); put("c", Values.doubleQuotedStringValue("d")); }}); @@ -259,7 +250,7 @@ class StringParameterParserTest { void test_quoted_string_14() { testSuccess( "a=\"\\\"b\\\",\",c=\"\\\"d\\\"\"", - new LinkedHashMap<String, Value>() {{ + new LinkedHashMap<>() {{ put("a", Values.doubleQuotedStringValue("\"b\",")); put("c", Values.doubleQuotedStringValue("\"d\"")); }}); @@ -269,7 +260,7 @@ class StringParameterParserTest { void test_quoted_string_15() { testSuccess( "a=\"\\\"b\",c=\",d\"", - new LinkedHashMap<String, Value>() {{ + new LinkedHashMap<>() {{ put("a", Values.doubleQuotedStringValue("\"b")); put("c", Values.doubleQuotedStringValue(",d")); }}); @@ -279,7 +270,7 @@ class StringParameterParserTest { void test_quoted_string_16() { testSuccess( "a=\"\\\"b\\\"\",c=\",d\"", - new LinkedHashMap<String, Value>() {{ + new LinkedHashMap<>() {{ put("a", Values.doubleQuotedStringValue("\"b\"")); put("c", Values.doubleQuotedStringValue(",d")); }}); @@ -289,7 +280,7 @@ class StringParameterParserTest { void test_quoted_string_17() { testSuccess( "a=\"\\\"b,\",c=\"\\\"d,\"", - new LinkedHashMap<String, Value>() {{ + new LinkedHashMap<>() {{ put("a", Values.doubleQuotedStringValue("\"b,")); put("c", Values.doubleQuotedStringValue("\"d,")); }}); @@ -299,7 +290,7 @@ class StringParameterParserTest { void test_quoted_string_18() { testSuccess( "a=\"\\\"b\\\",\",c=\"\\\"d\\\",\"", - new LinkedHashMap<String, Value>() {{ + new LinkedHashMap<>() {{ put("a", Values.doubleQuotedStringValue("\"b\",")); put("c", Values.doubleQuotedStringValue("\"d\",")); }}); diff --git a/log4j-api/src/main/java/org/apache/logging/log4j/message/MessageFactory.java b/log4j-api/src/main/java/org/apache/logging/log4j/message/MessageFactory.java index 142d63b1d3..fa777d3110 100644 --- a/log4j-api/src/main/java/org/apache/logging/log4j/message/MessageFactory.java +++ b/log4j-api/src/main/java/org/apache/logging/log4j/message/MessageFactory.java @@ -16,6 +16,8 @@ */ package org.apache.logging.log4j.message; +import org.apache.logging.log4j.spi.Recycler; + /** * Creates messages. Implementations can provide different message format syntaxes. * If messages created by an implementation are reusable, then the {@link #recycle(Message)} method must @@ -243,7 +245,7 @@ public interface MessageFactory { * Recycles a message back for potential reuse or cleanup. * * @since 3.0.0 - * @see org.apache.logging.log4j.spi.Recycler + * @see Recycler */ default void recycle(Message message) { if (message instanceof ReusableMessage) { diff --git a/log4j-api/src/main/java/org/apache/logging/log4j/message/ReusableParameterizedMessage.java b/log4j-api/src/main/java/org/apache/logging/log4j/message/ReusableParameterizedMessage.java index 24982934d2..4318a81b48 100644 --- a/log4j-api/src/main/java/org/apache/logging/log4j/message/ReusableParameterizedMessage.java +++ b/log4j-api/src/main/java/org/apache/logging/log4j/message/ReusableParameterizedMessage.java @@ -46,7 +46,7 @@ public class ReusableParameterizedMessage implements ReusableMessage, ParameterV private Object[] params = new Object[MAX_PARMS]; private Throwable throwable; - private final Recycler<StringBuilder> bufferRecycler; // non-static: LOG4J2-1583 + private final Recycler<StringBuilder> bufferRecycler; /** * Creates a reusable message. @@ -59,7 +59,8 @@ public class ReusableParameterizedMessage implements ReusableMessage, ParameterV bufferRecycler = recyclerFactory.create( () -> { final int currentPatternLength = messagePattern == null ? 0 : messagePattern.length(); - return new StringBuilder(Math.max(MIN_BUILDER_SIZE, currentPatternLength * 2)); + int capacity = Math.max(MIN_BUILDER_SIZE, Math.multiplyExact(currentPatternLength, 2)); + return new StringBuilder(capacity); }, buffer -> { StringBuilders.trimToMaxSize(buffer, Constants.MAX_REUSABLE_MESSAGE_SIZE); diff --git a/log4j-api/src/main/java/org/apache/logging/log4j/spi/AbstractLogger.java b/log4j-api/src/main/java/org/apache/logging/log4j/spi/AbstractLogger.java index 043da09bc6..06277b481d 100644 --- a/log4j-api/src/main/java/org/apache/logging/log4j/spi/AbstractLogger.java +++ b/log4j-api/src/main/java/org/apache/logging/log4j/spi/AbstractLogger.java @@ -69,7 +69,8 @@ public abstract class AbstractLogger implements ExtendedLogger { private final MessageFactory messageFactory; private final FlowMessageFactory flowMessageFactory; private static final ThreadLocal<int[]> recursionDepthHolder = new ThreadLocal<>(); // LOG4J2-1518, LOG4J2-2031 - protected final Recycler<LogBuilder> recycler = LoggingSystem.getRecyclerFactory() + protected final Recycler<DefaultLogBuilder> recycler = LoggingSystem + .getRecyclerFactory() .create(() -> new DefaultLogBuilder(this, null)); /** @@ -2747,7 +2748,7 @@ public abstract class AbstractLogger implements ExtendedLogger { * @since 2.20.0 */ protected LogBuilder getLogBuilder(Level level) { - DefaultLogBuilder builder = (DefaultLogBuilder) recycler.acquire(); + DefaultLogBuilder builder = recycler.acquire(); return builder.reset(this, level); } diff --git a/log4j-api/src/main/java/org/apache/logging/log4j/spi/QueueingRecyclerFactory.java b/log4j-api/src/main/java/org/apache/logging/log4j/spi/QueueingRecyclerFactory.java index 7579566ef1..e0b5a17a42 100644 --- a/log4j-api/src/main/java/org/apache/logging/log4j/spi/QueueingRecyclerFactory.java +++ b/log4j-api/src/main/java/org/apache/logging/log4j/spi/QueueingRecyclerFactory.java @@ -22,21 +22,28 @@ import java.util.function.Supplier; import org.apache.logging.log4j.util.QueueFactory; +import static java.util.Objects.requireNonNull; + +/** + * A {@link RecyclerFactory} pooling objects in a queue created using the provided {@link QueueFactory}. + */ public class QueueingRecyclerFactory implements RecyclerFactory { private final QueueFactory queueFactory; public QueueingRecyclerFactory(final QueueFactory queueFactory) { - this.queueFactory = queueFactory; + this.queueFactory = requireNonNull(queueFactory, "queueFactory"); } @Override public <V> Recycler<V> create(final Supplier<V> supplier, final Consumer<V> cleaner) { + requireNonNull(supplier, "supplier"); + requireNonNull(cleaner, "cleaner"); final Queue<V> queue = queueFactory.create(); return new QueueingRecycler<>(supplier, cleaner, queue); } - // Visible for tests. + // Visible for tests static class QueueingRecycler<V> implements Recycler<V> { private final Supplier<V> supplier; @@ -54,7 +61,7 @@ public class QueueingRecyclerFactory implements RecyclerFactory { this.queue = queue; } - // Visible for tests. + // Visible for tests Queue<V> getQueue() { return queue; } @@ -67,6 +74,7 @@ public class QueueingRecyclerFactory implements RecyclerFactory { @Override public void release(final V value) { + requireNonNull(value, "value"); cleaner.accept(value); queue.offer(value); } diff --git a/log4j-api/src/main/java/org/apache/logging/log4j/spi/RecyclerFactories.java b/log4j-api/src/main/java/org/apache/logging/log4j/spi/RecyclerFactories.java index 665bd2b215..55510019c8 100644 --- a/log4j-api/src/main/java/org/apache/logging/log4j/spi/RecyclerFactories.java +++ b/log4j-api/src/main/java/org/apache/logging/log4j/spi/RecyclerFactories.java @@ -19,26 +19,24 @@ package org.apache.logging.log4j.spi; import java.util.Map; import java.util.Set; +import org.apache.logging.log4j.util.QueueFactories; import org.apache.logging.log4j.util.QueueFactory; -import org.apache.logging.log4j.util.Queues; import org.apache.logging.log4j.util.StringParameterParser; import static org.apache.logging.log4j.util.Constants.isThreadLocalsEnabled; public final class RecyclerFactories { - private RecyclerFactories() {} + private static final int DEFAULT_QUEUE_CAPACITY = Math.max( + 2 * Runtime.getRuntime().availableProcessors() + 1, + 8); - private static int getDefaultCapacity() { - return Math.max( - 2 * Runtime.getRuntime().availableProcessors() + 1, - 8); - } + private RecyclerFactories() {} public static RecyclerFactory getDefault() { return isThreadLocalsEnabled() ? ThreadLocalRecyclerFactory.getInstance() - : new QueueingRecyclerFactory(Queues.MPMC.factory(getDefaultCapacity())); + : new QueueingRecyclerFactory(QueueFactories.MPMC.factory(DEFAULT_QUEUE_CAPACITY)); } public static RecyclerFactory ofSpec(final String recyclerFactorySpec) { @@ -60,11 +58,7 @@ public final class RecyclerFactories { // Is a queueing factory requested? else if (recyclerFactorySpec.startsWith("queue")) { - - // Determine the default capacity. - final int defaultCapacity = getDefaultCapacity(); - - return readQueueingRecyclerFactory(recyclerFactorySpec, defaultCapacity); + return readQueueingRecyclerFactory(recyclerFactorySpec); } // Bogus input, bail out. @@ -75,53 +69,41 @@ public final class RecyclerFactories { } - private static RecyclerFactory readQueueingRecyclerFactory( - final String recyclerFactorySpec, - final int defaultCapacity) { + private static RecyclerFactory readQueueingRecyclerFactory(final String recyclerFactorySpec) { // Parse the spec. final String queueFactorySpec = recyclerFactorySpec.substring( - "queue".length() + - (recyclerFactorySpec.startsWith("queue:") - ? 1 - : 0)); + "queue".length() + (recyclerFactorySpec.startsWith("queue:") ? 1 : 0)); final Map<String, StringParameterParser.Value> parsedValues = - StringParameterParser.parse( - queueFactorySpec, Set.of("supplier", "capacity")); + StringParameterParser.parse(queueFactorySpec, Set.of("supplier", "capacity")); // Read the capacity. final StringParameterParser.Value capacityValue = parsedValues.get("capacity"); final int capacity; if (capacityValue == null || capacityValue instanceof StringParameterParser.NullValue) { - capacity = defaultCapacity; + capacity = DEFAULT_QUEUE_CAPACITY; } else { try { capacity = Integer.parseInt(capacityValue.toString()); } catch (final NumberFormatException error) { throw new IllegalArgumentException( - "failed reading capacity in queueing recycler " + - "factory: " + queueFactorySpec, error); + "failed reading capacity in queueing recycler factory: " + queueFactorySpec, + error); } } - // Read the supplier path. + // Read the supplier path final StringParameterParser.Value supplierValue = parsedValues.get("supplier"); - final String supplierPath; - if (supplierValue == null || supplierValue instanceof StringParameterParser.NullValue) { - supplierPath = null; - } else { - supplierPath = supplierValue.toString(); - } - - // Execute the read spec. - final QueueFactory queueFactory; - if (supplierPath != null) { - queueFactory = Queues.createQueueFactory(supplierPath, capacity); - } else { - queueFactory = Queues.MPMC.factory(capacity); - } - + final String supplierPath = supplierValue == null || supplierValue instanceof StringParameterParser.NullValue + ? null + : supplierValue.toString(); + + // Execute the read spec + final QueueFactory queueFactory = supplierPath != null + ? QueueFactories.createQueueFactory(supplierPath, capacity) + : QueueFactories.MPMC.factory(capacity); return new QueueingRecyclerFactory(queueFactory); + } } diff --git a/log4j-api/src/main/java/org/apache/logging/log4j/spi/ThreadLocalRecyclerFactory.java b/log4j-api/src/main/java/org/apache/logging/log4j/spi/ThreadLocalRecyclerFactory.java index 65a8d1915f..43144a5ae8 100644 --- a/log4j-api/src/main/java/org/apache/logging/log4j/spi/ThreadLocalRecyclerFactory.java +++ b/log4j-api/src/main/java/org/apache/logging/log4j/spi/ThreadLocalRecyclerFactory.java @@ -20,13 +20,14 @@ import java.util.Queue; import java.util.function.Consumer; import java.util.function.Supplier; -import org.apache.logging.log4j.util.Queues; +import org.apache.logging.log4j.util.QueueFactories; /** - * Recycling strategy that caches instances in a ThreadLocal value to allow threads to reuse objects. 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; a {@linkplain QueueingRecyclerFactory queue-based approach} - * is more flexible. + * A {@link RecyclerFactory} pooling objects in a 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 QueueingRecyclerFactory}. + * </p> * * @since 3.0.0 */ @@ -39,8 +40,7 @@ public class ThreadLocalRecyclerFactory implements RecyclerFactory { // Visible for testing static final int MAX_QUEUE_SIZE = 8; - private static final ThreadLocalRecyclerFactory INSTANCE = - new ThreadLocalRecyclerFactory(); + private static final ThreadLocalRecyclerFactory INSTANCE = new ThreadLocalRecyclerFactory(); private ThreadLocalRecyclerFactory() {} @@ -65,7 +65,7 @@ public class ThreadLocalRecyclerFactory implements RecyclerFactory { private ThreadLocalRecycler(final Supplier<V> supplier, final Consumer<V> cleaner) { this.supplier = supplier; this.cleaner = cleaner; - this.holder = ThreadLocal.withInitial(() -> Queues.SPSC.create(MAX_QUEUE_SIZE)); + this.holder = ThreadLocal.withInitial(() -> QueueFactories.SPSC.create(MAX_QUEUE_SIZE)); } @Override diff --git a/log4j-api/src/main/java/org/apache/logging/log4j/status/StatusLogger.java b/log4j-api/src/main/java/org/apache/logging/log4j/status/StatusLogger.java index d25e3aedf6..6387829e2f 100644 --- a/log4j-api/src/main/java/org/apache/logging/log4j/status/StatusLogger.java +++ b/log4j-api/src/main/java/org/apache/logging/log4j/status/StatusLogger.java @@ -34,7 +34,7 @@ import org.apache.logging.log4j.simple.SimpleLoggerContext; import org.apache.logging.log4j.spi.AbstractLogger; import org.apache.logging.log4j.spi.LoggingSystemProperties; import org.apache.logging.log4j.util.LowLevelLogUtil; -import org.apache.logging.log4j.util.Queues; +import org.apache.logging.log4j.util.QueueFactories; /** * Records events that occur in the logging system. By default, only error messages are logged to {@link System#err}. @@ -97,7 +97,7 @@ public final class StatusLogger extends AbstractLogger { this.logger = logger; this.configuration = configuration; this.listenersLevel = configuration.getDefaultLevel().intLevel(); - messages = Queues.MPMC.create(configuration.getMaxEntries()); + messages = QueueFactories.MPMC.create(configuration.getMaxEntries()); } /** diff --git a/log4j-api/src/main/java/org/apache/logging/log4j/util/Queues.java b/log4j-api/src/main/java/org/apache/logging/log4j/util/QueueFactories.java similarity index 66% rename from log4j-api/src/main/java/org/apache/logging/log4j/util/Queues.java rename to log4j-api/src/main/java/org/apache/logging/log4j/util/QueueFactories.java index 2f0167f2ea..0d2f04216b 100644 --- a/log4j-api/src/main/java/org/apache/logging/log4j/util/Queues.java +++ b/log4j-api/src/main/java/org/apache/logging/log4j/util/QueueFactories.java @@ -27,38 +27,40 @@ import org.jctools.queues.SpmcArrayQueue; import org.jctools.queues.SpscArrayQueue; /** - * Provides {@link QueueFactory} and {@link Queue} instances for different use cases. When the - * <a href="https://jctools.github.io/JCTools/">JCTools</a> library is included at runtime, then - * the specialized lock free or wait free queues are used from there. Otherwise, {@link ArrayBlockingQueue} - * is provided as a fallback for thread-safety. Custom implementations of {@link QueueFactory} may also be - * created via {@link #createQueueFactory(String, int)}. + * Provides {@link QueueFactory} and {@link Queue} instances for different use cases. + * <p> + * Implementations provided by <a href="https://jctools.github.io/JCTools/">JCTools</a> will be preferred, if available at runtime. + * Otherwise, {@link ArrayBlockingQueue} will be used. + * </p> + * + * @since 3.0.0 */ @InternalApi -public enum Queues { +public enum QueueFactories { + /** - * Provides a bounded queue for single-producer/single-consumer usage. Only one thread may offer objects - * while only one thread may poll for them. + * Provides a bounded queue for single-producer/single-consumer usage. */ SPSC(Lazy.lazy(JCToolsQueueFactory.SPSC::load)), + /** - * Provides a bounded queue for multi-producer/single-consumer usage. Any thread may offer objects while only - * one thread may poll for them. + * Provides a bounded queue for multi-producer/single-consumer usage. */ MPSC(Lazy.lazy(JCToolsQueueFactory.MPSC::load)), + /** - * Provides a bounded queue for single-producer/multi-consumer usage. Only one thread may offer objects but - * any thread may poll for them. + * Provides a bounded queue for single-producer/multi-consumer usage. */ SPMC(Lazy.lazy(JCToolsQueueFactory.SPMC::load)), + /** - * Provides a bounded queue for multi-producer/multi-consumer usage. Any thread may offer objects and any thread - * may poll for them. + * Provides a bounded queue for multi-producer/multi-consumer usage. */ MPMC(Lazy.lazy(JCToolsQueueFactory.MPMC::load)); private final Lazy<BoundedQueueFactory> queueFactory; - Queues(final Lazy<BoundedQueueFactory> queueFactory) { + QueueFactories(final Lazy<BoundedQueueFactory> queueFactory) { this.queueFactory = queueFactory; } @@ -70,10 +72,25 @@ public enum Queues { return queueFactory.get().create(capacity); } + /** + * Creates a {@link QueueFactory} producing queues of provided capacity from the provided supplier. + * <p> + * A supplier path must be formatted as follows: + * <ul> + * <li>{@code <fully-qualified-class-name>.new} – the class constructor accepting a single {@code int} argument (denoting the capacity) will be used (e.g., {@code org.jctools.queues.MpmcArrayQueue.new})</li> + * <li>{@code <fully-qualified-class-name>.<static-factory-method>} – the static factory method accepting a single {@code int} argument (denoting the capacity) will be used (e.g., {@code com.acme.Queues.createBoundedQueue})</li> + * </ul> + * </p> + * + * @param supplierPath a queue supplier path (e.g., {@code org.jctools.queues.MpmcArrayQueue.new}, {@code com.acme.Queues.createBoundedQueue}) + * @param capacity the capacity that will be passed to the queue supplier + * @return a new {@link QueueFactory} instance + */ public static QueueFactory createQueueFactory(final String supplierPath, final int capacity) { final int supplierPathSplitterIndex = supplierPath.lastIndexOf('.'); if (supplierPathSplitterIndex < 0) { - throw new IllegalArgumentException("invalid supplier in queue factory: " + supplierPath); + final String message = String.format("invalid queue factory supplier path: `%s`", supplierPath); + throw new IllegalArgumentException(message); } final String supplierClassName = supplierPath.substring(0, supplierPathSplitterIndex); final String supplierMethodName = supplierPath.substring(supplierPathSplitterIndex + 1); @@ -81,22 +98,24 @@ public enum Queues { final Class<?> supplierClass = LoaderUtil.loadClass(supplierClassName); final BoundedQueueFactory queueFactory; if ("new".equals(supplierMethodName)) { - final Constructor<?> supplierCtor = - supplierClass.getDeclaredConstructor(int.class); + final Constructor<?> supplierCtor = supplierClass.getDeclaredConstructor(int.class); queueFactory = new ConstructorProvidedQueueFactory(supplierCtor); } else { - final Method supplierMethod = - supplierClass.getMethod(supplierMethodName, int.class); + final Method supplierMethod = supplierClass.getMethod(supplierMethodName, int.class); queueFactory = new StaticMethodProvidedQueueFactory(supplierMethod); } return new ProxyQueueFactory(queueFactory, capacity); } catch (final ReflectiveOperationException | LinkageError | SecurityException error) { - throw new RuntimeException("failed executing queue factory", error); + final String message = String.format( + "failed to create the queue factory using the supplier path `%s`", supplierPath); + throw new RuntimeException(message, error); } } - private static class ProxyQueueFactory implements QueueFactory { + private static final class ProxyQueueFactory implements QueueFactory { + private final BoundedQueueFactory factory; + private final int capacity; private ProxyQueueFactory(final BoundedQueueFactory factory, final int capacity) { @@ -108,38 +127,52 @@ public enum Queues { public <E> Queue<E> create() { return factory.create(capacity); } + } + @FunctionalInterface private interface BoundedQueueFactory { + <E> Queue<E> create(final int capacity); + } - private static class ArrayBlockingQueueFactory implements BoundedQueueFactory { + private static final class ArrayBlockingQueueFactory implements BoundedQueueFactory { + + private static final ArrayBlockingQueueFactory INSTANCE = new ArrayBlockingQueueFactory(); + + private ArrayBlockingQueueFactory() {} + @Override public <E> Queue<E> create(final int capacity) { return new ArrayBlockingQueue<>(capacity); } + } private enum JCToolsQueueFactory implements BoundedQueueFactory { + SPSC { @Override public <E> Queue<E> create(final int capacity) { return new SpscArrayQueue<>(capacity); } }, + MPSC { @Override public <E> Queue<E> create(final int capacity) { return new MpscArrayQueue<>(capacity); } }, + SPMC { @Override public <E> Queue<E> create(final int capacity) { return new SpmcArrayQueue<>(capacity); } }, + MPMC { @Override public <E> Queue<E> create(final int capacity) { @@ -147,21 +180,20 @@ public enum Queues { } }; - BoundedQueueFactory load() { + private BoundedQueueFactory load() { try { - // if JCTools is unavailable at runtime, then we'll only find out once we attempt to invoke - // BoundedQueueFactory::create which is the first time the ClassLoader will try to link the - // referenced JCTools class causing a NoClassDefFoundError or some other LinkageError potentially. - // also, test with a large enough capacity to avoid any IllegalArgumentExceptions from trivial queues + // Test with a large enough capacity to avoid any `IllegalArgumentExceptions` from trivial queues create(16); return this; } catch (final LinkageError ignored) { - return new ArrayBlockingQueueFactory(); + return ArrayBlockingQueueFactory.INSTANCE; } } + } - private static class ConstructorProvidedQueueFactory implements BoundedQueueFactory { + private static final class ConstructorProvidedQueueFactory implements BoundedQueueFactory { + private final Constructor<?> constructor; private ConstructorProvidedQueueFactory(final Constructor<?> constructor) { @@ -173,13 +205,15 @@ public enum Queues { final Constructor<Queue<E>> typedConstructor = Cast.cast(constructor); try { return typedConstructor.newInstance(capacity); - } catch (final ReflectiveOperationException e) { - throw new RuntimeException("queue construction failed for factory", e); + } catch (final ReflectiveOperationException error) { + throw new RuntimeException("queue construction failure", error); } } + } - private static class StaticMethodProvidedQueueFactory implements BoundedQueueFactory { + private static final class StaticMethodProvidedQueueFactory implements BoundedQueueFactory { + private final Method method; private StaticMethodProvidedQueueFactory(final Method method) { @@ -190,9 +224,11 @@ public enum Queues { public <E> Queue<E> create(final int capacity) { try { return Cast.cast(method.invoke(null, capacity)); - } catch (final ReflectiveOperationException e) { - throw new RuntimeException("queue construction failed for factory", e); + } catch (final ReflectiveOperationException error) { + throw new RuntimeException("queue construction failure", error); } } + } + } diff --git a/log4j-api/src/main/java/org/apache/logging/log4j/util/QueueFactory.java b/log4j-api/src/main/java/org/apache/logging/log4j/util/QueueFactory.java index 53a6d6db29..5fcd4240e4 100644 --- a/log4j-api/src/main/java/org/apache/logging/log4j/util/QueueFactory.java +++ b/log4j-api/src/main/java/org/apache/logging/log4j/util/QueueFactory.java @@ -18,6 +18,15 @@ package org.apache.logging.log4j.util; import java.util.Queue; +/** + * A {@link Queue} factory contract. + * + * @see QueueFactories + * @since 3.0.0 + */ +@FunctionalInterface public interface QueueFactory { + <E> Queue<E> create(); + } diff --git a/log4j-api/src/main/java/org/apache/logging/log4j/util/StringParameterParser.java b/log4j-api/src/main/java/org/apache/logging/log4j/util/StringParameterParser.java index 653877c1f4..2e5105b21c 100644 --- a/log4j-api/src/main/java/org/apache/logging/log4j/util/StringParameterParser.java +++ b/log4j-api/src/main/java/org/apache/logging/log4j/util/StringParameterParser.java @@ -16,15 +16,16 @@ */ package org.apache.logging.log4j.util; -import java.util.Collections; -import java.util.LinkedHashMap; -import java.util.Map; -import java.util.Objects; -import java.util.Set; +import java.util.*; import java.util.concurrent.Callable; -import org.apache.logging.log4j.util.Strings; - +/** + * Utility class for parsing string-formatted parameters, e.g., {@code queue:supplier=com.acme.FastestQueue.new,capacity=42}. + * <p> + * See the associated test class for possible combinations and double-, single-quote handling. + * </p> + */ +@InternalApi public final class StringParameterParser { private StringParameterParser() {} 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 a009dd201b..da403a010e 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 @@ -35,6 +35,7 @@ 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; import org.apache.logging.log4j.util.StackLocatorUtil; import org.apache.logging.log4j.util.StringBuilders; import org.apache.logging.log4j.util.StringMap; @@ -43,7 +44,7 @@ import org.apache.logging.log4j.util.Strings; /** * Mutable implementation of the {@code ReusableLogEvent} interface. * @since 2.6 - * @see org.apache.logging.log4j.spi.Recycler + * @see Recycler */ public class MutableLogEvent implements ReusableLogEvent, ReusableMessage, ParameterVisitable { private static final Message EMPTY = new SimpleMessage(Strings.EMPTY); diff --git a/log4j-layout-template-json-test/src/test/java/org/apache/logging/log4j/spi/RecyclerFactoriesTest.java b/log4j-layout-template-json-test/src/test/java/org/apache/logging/log4j/spi/RecyclerFactoriesTest.java deleted file mode 100644 index 74771534aa..0000000000 --- a/log4j-layout-template-json-test/src/test/java/org/apache/logging/log4j/spi/RecyclerFactoriesTest.java +++ /dev/null @@ -1,140 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache license, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the license for the specific language governing permissions and - * limitations under the license. - */ -package org.apache.logging.log4j.spi; - -import java.lang.reflect.Field; -import java.util.ArrayDeque; -import java.util.concurrent.ArrayBlockingQueue; - -import org.apache.logging.log4j.core.test.appender.ListAppender; -import org.apache.logging.log4j.core.test.junit.LoggerContextSource; -import org.apache.logging.log4j.core.test.junit.Named; -import org.apache.logging.log4j.layout.template.json.JsonTemplateLayout; -import org.apache.logging.log4j.plugins.convert.TypeConverter; -import org.apache.logging.log4j.plugins.di.DI; -import org.apache.logging.log4j.plugins.di.Injector; -import org.assertj.core.api.Assertions; -import org.jctools.queues.MpmcArrayQueue; -import org.junit.jupiter.api.Test; - -class RecyclerFactoriesTest { - - @Test - void test_RecyclerFactoryConverter() throws Exception { - - final Injector injector = DI.createInjector(); - injector.init(); - // Check if the type converter is registered. - final TypeConverter<?> converter = injector.getTypeConverter(RecyclerFactory.class); - Assertions.assertThat(converter).isNotNull(); - - // Check dummy recycler factory. - { - final Object actualDummyRecyclerFactory = converter.convert("dummy"); - Assertions - .assertThat(actualDummyRecyclerFactory) - .isSameAs(DummyRecyclerFactory.getInstance()); - } - - // Check thread-local recycler factory. - { - final Object actualThreadLocalRecyclerFactory = converter.convert("threadLocal"); - Assertions - .assertThat(actualThreadLocalRecyclerFactory) - .isSameAs(ThreadLocalRecyclerFactory.getInstance()); - } - - // Check queueing recycler factory. - { - final Object actualQueueingRecyclerFactory = converter.convert("queue"); - Assertions - .assertThat(actualQueueingRecyclerFactory) - .isInstanceOf(QueueingRecyclerFactory.class); - } - - // Check queueing recycler factory with supplier. - { - final Object recyclerFactory = converter.convert( - "queue:supplier=java.util.ArrayDeque.new"); - Assertions - .assertThat(recyclerFactory) - .isInstanceOf(QueueingRecyclerFactory.class); - final QueueingRecyclerFactory queueingRecyclerFactory = - (QueueingRecyclerFactory) recyclerFactory; - final Recycler<Object> recycler = - queueingRecyclerFactory.create(Object::new); - Assertions - .assertThat(recycler) - .isInstanceOf(QueueingRecyclerFactory.QueueingRecycler.class); - final QueueingRecyclerFactory.QueueingRecycler<Object> queueingRecycler = - (QueueingRecyclerFactory.QueueingRecycler<Object>) recycler; - Assertions - .assertThat(queueingRecycler.getQueue()) - .isInstanceOf(ArrayDeque.class); - } - - // Check queueing recycler factory with capacity. - { - final Object actualQueueingRecyclerFactory = converter.convert( - "queue:capacity=100"); - Assertions - .assertThat(actualQueueingRecyclerFactory) - .isInstanceOf(QueueingRecyclerFactory.class); - } - - // Check queueing recycler factory with supplier and capacity. - { - final Object recyclerFactory = converter.convert( - "queue:" + - "supplier=java.util.concurrent.ArrayBlockingQueue.new," + - "capacity=100"); - Assertions - .assertThat(recyclerFactory) - .isInstanceOf(QueueingRecyclerFactory.class); - final QueueingRecyclerFactory queueingRecyclerFactory = - (QueueingRecyclerFactory) recyclerFactory; - final Recycler<Object> recycler = - queueingRecyclerFactory.create(Object::new); - Assertions - .assertThat(recycler) - .isInstanceOf(QueueingRecyclerFactory.QueueingRecycler.class); - final QueueingRecyclerFactory.QueueingRecycler<Object> queueingRecycler = - (QueueingRecyclerFactory.QueueingRecycler<Object>) recycler; - Assertions - .assertThat(queueingRecycler.getQueue()) - .isInstanceOf(ArrayBlockingQueue.class); - final ArrayBlockingQueue<Object> queue = - (ArrayBlockingQueue<Object>) queueingRecycler.getQueue(); - Assertions.assertThat(queue.remainingCapacity()).isEqualTo(100); - } - - } - - @Test - @LoggerContextSource("recyclerFactoryCustomizedJsonTemplateLayoutLogging.xml") - void test_RecyclerFactoryConverter_using_XML_config( - final @Named(value = "List") ListAppender appender) - throws Exception { - final JsonTemplateLayout layout = (JsonTemplateLayout) appender.getLayout(); - final Field field = JsonTemplateLayout.class.getDeclaredField("contextRecycler"); - field.setAccessible(true); - final QueueingRecyclerFactory.QueueingRecycler<?> contextRecycler = (QueueingRecyclerFactory.QueueingRecycler<?>) field.get(layout); - final MpmcArrayQueue<?> queue = (MpmcArrayQueue<?>) contextRecycler.getQueue(); - Assertions.assertThat(queue.capacity()).isEqualTo(512); - } - -} diff --git a/log4j-to-slf4j/src/main/java/org/apache/logging/slf4j/SLF4JLogger.java b/log4j-to-slf4j/src/main/java/org/apache/logging/slf4j/SLF4JLogger.java index 4034b1ba3f..edd8483fe9 100644 --- a/log4j-to-slf4j/src/main/java/org/apache/logging/slf4j/SLF4JLogger.java +++ b/log4j-to-slf4j/src/main/java/org/apache/logging/slf4j/SLF4JLogger.java @@ -40,10 +40,12 @@ public class SLF4JLogger extends AbstractLogger { */ private static final boolean LAZY_LEVEL_CHECK = "ch.qos.logback.classic.LoggerContext" .equals(LoggerFactory.getILoggerFactory().getClass().getName()); - private static final Recycler<SLF4JLogBuilder> logBuilderRecycler = + + private static final Recycler<SLF4JLogBuilder> LOG_BUILDER_RECYCLER = LoggingSystem.getRecyclerFactory().create(SLF4JLogBuilder::new); private final org.slf4j.Logger logger; + private final LocationAwareLogger locationAwareLogger; public SLF4JLogger(final String name, final MessageFactory messageFactory, final org.slf4j.Logger logger) { @@ -271,6 +273,11 @@ public class SLF4JLogger extends AbstractLogger { } } + @Override + public LogBuilder always() { + return atLevel(Level.OFF); + } + @Override public LogBuilder atTrace() { return atLevel(Level.TRACE); @@ -303,7 +310,7 @@ public class SLF4JLogger extends AbstractLogger { @Override protected LogBuilder getLogBuilder(Level level) { - SLF4JLogBuilder builder = logBuilderRecycler.acquire(); + SLF4JLogBuilder builder = LOG_BUILDER_RECYCLER.acquire(); return builder.reset(this, level); }
