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/02/27 09:19:41 UTC
(logging-log4j2) 04/04: Rollback `log4j-api` to version from `2.x`
This is an automated email from the ASF dual-hosted git repository.
pkarwasz pushed a commit to branch fix/rollback-log4j-api
in repository https://gitbox.apache.org/repos/asf/logging-log4j2.git
commit 42a76d2924530ccb6cb0d443e44cdb0f0e1e9324
Author: Piotr P. Karwasz <pi...@karwasz.org>
AuthorDate: Mon Feb 26 16:08:09 2024 +0100
Rollback `log4j-api` to version from `2.x`
---
.../log4j/test/ThreadContextUtilityClass.java | 4 +
.../java/org/apache/logging/log4j/LoggerTest.java | 95 +++-----
.../logging/log4j/NoopThreadContextTest.java | 11 +-
.../log4j/ThreadContextInheritanceTest.java | 68 +++---
.../apache/logging/log4j/util/LoaderUtilTest.java | 61 ------
log4j-api/pom.xml | 8 +
.../java/org/apache/logging/log4j/BridgeAware.java | 4 +-
.../main/java/org/apache/logging/log4j/Level.java | 30 ++-
.../main/java/org/apache/logging/log4j/Logger.java | 132 +++++------
.../main/java/org/apache/logging/log4j/Marker.java | 5 +-
.../org/apache/logging/log4j/MarkerManager.java | 154 +++++++------
.../org/apache/logging/log4j/ThreadContext.java | 119 +++++++---
.../logging/log4j/internal/DefaultLogBuilder.java | 177 +++++++--------
.../log4j/message/AbstractMessageFactory.java | 176 ++++++++++++++-
.../logging/log4j/message/MessageFactory2.java | 180 ++++++++++++++-
.../log4j/message/ParameterizedMessageFactory.java | 192 ++++++++++++++++
.../apache/logging/log4j/spi/AbstractLogger.java | 174 +++++++++++----
.../log4j/spi/CleanableThreadContextMap.java | 20 +-
.../CopyOnWriteSortedArrayThreadContextMap.java | 36 ++-
.../logging/log4j/spi/DefaultThreadContextMap.java | 244 +++++++++++++++++++++
.../GarbageFreeSortedArrayThreadContextMap.java | 44 ++--
...eadContextMap.java => LocationAwareLogger.java} | 22 +-
.../apache/logging/log4j/spi/LoggingSystem.java | 4 +-
.../logging/log4j/spi/MessageFactory2Adapter.java | 157 +++++++++++++
.../logging/log4j/spi/NoOpThreadContextMap.java | 22 +-
.../logging/log4j/spi/ObjectThreadContextMap.java | 35 ++-
.../log4j/spi/ReadOnlyThreadContextMap.java | 3 +-
.../apache/logging/log4j/spi/ThreadContextMap.java | 87 --------
.../logging/log4j/spi/ThreadContextMap2.java | 34 ++-
.../logging/log4j/spi/ThreadContextMapFactory.java | 146 ++++++++++++
.../org/apache/logging/log4j/util/Constants.java | 51 ++++-
.../org/apache/logging/log4j/util/LoaderUtil.java | 200 +++++++++++++----
.../apache/logging/log4j/util/MessageSupplier.java | 18 +-
.../apache/logging/log4j/util/PropertiesUtil.java | 43 +++-
.../logging/log4j/util/ProviderActivator.java | 53 +++++
.../apache/logging/log4j/util/ProviderUtil.java | 178 +++++++++++++++
.../org/apache/logging/log4j/util/Supplier.java | 15 +-
.../log4j/async/logger/AsyncThreadContextTest.java | 20 +-
.../core/impl/ThreadContextDataInjectorTest.java | 86 +++-----
.../CopyOnWriteOpenHashMapThreadContextMap.java | 4 +
.../GarbageFreeOpenHashMapThreadContextMap.java | 11 +-
log4j-to-slf4j/pom.xml | 5 +
.../slf4j/{SLF4JProvider.java => Activator.java} | 20 +-
.../org/apache/logging/slf4j/MDCContextMap.java | 4 +-
.../java/org/apache/logging/slf4j/SLF4JLogger.java | 14 +-
.../org/apache/logging/slf4j/SLF4JProvider.java | 2 +-
.../java/org/apache/logging/slf4j/LoggerTest.java | 40 +++-
47 files changed, 2385 insertions(+), 823 deletions(-)
diff --git a/log4j-api-test/src/main/java/org/apache/logging/log4j/test/ThreadContextUtilityClass.java b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/ThreadContextUtilityClass.java
index 61cd380829..c8424dc882 100644
--- a/log4j-api-test/src/main/java/org/apache/logging/log4j/test/ThreadContextUtilityClass.java
+++ b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/ThreadContextUtilityClass.java
@@ -111,4 +111,8 @@ public class ThreadContextUtilityClass {
ThreadContext.put("testKey", "testValue");
assertEquals("testValue", ThreadContext.get("testKey"));
}
+
+ public static void reset() {
+ ThreadContext.init();
+ }
}
diff --git a/log4j-api-test/src/test/java/org/apache/logging/log4j/LoggerTest.java b/log4j-api-test/src/test/java/org/apache/logging/log4j/LoggerTest.java
index 7f4d6b8b37..db46820465 100644
--- a/log4j-api-test/src/test/java/org/apache/logging/log4j/LoggerTest.java
+++ b/log4j-api-test/src/test/java/org/apache/logging/log4j/LoggerTest.java
@@ -34,21 +34,19 @@ import org.apache.logging.log4j.message.JsonMessage;
import org.apache.logging.log4j.message.Message;
import org.apache.logging.log4j.message.MessageFactory;
import org.apache.logging.log4j.message.ObjectMessage;
-import org.apache.logging.log4j.message.ReusableMessageFactory;
+import org.apache.logging.log4j.message.ParameterizedMessageFactory;
import org.apache.logging.log4j.message.SimpleMessage;
import org.apache.logging.log4j.message.SimpleMessageFactory;
import org.apache.logging.log4j.message.StringFormatterMessageFactory;
import org.apache.logging.log4j.message.StructuredDataMessage;
+import org.apache.logging.log4j.spi.MessageFactory2Adapter;
import org.apache.logging.log4j.test.TestLogger;
-import org.apache.logging.log4j.test.TestLoggerContextFactory;
-import org.apache.logging.log4j.test.junit.LoggerContextFactoryExtension;
import org.apache.logging.log4j.test.junit.Resources;
import org.apache.logging.log4j.test.junit.UsingThreadContextMap;
import org.apache.logging.log4j.util.Strings;
import org.apache.logging.log4j.util.Supplier;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.extension.RegisterExtension;
import org.junit.jupiter.api.parallel.ResourceAccessMode;
import org.junit.jupiter.api.parallel.ResourceLock;
import org.junitpioneer.jupiter.ReadsSystemProperty;
@@ -57,11 +55,7 @@ import org.junitpioneer.jupiter.ReadsSystemProperty;
@ReadsSystemProperty
public class LoggerTest {
- @RegisterExtension
- public static final LoggerContextFactoryExtension EXTENSION =
- new LoggerContextFactoryExtension(new TestLoggerContextFactory());
-
- private static class TestReusableMessageFactory {
+ private static class TestParameterizedMessageFactory {
// empty
}
@@ -82,7 +76,7 @@ public class LoggerTest {
assertThat(
"Incorrect message 1",
results.get(0),
- equalTo(" DEBUG org.apache.logging.log4j.LoggerTest.builder(LoggerTest.java:78) Hello"));
+ equalTo(" DEBUG org.apache.logging.log4j.LoggerTest.builder(LoggerTest.java:72) Hello"));
assertThat("Incorrect message 2", results.get(1), equalTo("test ERROR Hello John"));
assertThat(
"Incorrect message 3",
@@ -91,13 +85,13 @@ public class LoggerTest {
assertThat(
"Throwable incorrect in message 3",
results.get(2),
- containsString("org.apache.logging.log4j.LoggerTest.builder(LoggerTest.java:80)"));
+ containsString("org.apache.logging.log4j.LoggerTest.builder(LoggerTest.java:74)"));
}
@Test
public void basicFlow() {
- logger.traceEntry();
- logger.traceExit();
+ logger.entry();
+ logger.exit();
assertEquals(2, results.size());
assertThat(results.get(0)).isEqualTo("ENTER[ FLOW ] TRACE Enter");
assertThat(results.get(1)).isEqualTo("EXIT[ FLOW ] TRACE Exit");
@@ -172,19 +166,8 @@ public class LoggerTest {
@Test
public void flowTracingString_SupplierOfObjectMessages() {
final EntryMessage msg = logger.traceEntry(
- "doFoo(a={}, b={})",
- new Supplier<Message>() {
- @Override
- public Message get() {
- return new ObjectMessage(1);
- }
- },
- new Supplier<Message>() {
- @Override
- public Message get() {
- return new ObjectMessage(2);
- }
- });
+ "doFoo(a={}, b={})", (Supplier<Message>) () -> new ObjectMessage(1), (Supplier<Message>)
+ () -> new ObjectMessage(2));
logger.traceExit(msg, 3);
assertThat(results).hasSize(2);
assertThat(results.get(0)).startsWith("ENTER[ FLOW ] TRACE Enter").contains("doFoo(a=1, b=2)");
@@ -193,20 +176,8 @@ public class LoggerTest {
@Test
public void flowTracingString_SupplierOfStrings() {
- final EntryMessage msg = logger.traceEntry(
- "doFoo(a={}, b={})",
- new Supplier<String>() {
- @Override
- public String get() {
- return "1";
- }
- },
- new Supplier<String>() {
- @Override
- public String get() {
- return "2";
- }
- });
+ final EntryMessage msg =
+ logger.traceEntry("doFoo(a={}, b={})", (Supplier<String>) () -> "1", (Supplier<String>) () -> "2");
logger.traceExit(msg, 3);
assertThat(results).hasSize(2);
assertThat(results.get(0)).startsWith("ENTER[ FLOW ] TRACE Enter").contains("doFoo(a=1, b=2)");
@@ -267,7 +238,6 @@ public class LoggerTest {
}
@Test
- @ResourceLock("log4j2.TestLogger")
@ResourceLock(value = org.junit.jupiter.api.parallel.Resources.LOCALE, mode = ResourceAccessMode.READ)
public void getFormatterLogger() {
// The TestLogger logger was already created in an instance variable for this class.
@@ -286,7 +256,6 @@ public class LoggerTest {
}
@Test
- @ResourceLock("log4j2.TestLogger")
@ResourceLock(value = org.junit.jupiter.api.parallel.Resources.LOCALE, mode = ResourceAccessMode.READ)
public void getFormatterLogger_Class() {
// The TestLogger logger was already created in an instance variable for this class.
@@ -303,12 +272,14 @@ public class LoggerTest {
testLogger.getEntries().get(0));
}
- private static void assertMessageFactoryInstanceOf(final MessageFactory factory, final Class<?> cls) {
+ private static void assertMessageFactoryInstanceOf(MessageFactory factory, final Class<?> cls) {
+ if (factory instanceof MessageFactory2Adapter) {
+ factory = ((MessageFactory2Adapter) factory).getOriginal();
+ }
assertTrue(factory.getClass().isAssignableFrom(cls));
}
@Test
- @ResourceLock("log4j2.TestLogger")
@ResourceLock(value = org.junit.jupiter.api.parallel.Resources.LOCALE, mode = ResourceAccessMode.READ)
public void getFormatterLogger_Object() {
// The TestLogger logger was already created in an instance variable for this class.
@@ -326,7 +297,6 @@ public class LoggerTest {
}
@Test
- @ResourceLock("log4j2.TestLogger")
@ResourceLock(value = org.junit.jupiter.api.parallel.Resources.LOCALE, mode = ResourceAccessMode.READ)
public void getFormatterLogger_String() {
final StringFormatterMessageFactory messageFactory = StringFormatterMessageFactory.INSTANCE;
@@ -343,13 +313,12 @@ public class LoggerTest {
}
@Test
- @ResourceLock("log4j2.TestLogger")
- public void getLogger_Class_ReusableMessageFactory() {
+ public void getLogger_Class_ParameterizedMessageFactory() {
// The TestLogger logger was already created in an instance variable for this class.
// The message factory is only used when the logger is created.
- final MessageFactory messageFactory = new ReusableMessageFactory();
+ final ParameterizedMessageFactory messageFactory = ParameterizedMessageFactory.INSTANCE;
final TestLogger testLogger =
- (TestLogger) LogManager.getLogger(TestReusableMessageFactory.class, messageFactory);
+ (TestLogger) LogManager.getLogger(TestParameterizedMessageFactory.class, messageFactory);
assertNotNull(testLogger);
assertEqualMessageFactory(messageFactory, testLogger);
testLogger.debug("{}", Integer.MAX_VALUE);
@@ -358,7 +327,6 @@ public class LoggerTest {
}
@Test
- @ResourceLock("log4j2.TestLogger")
public void getLogger_Class_StringFormatterMessageFactory() {
// The TestLogger logger was already created in an instance variable for this class.
// The message factory is only used when the logger is created.
@@ -374,13 +342,12 @@ public class LoggerTest {
}
@Test
- @ResourceLock("log4j2.TestLogger")
- public void getLogger_Object_ReusableMessageFactory() {
+ public void getLogger_Object_ParameterizedMessageFactory() {
// The TestLogger logger was already created in an instance variable for this class.
// The message factory is only used when the logger is created.
- final MessageFactory messageFactory = new ReusableMessageFactory();
+ final ParameterizedMessageFactory messageFactory = ParameterizedMessageFactory.INSTANCE;
final TestLogger testLogger =
- (TestLogger) LogManager.getLogger(new TestReusableMessageFactory(), messageFactory);
+ (TestLogger) LogManager.getLogger(new TestParameterizedMessageFactory(), messageFactory);
assertNotNull(testLogger);
assertEqualMessageFactory(messageFactory, testLogger);
testLogger.debug("{}", Integer.MAX_VALUE);
@@ -389,12 +356,14 @@ public class LoggerTest {
}
private void assertEqualMessageFactory(final MessageFactory messageFactory, final TestLogger testLogger) {
- final MessageFactory actual = testLogger.getMessageFactory();
+ MessageFactory actual = testLogger.getMessageFactory();
+ if (actual instanceof MessageFactory2Adapter) {
+ actual = ((MessageFactory2Adapter) actual).getOriginal();
+ }
assertEquals(messageFactory, actual);
}
@Test
- @ResourceLock("log4j2.TestLogger")
public void getLogger_Object_StringFormatterMessageFactory() {
// The TestLogger logger was already created in an instance variable for this class.
// The message factory is only used when the logger is created.
@@ -411,7 +380,6 @@ public class LoggerTest {
}
@Test
- @ResourceLock("log4j2.TestLogger")
public void getLogger_String_MessageFactoryMismatch() {
final StringFormatterMessageFactory messageFactory = StringFormatterMessageFactory.INSTANCE;
final TestLogger testLogger =
@@ -419,7 +387,7 @@ public class LoggerTest {
assertNotNull(testLogger);
assertEqualMessageFactory(messageFactory, testLogger);
final TestLogger testLogger2 = (TestLogger)
- LogManager.getLogger("getLogger_String_MessageFactoryMismatch", new ReusableMessageFactory());
+ LogManager.getLogger("getLogger_String_MessageFactoryMismatch", ParameterizedMessageFactory.INSTANCE);
assertNotNull(testLogger2);
// TODO: How to test?
// This test context always creates new loggers, other test context impls I tried fail other tests.
@@ -432,11 +400,10 @@ public class LoggerTest {
}
@Test
- @ResourceLock("log4j2.TestLogger")
- public void getLogger_String_ReusableMessageFactory() {
- final MessageFactory messageFactory = new ReusableMessageFactory();
+ public void getLogger_String_ParameterizedMessageFactory() {
+ final ParameterizedMessageFactory messageFactory = ParameterizedMessageFactory.INSTANCE;
final TestLogger testLogger =
- (TestLogger) LogManager.getLogger("getLogger_String_ReusableMessageFactory", messageFactory);
+ (TestLogger) LogManager.getLogger("getLogger_String_ParameterizedMessageFactory", messageFactory);
assertNotNull(testLogger);
assertEqualMessageFactory(messageFactory, testLogger);
testLogger.debug("{}", Integer.MAX_VALUE);
@@ -445,7 +412,6 @@ public class LoggerTest {
}
@Test
- @ResourceLock("log4j2.TestLogger")
public void getLogger_String_SimpleMessageFactory() {
final SimpleMessageFactory messageFactory = SimpleMessageFactory.INSTANCE;
final TestLogger testLogger =
@@ -458,7 +424,6 @@ public class LoggerTest {
}
@Test
- @ResourceLock("log4j2.TestLogger")
public void getLogger_String_StringFormatterMessageFactory() {
final StringFormatterMessageFactory messageFactory = StringFormatterMessageFactory.INSTANCE;
final TestLogger testLogger =
@@ -607,7 +572,7 @@ public class LoggerTest {
@Test
@UsingThreadContextMap
public void mdc() {
- ThreadContext.put("TestYear", Integer.valueOf(2010).toString());
+ ThreadContext.put("TestYear", Integer.toString(2010));
logger.debug("Debug message");
final String testYear = ThreadContext.get("TestYear");
assertNotNull(testYear, "Test Year is null");
diff --git a/log4j-api-test/src/test/java/org/apache/logging/log4j/NoopThreadContextTest.java b/log4j-api-test/src/test/java/org/apache/logging/log4j/NoopThreadContextTest.java
index ef51fdf7d3..d4ce703b56 100644
--- a/log4j-api-test/src/test/java/org/apache/logging/log4j/NoopThreadContextTest.java
+++ b/log4j-api-test/src/test/java/org/apache/logging/log4j/NoopThreadContextTest.java
@@ -18,22 +18,19 @@ package org.apache.logging.log4j;
import static org.junit.jupiter.api.Assertions.assertNull;
-import org.apache.logging.log4j.spi.LoggingSystemProperty;
-import org.apache.logging.log4j.test.junit.InitializesThreadContext;
+import org.apache.logging.log4j.test.junit.SetTestProperty;
import org.apache.logging.log4j.test.junit.UsingThreadContextMap;
import org.junit.jupiter.api.Test;
-import org.junitpioneer.jupiter.SetSystemProperty;
/**
* Tests {@link ThreadContext}.
*/
+@SetTestProperty(key = "log4j2.disableThreadContext", value = "true")
+@SetTestProperty(key = "log4j2.disableThreadContextMap", value = "true")
+@UsingThreadContextMap
public class NoopThreadContextTest {
@Test
- @SetSystemProperty(key = LoggingSystemProperty.Constant.THREAD_CONTEXT_ENABLED, value = "false")
- @SetSystemProperty(key = LoggingSystemProperty.Constant.THREAD_CONTEXT_MAP_ENABLED, value = "false")
- @InitializesThreadContext
- @UsingThreadContextMap
public void testNoop() {
ThreadContext.put("Test", "Test");
final String value = ThreadContext.get("Test");
diff --git a/log4j-api-test/src/test/java/org/apache/logging/log4j/ThreadContextInheritanceTest.java b/log4j-api-test/src/test/java/org/apache/logging/log4j/ThreadContextInheritanceTest.java
index 08f216ba90..96783ae600 100644
--- a/log4j-api-test/src/test/java/org/apache/logging/log4j/ThreadContextInheritanceTest.java
+++ b/log4j-api-test/src/test/java/org/apache/logging/log4j/ThreadContextInheritanceTest.java
@@ -21,11 +21,14 @@ import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
-import org.apache.logging.log4j.spi.LoggingSystemProperty;
+import org.apache.logging.log4j.spi.DefaultThreadContextMap;
import org.apache.logging.log4j.test.ThreadContextUtilityClass;
import org.apache.logging.log4j.test.junit.InitializesThreadContext;
+import org.apache.logging.log4j.test.junit.SetTestProperty;
import org.apache.logging.log4j.test.junit.UsingThreadContextMap;
import org.apache.logging.log4j.test.junit.UsingThreadContextStack;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Tag;
import org.junit.jupiter.api.Test;
import org.junitpioneer.jupiter.SetSystemProperty;
@@ -33,12 +36,25 @@ import org.junitpioneer.jupiter.SetSystemProperty;
/**
* Tests {@link ThreadContext}.
*/
-@SetSystemProperty(key = LoggingSystemProperty.Constant.THREAD_CONTEXT_MAP_INHERITABLE, value = "true")
+@SetTestProperty(key = DefaultThreadContextMap.INHERITABLE_MAP, value = "true")
@InitializesThreadContext
+@UsingThreadContextMap
+@UsingThreadContextStack
public class ThreadContextInheritanceTest {
+ @BeforeAll
+ public static void setupClass() {
+ System.setProperty(DefaultThreadContextMap.INHERITABLE_MAP, "true");
+ ThreadContext.init();
+ }
+
+ @AfterAll
+ public static void tearDownClass() {
+ System.clearProperty(DefaultThreadContextMap.INHERITABLE_MAP);
+ ThreadContext.init();
+ }
+
@Test
- @UsingThreadContextStack
public void testPush() {
ThreadContext.push("Hello");
ThreadContext.push("{} is {}", ThreadContextInheritanceTest.class.getSimpleName(), "running");
@@ -48,76 +64,74 @@ public class ThreadContextInheritanceTest {
}
@Test
- @SetSystemProperty(key = LoggingSystemProperty.Constant.THREAD_CONTEXT_MAP_INHERITABLE, value = "true")
+ @SetSystemProperty(key = DefaultThreadContextMap.INHERITABLE_MAP, value = "true")
@InitializesThreadContext
public void testInheritanceSwitchedOn() throws Exception {
- ThreadContext.put("Greeting", "Hello");
- StringBuilder sb = new StringBuilder();
- TestThread thread = new TestThread(sb);
- thread.start();
- thread.join();
- String str = sb.toString();
- assertEquals("Hello", str, "Unexpected ThreadContext value. Expected Hello. Actual " + str);
- sb = new StringBuilder();
- thread = new TestThread(sb);
- thread.start();
- thread.join();
- str = sb.toString();
- assertEquals("Hello", str, "Unexpected ThreadContext value. Expected Hello. Actual " + str);
+ System.setProperty(DefaultThreadContextMap.INHERITABLE_MAP, "true");
+ try {
+ ThreadContext.clearMap();
+ ThreadContext.put("Greeting", "Hello");
+ StringBuilder sb = new StringBuilder();
+ TestThread thread = new TestThread(sb);
+ thread.start();
+ thread.join();
+ String str = sb.toString();
+ assertEquals("Hello", str, "Unexpected ThreadContext value. Expected Hello. Actual " + str);
+ sb = new StringBuilder();
+ thread = new TestThread(sb);
+ thread.start();
+ thread.join();
+ str = sb.toString();
+ assertEquals("Hello", str, "Unexpected ThreadContext value. Expected Hello. Actual " + str);
+ } finally {
+ System.clearProperty(DefaultThreadContextMap.INHERITABLE_MAP);
+ }
}
@Test
@Tag("performance")
- @UsingThreadContextMap
public void perfTest() {
ThreadContextUtilityClass.perfTest();
}
@Test
- @UsingThreadContextMap
public void testGetContextReturnsEmptyMapIfEmpty() {
ThreadContextUtilityClass.testGetContextReturnsEmptyMapIfEmpty();
}
@Test
- @UsingThreadContextMap
public void testGetContextReturnsMutableCopy() {
ThreadContextUtilityClass.testGetContextReturnsMutableCopy();
}
@Test
- @UsingThreadContextMap
public void testGetImmutableContextReturnsEmptyMapIfEmpty() {
ThreadContextUtilityClass.testGetImmutableContextReturnsEmptyMapIfEmpty();
}
@Test
- @UsingThreadContextMap
public void testGetImmutableContextReturnsImmutableMapIfNonEmpty() {
ThreadContextUtilityClass.testGetImmutableContextReturnsImmutableMapIfNonEmpty();
}
@Test
- @UsingThreadContextMap
public void testGetImmutableContextReturnsImmutableMapIfEmpty() {
ThreadContextUtilityClass.testGetImmutableContextReturnsImmutableMapIfEmpty();
}
@Test
- @UsingThreadContextStack
public void testGetImmutableStackReturnsEmptyStackIfEmpty() {
ThreadContextUtilityClass.testGetImmutableStackReturnsEmptyStackIfEmpty();
}
@Test
- @UsingThreadContextMap
public void testPut() {
ThreadContextUtilityClass.testPut();
}
@Test
- @UsingThreadContextMap
public void testRemove() {
+ ThreadContext.clearMap();
assertNull(ThreadContext.get("testKey"));
ThreadContext.put("testKey", "testValue");
assertEquals("testValue", ThreadContext.get("testKey"));
@@ -128,8 +142,8 @@ public class ThreadContextInheritanceTest {
}
@Test
- @UsingThreadContextMap
public void testContainsKey() {
+ ThreadContext.clearMap();
assertFalse(ThreadContext.containsKey("testKey"));
ThreadContext.put("testKey", "testValue");
assertTrue(ThreadContext.containsKey("testKey"));
diff --git a/log4j-api-test/src/test/java/org/apache/logging/log4j/util/LoaderUtilTest.java b/log4j-api-test/src/test/java/org/apache/logging/log4j/util/LoaderUtilTest.java
deleted file mode 100644
index 12b22e1de1..0000000000
--- a/log4j-api-test/src/test/java/org/apache/logging/log4j/util/LoaderUtilTest.java
+++ /dev/null
@@ -1,61 +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.util;
-
-import static org.junit.jupiter.api.Assertions.assertEquals;
-
-import java.net.URL;
-import java.util.Collections;
-import java.util.Enumeration;
-import org.junit.jupiter.api.AfterEach;
-import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.Test;
-import org.junitpioneer.jupiter.ReadsSystemProperty;
-
-@ReadsSystemProperty
-public class LoaderUtilTest {
- @BeforeEach
- @AfterEach
- public void reset() {
- LoaderUtil.forceTcclOnly = null;
- }
-
- @Test
- public void systemClassLoader() {
- final Thread thread = Thread.currentThread();
- final ClassLoader tccl = thread.getContextClassLoader();
-
- LoaderUtil.forceTcclOnly = true;
- final ClassLoader loader = new ClassLoader(tccl) {
- @Override
- public Enumeration<URL> getResources(final String name) {
- return Collections.emptyEnumeration();
- }
- };
- thread.setContextClassLoader(loader);
- try {
- assertEquals(
- 0, LoaderUtil.findUrlResources("Log4j-charsets.properties").size());
-
- LoaderUtil.forceTcclOnly = false;
- assertEquals(
- 1, LoaderUtil.findUrlResources("Log4j-charsets.properties").size());
- } finally {
- thread.setContextClassLoader(tccl);
- }
- }
-}
diff --git a/log4j-api/pom.xml b/log4j-api/pom.xml
index 249887921c..c5a4505f76 100644
--- a/log4j-api/pom.xml
+++ b/log4j-api/pom.xml
@@ -52,4 +52,12 @@
</properties>
+ <dependencies>
+ <dependency>
+ <groupId>org.osgi</groupId>
+ <artifactId>org.osgi.framework</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ </dependencies>
+
</project>
diff --git a/log4j-api/src/main/java/org/apache/logging/log4j/BridgeAware.java b/log4j-api/src/main/java/org/apache/logging/log4j/BridgeAware.java
index 86b7c00d7e..4ee476c524 100644
--- a/log4j-api/src/main/java/org/apache/logging/log4j/BridgeAware.java
+++ b/log4j-api/src/main/java/org/apache/logging/log4j/BridgeAware.java
@@ -24,10 +24,10 @@ package org.apache.logging.log4j;
public interface BridgeAware {
/**
- * Fully qualified class name of the entry point to the logging system. This
+ * To set fully qualified class name of the entry point to the logging system. This
* class will not appear in the location information.
*
- * @param fqcn fully qualified class name of logging system entry point
+ * @param fqcn fully qualified class name
*/
void setEntryPoint(final String fqcn);
}
diff --git a/log4j-api/src/main/java/org/apache/logging/log4j/Level.java b/log4j-api/src/main/java/org/apache/logging/log4j/Level.java
index 02c7d2f750..9f8e786326 100644
--- a/log4j-api/src/main/java/org/apache/logging/log4j/Level.java
+++ b/log4j-api/src/main/java/org/apache/logging/log4j/Level.java
@@ -19,6 +19,7 @@ package org.apache.logging.log4j;
import static org.apache.logging.log4j.util.Strings.toRootUpperCase;
import aQute.bnd.annotation.baseline.BaselineIgnore;
+import java.io.Serializable;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
@@ -27,8 +28,9 @@ import org.apache.logging.log4j.util.Strings;
/**
* Levels used for identifying the severity of an event. Levels are organized from most specific to least:
+ * <p>
* <table>
- * <caption>Levels</caption>
+ * <caption>Level names with description</caption>
* <tr>
* <th>Name</th>
* <th>Description</th>
@@ -66,14 +68,15 @@ import org.apache.logging.log4j.util.Strings;
* <td>All events should be logged.</td>
* </tr>
* </table>
+ * </p>
* <p>
* Typically, configuring a level in a filter or on a logger will cause logging events of that level and those that are
* more specific to pass through the filter. A special level, {@link #ALL}, is guaranteed to capture all levels when
* used in logging configurations.
* </p>
*/
-@BaselineIgnore("3.0.0")
-public final class Level implements Comparable<Level> {
+@BaselineIgnore("2.22.0")
+public final class Level implements Comparable<Level>, Serializable {
private static final Level[] EMPTY_ARRAY = {};
@@ -119,14 +122,14 @@ public final class Level implements Comparable<Level> {
*/
public static final Level ALL = new Level("ALL", StandardLevel.ALL.intLevel());
- public static final String NAMESPACE = "Level";
-
/**
* Category to be used by custom levels.
*
* @since 2.1
*/
- public static final String CATEGORY = NAMESPACE;
+ public static final String CATEGORY = "Level";
+
+ private static final long serialVersionUID = 1581082L;
private final String name;
private final int intLevel;
@@ -171,7 +174,7 @@ public final class Level implements Comparable<Level> {
*
* @param minLevel The minimum level to test.
* @param maxLevel The maximum level to test.
- * @return true if this level is in between the given levels
+ * @return True true if this level is in between the given levels
* @since 2.4
*/
public boolean isInRange(final Level minLevel, final Level maxLevel) {
@@ -190,7 +193,7 @@ public final class Level implements Comparable<Level> {
*
* @param level
* The level to test.
- * @return True if this Level is less specific or the same as the given Level.
+ * @return True if this level Level is less specific or the same as the given Level.
*/
public boolean isLessSpecificThan(final Level level) {
return this.intLevel >= level.intLevel;
@@ -206,7 +209,7 @@ public final class Level implements Comparable<Level> {
* </p>
*
* @param level The level to test.
- * @return True if this Level is more specific or the same as the given Level.
+ * @return True if this level Level is more specific or the same as the given Level.
*/
public boolean isMoreSpecificThan(final Level level) {
return this.intLevel <= level.intLevel;
@@ -222,12 +225,12 @@ public final class Level implements Comparable<Level> {
@Override
public int compareTo(final Level other) {
- return Integer.compare(intLevel, other.intLevel);
+ return intLevel < other.intLevel ? -1 : (intLevel > other.intLevel ? 1 : 0);
}
@Override
public boolean equals(final Object other) {
- return other == this;
+ return other instanceof Level && other == this;
}
public Class<Level> getDeclaringClass() {
@@ -363,4 +366,9 @@ public final class Level implements Comparable<Level> {
public static <T extends Enum<T>> T valueOf(final Class<T> enumType, final String name) {
return Enum.valueOf(enumType, name);
}
+
+ // for deserialization
+ private Object readResolve() {
+ return Level.valueOf(this.name);
+ }
}
diff --git a/log4j-api/src/main/java/org/apache/logging/log4j/Logger.java b/log4j-api/src/main/java/org/apache/logging/log4j/Logger.java
index 2ca3b07132..7dc872e21a 100644
--- a/log4j-api/src/main/java/org/apache/logging/log4j/Logger.java
+++ b/log4j-api/src/main/java/org/apache/logging/log4j/Logger.java
@@ -20,6 +20,7 @@ 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.util.MessageSupplier;
import org.apache.logging.log4j.util.Supplier;
@@ -122,7 +123,6 @@ public interface Logger {
* @param messageSupplier A function, which when called, produces the desired log message.
* @since 2.4
*/
- @SuppressWarnings("removal")
void debug(Marker marker, MessageSupplier messageSupplier);
/**
@@ -135,7 +135,6 @@ public interface Logger {
* @param throwable A Throwable or null.
* @since 2.4
*/
- @SuppressWarnings("removal")
void debug(Marker marker, MessageSupplier messageSupplier, Throwable throwable);
/**
@@ -201,7 +200,7 @@ public interface Logger {
* @param paramSuppliers An array of functions, which when called, produce the desired log message parameters.
* @since 2.4
*/
- @SuppressWarnings("removal")
+ @SuppressWarnings("deprecation")
void debug(Marker marker, String message, Supplier<?>... paramSuppliers);
/**
@@ -223,7 +222,7 @@ public interface Logger {
* message factory.
* @since 2.4
*/
- @SuppressWarnings("removal")
+ @SuppressWarnings("deprecation")
void debug(Marker marker, Supplier<?> messageSupplier);
/**
@@ -236,7 +235,7 @@ public interface Logger {
* @param throwable A Throwable or null.
* @since 2.4
*/
- @SuppressWarnings("removal")
+ @SuppressWarnings("deprecation")
void debug(Marker marker, Supplier<?> messageSupplier, Throwable throwable);
/**
@@ -330,7 +329,7 @@ public interface Logger {
* @param paramSuppliers An array of functions, which when called, produce the desired log message parameters.
* @since 2.4
*/
- @SuppressWarnings("removal")
+ @SuppressWarnings("deprecation")
void debug(String message, Supplier<?>... paramSuppliers);
/**
@@ -349,7 +348,7 @@ public interface Logger {
* message factory.
* @since 2.4
*/
- @SuppressWarnings("removal")
+ @SuppressWarnings("deprecation")
void debug(Supplier<?> messageSupplier);
/**
@@ -361,7 +360,7 @@ public interface Logger {
* @param throwable the {@code Throwable} to log, including its stack trace.
* @since 2.4
*/
- @SuppressWarnings("removal")
+ @SuppressWarnings("deprecation")
void debug(Supplier<?> messageSupplier, Throwable throwable);
/**
@@ -684,10 +683,8 @@ public interface Logger {
* logged.
* @deprecated Use {@link #traceEntry()} instead which performs the same function.
*/
- @Deprecated(forRemoval = true, since = "2.6")
- default void entry() {
- traceEntry();
- }
+ @Deprecated
+ void entry();
/**
* Logs entry to a method along with its parameters (consider using one of the {@code traceEntry(...)} methods instead.)
@@ -708,10 +705,8 @@ public interface Logger {
* @param params The parameters to the method.
* @deprecated Use {@link #traceEntry(String, Object...)} instead which performs the same function.
*/
- @Deprecated(forRemoval = true, since = "2.12")
- default void entry(final Object... params) {
- traceEntry(null, params);
- }
+ @Deprecated
+ void entry(Object... params);
/**
* Logs a message with the specific Marker at the {@link Level#ERROR ERROR} level.
@@ -816,7 +811,7 @@ public interface Logger {
* @param paramSuppliers An array of functions, which when called, produce the desired log message parameters.
* @since 2.4
*/
- @SuppressWarnings("removal")
+ @SuppressWarnings("deprecation")
void error(Marker marker, String message, Supplier<?>... paramSuppliers);
/**
@@ -838,7 +833,7 @@ public interface Logger {
* message factory.
* @since 2.4
*/
- @SuppressWarnings("removal")
+ @SuppressWarnings("deprecation")
void error(Marker marker, Supplier<?> messageSupplier);
/**
@@ -851,7 +846,7 @@ public interface Logger {
* @param throwable A Throwable or null.
* @since 2.4
*/
- @SuppressWarnings("removal")
+ @SuppressWarnings("deprecation")
void error(Marker marker, Supplier<?> messageSupplier, Throwable throwable);
/**
@@ -945,7 +940,7 @@ public interface Logger {
* @param paramSuppliers An array of functions, which when called, produce the desired log message parameters.
* @since 2.4
*/
- @SuppressWarnings("removal")
+ @SuppressWarnings("deprecation")
void error(String message, Supplier<?>... paramSuppliers);
/**
@@ -964,7 +959,7 @@ public interface Logger {
* message factory.
* @since 2.4
*/
- @SuppressWarnings("removal")
+ @SuppressWarnings("deprecation")
void error(Supplier<?> messageSupplier);
/**
@@ -976,7 +971,7 @@ public interface Logger {
* @param throwable the {@code Throwable} to log, including its stack trace.
* @since 2.4
*/
- @SuppressWarnings("removal")
+ @SuppressWarnings("deprecation")
void error(Supplier<?> messageSupplier, Throwable throwable);
/**
@@ -1298,10 +1293,8 @@ public interface Logger {
* Logs exit from a method. Used for methods that do not return anything.
* @deprecated Use {@link #traceExit()} instead which performs the same function.
*/
- @Deprecated(forRemoval = true, since = "2.6")
- default void exit() {
- traceExit();
- }
+ @Deprecated
+ void exit();
/**
* Logs exiting from a method with the result. This may be coded as:
@@ -1315,10 +1308,8 @@ public interface Logger {
* @return the result.
* @deprecated Use {@link #traceExit(Object)} instead which performs the same function.
*/
- @Deprecated(forRemoval = true, since = "2.6")
- default <R> R exit(final R result) {
- return traceExit(result);
- }
+ @Deprecated
+ <R> R exit(R result);
/**
* Logs a message with the specific Marker at the {@link Level#FATAL FATAL} level.
@@ -1423,7 +1414,7 @@ public interface Logger {
* @param paramSuppliers An array of functions, which when called, produce the desired log message parameters.
* @since 2.4
*/
- @SuppressWarnings("removal")
+ @SuppressWarnings("deprecation")
void fatal(Marker marker, String message, Supplier<?>... paramSuppliers);
/**
@@ -1445,7 +1436,7 @@ public interface Logger {
* message factory.
* @since 2.4
*/
- @SuppressWarnings("removal")
+ @SuppressWarnings("deprecation")
void fatal(Marker marker, Supplier<?> messageSupplier);
/**
@@ -1458,7 +1449,7 @@ public interface Logger {
* @param throwable A Throwable or null.
* @since 2.4
*/
- @SuppressWarnings("removal")
+ @SuppressWarnings("deprecation")
void fatal(Marker marker, Supplier<?> messageSupplier, Throwable throwable);
/**
@@ -1552,7 +1543,7 @@ public interface Logger {
* @param paramSuppliers An array of functions, which when called, produce the desired log message parameters.
* @since 2.4
*/
- @SuppressWarnings("removal")
+ @SuppressWarnings("deprecation")
void fatal(String message, Supplier<?>... paramSuppliers);
/**
@@ -1571,7 +1562,7 @@ public interface Logger {
* message factory.
* @since 2.4
*/
- @SuppressWarnings("removal")
+ @SuppressWarnings("deprecation")
void fatal(Supplier<?> messageSupplier);
/**
@@ -1583,7 +1574,7 @@ public interface Logger {
* @param throwable the {@code Throwable} to log, including its stack trace.
* @since 2.4
*/
- @SuppressWarnings("removal")
+ @SuppressWarnings("deprecation")
void fatal(Supplier<?> messageSupplier, Throwable throwable);
/**
@@ -1911,13 +1902,13 @@ public interface Logger {
/**
* Gets the message factory used to convert message Objects and Strings/CharSequences into actual log Messages.
*
- * Since version 2.6, Log4j internally uses message factories that implement the {@link MessageFactory} interface.
+ * Since version 2.6, Log4j internally uses message factories that implement the {@link MessageFactory2} interface.
* From version 2.6.2, the return type of this method was changed from {@link MessageFactory} to
- * {@code <MF extends MessageFactory> MF}. The returned factory will always implement {@link MessageFactory},
- * but the return type of this method could not be changed to {@link MessageFactory} without breaking binary
+ * {@code <MF extends MessageFactory> MF}. The returned factory will always implement {@link MessageFactory2},
+ * but the return type of this method could not be changed to {@link MessageFactory2} without breaking binary
* compatibility.
*
- * @return the message factory, as an instance of {@link MessageFactory}
+ * @return the message factory, as an instance of {@link MessageFactory2}
*/
<MF extends MessageFactory> MF getMessageFactory();
@@ -2039,7 +2030,7 @@ public interface Logger {
* @param paramSuppliers An array of functions, which when called, produce the desired log message parameters.
* @since 2.4
*/
- @SuppressWarnings("removal")
+ @SuppressWarnings("deprecation")
void info(Marker marker, String message, Supplier<?>... paramSuppliers);
/**
@@ -2061,7 +2052,7 @@ public interface Logger {
* message factory.
* @since 2.4
*/
- @SuppressWarnings("removal")
+ @SuppressWarnings("deprecation")
void info(Marker marker, Supplier<?> messageSupplier);
/**
@@ -2074,7 +2065,7 @@ public interface Logger {
* @param throwable A Throwable or null.
* @since 2.4
*/
- @SuppressWarnings("removal")
+ @SuppressWarnings("deprecation")
void info(Marker marker, Supplier<?> messageSupplier, Throwable throwable);
/**
@@ -2168,7 +2159,7 @@ public interface Logger {
* @param paramSuppliers An array of functions, which when called, produce the desired log message parameters.
* @since 2.4
*/
- @SuppressWarnings("removal")
+ @SuppressWarnings("deprecation")
void info(String message, Supplier<?>... paramSuppliers);
/**
@@ -2187,7 +2178,7 @@ public interface Logger {
* message factory.
* @since 2.4
*/
- @SuppressWarnings("removal")
+ @SuppressWarnings("deprecation")
void info(Supplier<?> messageSupplier);
/**
@@ -2199,7 +2190,7 @@ public interface Logger {
* @param throwable the {@code Throwable} to log, including its stack trace.
* @since 2.4
*/
- @SuppressWarnings("removal")
+ @SuppressWarnings("deprecation")
void info(Supplier<?> messageSupplier, Throwable throwable);
/**
@@ -2746,7 +2737,7 @@ public interface Logger {
* @param paramSuppliers An array of functions, which when called, produce the desired log message parameters.
* @since 2.4
*/
- @SuppressWarnings("removal")
+ @SuppressWarnings("deprecation")
void log(Level level, Marker marker, String message, Supplier<?>... paramSuppliers);
/**
@@ -2769,7 +2760,7 @@ public interface Logger {
* message factory.
* @since 2.4
*/
- @SuppressWarnings("removal")
+ @SuppressWarnings("deprecation")
void log(Level level, Marker marker, Supplier<?> messageSupplier);
/**
@@ -2783,7 +2774,7 @@ public interface Logger {
* @param throwable A Throwable or null.
* @since 2.4
*/
- @SuppressWarnings("removal")
+ @SuppressWarnings("deprecation")
void log(Level level, Marker marker, Supplier<?> messageSupplier, Throwable throwable);
/**
@@ -2887,7 +2878,7 @@ public interface Logger {
* @param paramSuppliers An array of functions, which when called, produce the desired log message parameters.
* @since 2.4
*/
- @SuppressWarnings("removal")
+ @SuppressWarnings("deprecation")
void log(Level level, String message, Supplier<?>... paramSuppliers);
/**
@@ -2908,7 +2899,7 @@ public interface Logger {
* message factory.
* @since 2.4
*/
- @SuppressWarnings("removal")
+ @SuppressWarnings("deprecation")
void log(Level level, Supplier<?> messageSupplier);
/**
@@ -2921,7 +2912,7 @@ public interface Logger {
* @param throwable the {@code Throwable} to log, including its stack log.
* @since 2.4
*/
- @SuppressWarnings("removal")
+ @SuppressWarnings("deprecation")
void log(Level level, Supplier<?> messageSupplier, Throwable throwable);
/**
@@ -3444,7 +3435,7 @@ public interface Logger {
* @param paramSuppliers An array of functions, which when called, produce the desired log message parameters.
* @since 2.4
*/
- @SuppressWarnings("removal")
+ @SuppressWarnings("deprecation")
void trace(Marker marker, String message, Supplier<?>... paramSuppliers);
/**
@@ -3467,7 +3458,7 @@ public interface Logger {
* message factory.
* @since 2.4
*/
- @SuppressWarnings("removal")
+ @SuppressWarnings("deprecation")
void trace(Marker marker, Supplier<?> messageSupplier);
/**
@@ -3480,7 +3471,7 @@ public interface Logger {
* @param throwable A Throwable or null.
* @since 2.4
*/
- @SuppressWarnings("removal")
+ @SuppressWarnings("deprecation")
void trace(Marker marker, Supplier<?> messageSupplier, Throwable throwable);
/**
@@ -3576,7 +3567,7 @@ public interface Logger {
* @param paramSuppliers An array of functions, which when called, produce the desired log message parameters.
* @since 2.4
*/
- @SuppressWarnings("removal")
+ @SuppressWarnings("deprecation")
void trace(String message, Supplier<?>... paramSuppliers);
/**
@@ -3596,7 +3587,7 @@ public interface Logger {
* message factory.
* @since 2.4
*/
- @SuppressWarnings("removal")
+ @SuppressWarnings("deprecation")
void trace(Supplier<?> messageSupplier);
/**
@@ -3608,7 +3599,7 @@ public interface Logger {
* @param throwable the {@code Throwable} to log, including its stack trace.
* @since 2.4
*/
- @SuppressWarnings("removal")
+ @SuppressWarnings("deprecation")
void trace(Supplier<?> messageSupplier, Throwable throwable);
/**
@@ -3976,7 +3967,7 @@ public interface Logger {
*
* @since 2.6
*/
- @SuppressWarnings("removal")
+ @SuppressWarnings("deprecation")
EntryMessage traceEntry(Supplier<?>... paramSuppliers);
/**
@@ -3995,7 +3986,7 @@ public interface Logger {
*
* @since 2.6
*/
- @SuppressWarnings("removal")
+ @SuppressWarnings("deprecation")
EntryMessage traceEntry(String format, Supplier<?>... paramSuppliers);
/**
@@ -4214,7 +4205,7 @@ public interface Logger {
* @param paramSuppliers An array of functions, which when called, produce the desired log message parameters.
* @since 2.4
*/
- @SuppressWarnings("removal")
+ @SuppressWarnings("deprecation")
void warn(Marker marker, String message, Supplier<?>... paramSuppliers);
/**
@@ -4236,7 +4227,7 @@ public interface Logger {
* message factory.
* @since 2.4
*/
- @SuppressWarnings("removal")
+ @SuppressWarnings("deprecation")
void warn(Marker marker, Supplier<?> messageSupplier);
/**
@@ -4249,7 +4240,7 @@ public interface Logger {
* @param throwable A Throwable or null.
* @since 2.4
*/
- @SuppressWarnings("removal")
+ @SuppressWarnings("deprecation")
void warn(Marker marker, Supplier<?> messageSupplier, Throwable throwable);
/**
@@ -4343,7 +4334,7 @@ public interface Logger {
* @param paramSuppliers An array of functions, which when called, produce the desired log message parameters.
* @since 2.4
*/
- @SuppressWarnings("removal")
+ @SuppressWarnings("deprecation")
void warn(String message, Supplier<?>... paramSuppliers);
/**
@@ -4362,7 +4353,7 @@ public interface Logger {
* message factory.
* @since 2.4
*/
- @SuppressWarnings("removal")
+ @SuppressWarnings("deprecation")
void warn(Supplier<?> messageSupplier);
/**
@@ -4374,7 +4365,7 @@ public interface Logger {
* @param throwable the {@code Throwable} to log, including its stack warn.
* @since 2.4
*/
- @SuppressWarnings("removal")
+ @SuppressWarnings("deprecation")
void warn(Supplier<?> messageSupplier, Throwable throwable);
/**
@@ -4704,12 +4695,7 @@ public interface Logger {
* @since 2.13.0
*/
default void logMessage(
- final Level level,
- final Marker marker,
- final String fqcn,
- final StackTraceElement location,
- final Message message,
- final Throwable throwable) {
+ Level level, Marker marker, String fqcn, StackTraceElement location, Message message, Throwable throwable) {
// noop
}
@@ -4782,7 +4768,7 @@ public interface Logger {
* @return a LogBuilder.
* @since 2.13.0
*/
- default LogBuilder atLevel(final Level level) {
+ default LogBuilder atLevel(Level level) {
return LogBuilder.NOOP;
}
}
diff --git a/log4j-api/src/main/java/org/apache/logging/log4j/Marker.java b/log4j-api/src/main/java/org/apache/logging/log4j/Marker.java
index 049a12df4b..9be36461eb 100644
--- a/log4j-api/src/main/java/org/apache/logging/log4j/Marker.java
+++ b/log4j-api/src/main/java/org/apache/logging/log4j/Marker.java
@@ -16,7 +16,7 @@
*/
package org.apache.logging.log4j;
-import aQute.bnd.annotation.baseline.BaselineIgnore;
+import java.io.Serializable;
/**
* Markers are objects that are used to add easily filterable information to log messages.
@@ -25,8 +25,7 @@ import aQute.bnd.annotation.baseline.BaselineIgnore;
* more specific categories. An example might be a Marker named "Error" with children named "SystemError" and
* "ApplicationError".
*/
-@BaselineIgnore("3.0.0")
-public interface Marker {
+public interface Marker extends Serializable {
/**
* Adds a Marker as a parent to this Marker.
diff --git a/log4j-api/src/main/java/org/apache/logging/log4j/MarkerManager.java b/log4j-api/src/main/java/org/apache/logging/log4j/MarkerManager.java
index f64ed2b386..9cef9688e8 100644
--- a/log4j-api/src/main/java/org/apache/logging/log4j/MarkerManager.java
+++ b/log4j-api/src/main/java/org/apache/logging/log4j/MarkerManager.java
@@ -16,12 +16,10 @@
*/
package org.apache.logging.log4j;
-import aQute.bnd.annotation.baseline.BaselineIgnore;
+import com.google.errorprone.annotations.InlineMe;
import java.util.Arrays;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
-import java.util.concurrent.locks.Lock;
-import java.util.concurrent.locks.ReentrantLock;
import org.apache.logging.log4j.util.PerformanceSensitive;
import org.apache.logging.log4j.util.StringBuilderFormattable;
@@ -70,6 +68,41 @@ public final class MarkerManager {
return result;
}
+ /**
+ * Retrieves or creates a Marker with the specified parent. The parent must have been previously created.
+ *
+ * @param name The name of the Marker.
+ * @param parent The name of the parent Marker.
+ * @return The Marker with the specified name.
+ * @throws IllegalArgumentException if the parent Marker does not exist.
+ * @deprecated Use the Marker add or set methods to add parent Markers. Will be removed by final GA release.
+ */
+ @Deprecated
+ public static Marker getMarker(final String name, final String parent) {
+ final Marker parentMarker = MARKERS.get(parent);
+ if (parentMarker == null) {
+ throw new IllegalArgumentException("Parent Marker " + parent + " has not been defined");
+ }
+ return getMarker(name).addParents(parentMarker);
+ }
+
+ /**
+ * Retrieves or creates a Marker with the specified parent.
+ *
+ * @param name The name of the Marker.
+ * @param parent The parent Marker.
+ * @return The Marker with the specified name.
+ * @throws IllegalArgumentException if any argument is {@code null}
+ * @deprecated Use the Marker add or set methods to add parent Markers. Will be removed by final GA release.
+ */
+ @InlineMe(
+ replacement = "MarkerManager.getMarker(name).addParents(parent)",
+ imports = "org.apache.logging.log4j.MarkerManager")
+ @Deprecated
+ public static Marker getMarker(final String name, final Marker parent) {
+ return getMarker(name).addParents(parent);
+ }
+
/**
* <em>Consider this class private, it is only public to satisfy Jackson for XML and JSON IO.</em>
* <p>
@@ -81,15 +114,14 @@ public final class MarkerManager {
* is moved to this package and would of course stay in its current module.</em>
* </p>
*/
- @BaselineIgnore("3.0.0")
public static class Log4jMarker implements Marker, StringBuilderFormattable {
+ private static final long serialVersionUID = 100L;
+
private final String name;
private volatile Marker[] parents;
- private final Lock lock = new ReentrantLock();
-
/**
* Required by JAXB and Jackson for XML and JSON IO.
*/
@@ -113,81 +145,73 @@ public final class MarkerManager {
this.parents = null;
}
+ // TODO: use java.util.concurrent
+
@Override
- public Marker addParents(final Marker... parentMarkers) {
+ public synchronized Marker addParents(final Marker... parentMarkers) {
requireNonNull(parentMarkers, "A parent marker must be specified");
- lock.lock();
- try {
- // It is not strictly necessary to copy the variable here, but it should perform better than
- // accessing a volatile variable multiple times.
- final Marker[] localParents = this.parents;
- // Don't add a parent that is already in the hierarchy.
- int count = 0;
- int size = parentMarkers.length;
- if (localParents != null) {
- for (final Marker parent : parentMarkers) {
- if (!(contains(parent, localParents) || parent.isInstanceOf(this))) {
- ++count;
- }
- }
- if (count == 0) {
- return this;
+ // It is not strictly necessary to copy the variable here but it should perform better than
+ // Accessing a volatile variable multiple times.
+ final Marker[] localParents = this.parents;
+ // Don't add a parent that is already in the hierarchy.
+ int count = 0;
+ int size = parentMarkers.length;
+ if (localParents != null) {
+ for (final Marker parent : parentMarkers) {
+ if (!(contains(parent, localParents) || parent.isInstanceOf(this))) {
+ ++count;
}
- size = localParents.length + count;
}
- final Marker[] markers = new Marker[size];
- if (localParents != null) {
- // It's perfectly OK to call arraycopy in a synchronized context; it's still faster
- // noinspection CallToNativeMethodWhileLocked
- System.arraycopy(localParents, 0, markers, 0, localParents.length);
+ if (count == 0) {
+ return this;
}
- int index = localParents == null ? 0 : localParents.length;
- for (final Marker parent : parentMarkers) {
- if (localParents == null || !(contains(parent, localParents) || parent.isInstanceOf(this))) {
- markers[index++] = parent;
- }
+ size = localParents.length + count;
+ }
+ final Marker[] markers = new Marker[size];
+ if (localParents != null) {
+ // It's perfectly OK to call arraycopy in a synchronized context; it's still faster
+ // noinspection CallToNativeMethodWhileLocked
+ System.arraycopy(localParents, 0, markers, 0, localParents.length);
+ }
+ int index = localParents == null ? 0 : localParents.length;
+ for (final Marker parent : parentMarkers) {
+ if (localParents == null || !(contains(parent, localParents) || parent.isInstanceOf(this))) {
+ markers[index++] = parent;
}
- this.parents = markers;
- } finally {
- lock.unlock();
}
+ this.parents = markers;
return this;
}
@Override
- public boolean remove(final Marker parent) {
+ public synchronized boolean remove(final Marker parent) {
requireNonNull(parent, "A parent marker must be specified");
- lock.lock();
- try {
- final Marker[] localParents = this.parents;
- if (localParents == null) {
- return false;
- }
- final int localParentsLength = localParents.length;
- if (localParentsLength == 1) {
- if (localParents[0].equals(parent)) {
- parents = null;
- return true;
- }
- return false;
+ final Marker[] localParents = this.parents;
+ if (localParents == null) {
+ return false;
+ }
+ final int localParentsLength = localParents.length;
+ if (localParentsLength == 1) {
+ if (localParents[0].equals(parent)) {
+ parents = null;
+ return true;
}
- int index = 0;
- final Marker[] markers = new Marker[localParentsLength - 1];
- // noinspection ForLoopReplaceableByForEach
- for (int i = 0; i < localParentsLength; i++) {
- final Marker marker = localParents[i];
- if (!marker.equals(parent)) {
- if (index == localParentsLength - 1) {
- // no need to swap array
- return false;
- }
- markers[index++] = marker;
+ return false;
+ }
+ int index = 0;
+ final Marker[] markers = new Marker[localParentsLength - 1];
+ // noinspection ForLoopReplaceableByForEach
+ for (int i = 0; i < localParentsLength; i++) {
+ final Marker marker = localParents[i];
+ if (!marker.equals(parent)) {
+ if (index == localParentsLength - 1) {
+ // no need to swap array
+ return false;
}
+ markers[index++] = marker;
}
- parents = markers;
- } finally {
- lock.unlock();
}
+ parents = markers;
return true;
}
diff --git a/log4j-api/src/main/java/org/apache/logging/log4j/ThreadContext.java b/log4j-api/src/main/java/org/apache/logging/log4j/ThreadContext.java
index 8a5a6a6b18..9d5e785386 100644
--- a/log4j-api/src/main/java/org/apache/logging/log4j/ThreadContext.java
+++ b/log4j-api/src/main/java/org/apache/logging/log4j/ThreadContext.java
@@ -16,28 +16,31 @@
*/
package org.apache.logging.log4j;
-import aQute.bnd.annotation.baseline.BaselineIgnore;
+import java.io.Serializable;
import java.util.AbstractCollection;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
-import java.util.Objects;
+import java.util.NoSuchElementException;
import org.apache.logging.log4j.message.ParameterizedMessage;
-import org.apache.logging.log4j.spi.LoggingSystem;
-import org.apache.logging.log4j.spi.LoggingSystemProperty;
+import org.apache.logging.log4j.spi.CleanableThreadContextMap;
+import org.apache.logging.log4j.spi.DefaultThreadContextMap;
+import org.apache.logging.log4j.spi.DefaultThreadContextStack;
+import org.apache.logging.log4j.spi.NoOpThreadContextMap;
import org.apache.logging.log4j.spi.ReadOnlyThreadContextMap;
import org.apache.logging.log4j.spi.ThreadContextMap;
+import org.apache.logging.log4j.spi.ThreadContextMap2;
+import org.apache.logging.log4j.spi.ThreadContextMapFactory;
import org.apache.logging.log4j.spi.ThreadContextStack;
-import org.apache.logging.log4j.util.InternalApi;
-import org.apache.logging.log4j.util.Strings;
+import org.apache.logging.log4j.util.PropertiesUtil;
/**
* The ThreadContext allows applications to store information either in a Map or a Stack.
* <p>
* <b><em>The MDC is managed on a per thread basis</em></b>. To enable automatic inheritance of <i>copies</i> of the MDC
- * to newly created threads, enable the {@link LoggingSystemProperty#THREAD_CONTEXT_MAP_INHERITABLE}
+ * to newly created threads, enable the {@value org.apache.logging.log4j.spi.DefaultThreadContextMap#INHERITABLE_MAP}
* Log4j system property.
* </p>
* @see <a href="https://logging.apache.org/log4j/2.x/manual/thread-context.html">Thread Context Manual</a>
@@ -49,14 +52,18 @@ public final class ThreadContext {
*/
private static class EmptyThreadContextStack extends AbstractCollection<String> implements ThreadContextStack {
+ private static final long serialVersionUID = 1L;
+
+ private static final Iterator<String> EMPTY_ITERATOR = new EmptyIterator<>();
+
@Override
public String pop() {
- return Strings.EMPTY;
+ return null;
}
@Override
public String peek() {
- return Strings.EMPTY;
+ return null;
}
@Override
@@ -128,7 +135,7 @@ public final class ThreadContext {
@Override
public Iterator<String> iterator() {
- return Collections.emptyIterator();
+ return EMPTY_ITERATOR;
}
@Override
@@ -142,6 +149,29 @@ public final class ThreadContext {
}
}
+ /**
+ * An empty iterator. Since Java 1.7 added the Collections.emptyIterator() method, we have to make do.
+ *
+ * @param <E> the type of the empty iterator
+ */
+ private static class EmptyIterator<E> implements Iterator<E> {
+
+ @Override
+ public boolean hasNext() {
+ return false;
+ }
+
+ @Override
+ public E next() {
+ throw new NoSuchElementException("This is an empty iterator!");
+ }
+
+ @Override
+ public void remove() {
+ // no-op
+ }
+ }
+
/**
* Empty, immutable Map.
*/
@@ -157,6 +187,11 @@ public final class ThreadContext {
@SuppressWarnings("PublicStaticCollectionField")
public static final ThreadContextStack EMPTY_STACK = new EmptyThreadContextStack();
+ private static final String DISABLE_MAP = "log4j2.disableThreadContextMap";
+ private static final String DISABLE_STACK = "log4j2.disableThreadContextStack";
+ private static final String DISABLE_ALL = "log4j2.disableThreadContext";
+
+ private static boolean useStack;
private static ThreadContextMap contextMap;
private static ThreadContextStack contextStack;
private static ReadOnlyThreadContextMap readOnlyContextMap;
@@ -172,10 +207,20 @@ public final class ThreadContext {
/**
* <em>Consider private, used for testing.</em>
*/
- @InternalApi
public static void init() {
- contextMap = LoggingSystem.createContextMap();
- contextStack = LoggingSystem.createContextStack();
+ ThreadContextMapFactory.init();
+ contextMap = null;
+ final PropertiesUtil managerProps = PropertiesUtil.getProperties();
+ final boolean disableAll = managerProps.getBooleanProperty(DISABLE_ALL);
+ useStack = !(managerProps.getBooleanProperty(DISABLE_STACK) || disableAll);
+ final boolean useMap = !(managerProps.getBooleanProperty(DISABLE_MAP) || disableAll);
+
+ contextStack = new DefaultThreadContextStack(useStack);
+ if (!useMap) {
+ contextMap = new NoOpThreadContextMap();
+ } else {
+ contextMap = ThreadContextMapFactory.createThreadContextMap();
+ }
if (contextMap instanceof ReadOnlyThreadContextMap) {
readOnlyContextMap = (ReadOnlyThreadContextMap) contextMap;
} else {
@@ -226,7 +271,15 @@ public final class ThreadContext {
* @since 2.7
*/
public static void putAll(final Map<String, String> m) {
- contextMap.putAll(m);
+ if (contextMap instanceof ThreadContextMap2) {
+ ((ThreadContextMap2) contextMap).putAll(m);
+ } else if (contextMap instanceof DefaultThreadContextMap) {
+ ((DefaultThreadContextMap) contextMap).putAll(m);
+ } else {
+ for (final Map.Entry<String, String> entry : m.entrySet()) {
+ contextMap.put(entry.getKey(), entry.getValue());
+ }
+ }
}
/**
@@ -260,7 +313,15 @@ public final class ThreadContext {
* @since 2.8
*/
public static void removeAll(final Iterable<String> keys) {
- contextMap.removeAll(keys);
+ if (contextMap instanceof CleanableThreadContextMap) {
+ ((CleanableThreadContextMap) contextMap).removeAll(keys);
+ } else if (contextMap instanceof DefaultThreadContextMap) {
+ ((DefaultThreadContextMap) contextMap).removeAll(keys);
+ } else {
+ for (final String key : keys) {
+ contextMap.remove(key);
+ }
+ }
}
/**
@@ -311,8 +372,16 @@ public final class ThreadContext {
* Returns a read-only view of the internal data structure used to store thread context key-value pairs,
* or {@code null} if the internal data structure does not implement the
* {@code ReadOnlyThreadContextMap} interface.
+ * <p>
+ * The {@link DefaultThreadContextMap} implementation does not implement {@code ReadOnlyThreadContextMap}, so by
+ * default this method returns {@code null}.
+ * </p>
*
* @return the internal data structure used to store thread context key-value pairs or {@code null}
+ * @see ThreadContextMapFactory
+ * @see DefaultThreadContextMap
+ * @see org.apache.logging.log4j.spi.CopyOnWriteSortedArrayThreadContextMap
+ * @see org.apache.logging.log4j.spi.GarbageFreeSortedArrayThreadContextMap
* @since 2.8
*/
public static ReadOnlyThreadContextMap getThreadContextMap() {
@@ -358,11 +427,9 @@ public final class ThreadContext {
* Sets this thread's stack.
*
* @param stack The stack to use.
- * @throws NullPointerException if stack is null
*/
public static void setStack(final Collection<String> stack) {
- Objects.requireNonNull(stack, "No stack provided");
- if (stack.isEmpty()) {
+ if (stack.isEmpty() || !useStack) {
return;
}
contextStack.clear();
@@ -416,7 +483,6 @@ public final class ThreadContext {
* </p>
*
* @param message The new diagnostic context information.
- * @throws UnsupportedOperationException if the context stack is disabled
*/
public static void push(final String message) {
contextStack.push(message);
@@ -433,7 +499,6 @@ public final class ThreadContext {
*
* @param message The new diagnostic context information.
* @param args Parameters for the message.
- * @throws UnsupportedOperationException if the context stack is disabled
*/
public static void push(final String message, final Object... args) {
contextStack.push(ParameterizedMessage.format(message, args));
@@ -490,7 +555,6 @@ public final class ThreadContext {
*
* @see #getDepth
* @param depth The number of elements to keep.
- * @throws IllegalArgumentException if depth is negative
*/
public static void trim(final int depth) {
contextStack.trim(depth);
@@ -499,20 +563,20 @@ public final class ThreadContext {
/**
* The ThreadContext Stack interface.
*/
- @BaselineIgnore("3.0.0")
- public interface ContextStack extends Collection<String> {
+ public interface ContextStack extends Serializable, Collection<String> {
/**
- * Returns the element at the top of the stack. If the stack is empty, then the empty string is returned.
+ * Returns the element at the top of the stack.
*
* @return The element at the top of the stack.
+ * @throws java.util.NoSuchElementException if the stack is empty.
*/
String pop();
/**
- * Returns the element at the top of the stack without removing it or the empty string if the stack is empty.
+ * Returns the element at the top of the stack without removing it or null if the stack is empty.
*
- * @return the element at the top of the stack or the empty string if the stack is empty.
+ * @return the element at the top of the stack or null if the stack is empty.
*/
String peek();
@@ -520,8 +584,6 @@ public final class ThreadContext {
* Pushes an element onto the stack.
*
* @param message The element to add.
- * @throws NullPointerException if message is null
- * @throws UnsupportedOperationException if the context stack is disabled
*/
void push(String message);
@@ -543,7 +605,6 @@ public final class ThreadContext {
* Trims elements from the end of the stack.
*
* @param depth The maximum number of items in the stack to keep.
- * @throws IllegalArgumentException if depth is negative
*/
void trim(int depth);
diff --git a/log4j-api/src/main/java/org/apache/logging/log4j/internal/DefaultLogBuilder.java b/log4j-api/src/main/java/org/apache/logging/log4j/internal/DefaultLogBuilder.java
index e0e6b4362c..608f0ad21e 100644
--- a/log4j-api/src/main/java/org/apache/logging/log4j/internal/DefaultLogBuilder.java
+++ b/log4j-api/src/main/java/org/apache/logging/log4j/internal/DefaultLogBuilder.java
@@ -20,38 +20,39 @@ import java.util.Arrays;
import org.apache.logging.log4j.BridgeAware;
import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.LogBuilder;
+import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.Marker;
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.InternalApi;
+import org.apache.logging.log4j.status.StatusLogger;
import org.apache.logging.log4j.util.LambdaUtil;
import org.apache.logging.log4j.util.StackLocatorUtil;
-import org.apache.logging.log4j.util.Strings;
import org.apache.logging.log4j.util.Supplier;
/**
* Collects data for a log event and then logs it. This class should be considered private.
*/
-@InternalApi
-public class DefaultLogBuilder implements BridgeAware, LogBuilder, RecyclerAware<LogBuilder> {
+public class DefaultLogBuilder implements BridgeAware, LogBuilder {
+ private static Message EMPTY_MESSAGE = new SimpleMessage("");
private static final String FQCN = DefaultLogBuilder.class.getName();
- private static final Message EMPTY_MESSAGE = new SimpleMessage(Strings.EMPTY);
+ private static final Logger LOGGER = StatusLogger.getLogger();
private ExtendedLogger logger;
private Level level;
private Marker marker;
private Throwable throwable;
private StackTraceElement location;
+ private volatile boolean inUse;
+ private long threadId;
private String fqcn = FQCN;
- private Recycler<LogBuilder> recycler;
public DefaultLogBuilder(final ExtendedLogger logger, final Level level) {
this.logger = logger;
this.level = level;
+ this.threadId = Thread.currentThread().getId();
+ this.inUse = level != null;
}
public DefaultLogBuilder() {
@@ -74,6 +75,7 @@ public class DefaultLogBuilder implements BridgeAware, LogBuilder, RecyclerAware
this.marker = null;
this.throwable = null;
this.location = null;
+ this.inUse = true;
return this;
}
@@ -96,14 +98,18 @@ public class DefaultLogBuilder implements BridgeAware, LogBuilder, RecyclerAware
}
@Override
- public LogBuilder withLocation(final StackTraceElement location) {
+ public LogBuilder withLocation(StackTraceElement location) {
this.location = location;
return this;
}
+ public boolean isInUse() {
+ return inUse;
+ }
+
@Override
- public void log(final Message message) {
- if (isEnabled(message)) {
+ public void log(Message message) {
+ if (isValid() && isEnabled(message)) {
logMessage(message);
}
}
@@ -112,7 +118,7 @@ public class DefaultLogBuilder implements BridgeAware, LogBuilder, RecyclerAware
@SuppressWarnings("deprecation")
public Message logAndGet(final Supplier<Message> messageSupplier) {
Message message = null;
- if (isEnabled(message = messageSupplier.get())) {
+ if (isValid() && isEnabled(message = messageSupplier.get())) {
logMessage(message);
}
return message;
@@ -120,21 +126,21 @@ public class DefaultLogBuilder implements BridgeAware, LogBuilder, RecyclerAware
@Override
public void log(final CharSequence message) {
- if (isEnabled(message)) {
+ if (isValid() && isEnabled(message)) {
logMessage(logger.getMessageFactory().newMessage(message));
}
}
@Override
- public void log(final String message) {
- if (isEnabled(message)) {
+ public void log(String message) {
+ if (isValid() && isEnabled(message)) {
logMessage(logger.getMessageFactory().newMessage(message));
}
}
@Override
- public void log(final String message, final Object... params) {
- if (isEnabled(message, params)) {
+ public void log(String message, Object... params) {
+ if (isValid() && isEnabled(message, params)) {
logMessage(logger.getMessageFactory().newMessage(message, params));
}
}
@@ -143,7 +149,7 @@ public class DefaultLogBuilder implements BridgeAware, LogBuilder, RecyclerAware
@SuppressWarnings("deprecation")
public void log(String message, Supplier<?>... params) {
final Object[] objs;
- if (isEnabled(message, objs = LambdaUtil.getAll(params))) {
+ if (isValid() && isEnabled(message, objs = LambdaUtil.getAll(params))) {
logMessage(logger.getMessageFactory().newMessage(message, objs));
}
}
@@ -155,147 +161,129 @@ public class DefaultLogBuilder implements BridgeAware, LogBuilder, RecyclerAware
}
@Override
- public void log(final Object message) {
- if (isEnabled(message)) {
+ public void log(Object message) {
+ if (isValid() && isEnabled(message)) {
logMessage(logger.getMessageFactory().newMessage(message));
}
}
@Override
- public void log(final String message, final Object p0) {
- if (isEnabled(message, p0)) {
+ public void log(String message, Object p0) {
+ if (isValid() && isEnabled(message, p0)) {
logMessage(logger.getMessageFactory().newMessage(message, p0));
}
}
@Override
- public void log(final String message, final Object p0, final Object p1) {
- if (isEnabled(message, p0, p1)) {
+ public void log(String message, Object p0, Object p1) {
+ if (isValid() && isEnabled(message, p0, p1)) {
logMessage(logger.getMessageFactory().newMessage(message, p0, p1));
}
}
@Override
- public void log(final String message, final Object p0, final Object p1, final Object p2) {
- if (isEnabled(message, p0, p1, p2)) {
+ public void log(String message, Object p0, Object p1, Object p2) {
+ if (isValid() && isEnabled(message, p0, p1, p2)) {
logMessage(logger.getMessageFactory().newMessage(message, p0, p1, p2));
}
}
@Override
- public void log(final String message, final Object p0, final Object p1, final Object p2, final Object p3) {
- if (isEnabled(message, p0, p1, p2, p3)) {
+ public void log(String message, Object p0, Object p1, Object p2, Object p3) {
+ if (isValid() && isEnabled(message, p0, p1, p2, p3)) {
logMessage(logger.getMessageFactory().newMessage(message, p0, p1, p2, p3));
}
}
@Override
- public void log(
- final String message, final Object p0, final Object p1, final Object p2, final Object p3, final Object p4) {
- if (isEnabled(message, p0, p1, p2, p3, p4)) {
+ public void log(String message, Object p0, Object p1, Object p2, Object p3, Object p4) {
+ if (isValid() && isEnabled(message, p0, p1, p2, p3, p4)) {
logMessage(logger.getMessageFactory().newMessage(message, p0, p1, p2, p3, p4));
}
}
@Override
- public void log(
- final String message,
- final Object p0,
- final Object p1,
- final Object p2,
- final Object p3,
- final Object p4,
- final Object p5) {
- if (isEnabled(message, p0, p1, p2, p3, p4, p5)) {
+ public void log(String message, Object p0, Object p1, Object p2, Object p3, Object p4, Object p5) {
+ if (isValid() && isEnabled(message, p0, p1, p2, p3, p4, p5)) {
logMessage(logger.getMessageFactory().newMessage(message, p0, p1, p2, p3, p4, p5));
}
}
@Override
- public void log(
- final String message,
- final Object p0,
- final Object p1,
- final Object p2,
- final Object p3,
- final Object p4,
- final Object p5,
- final Object p6) {
- if (isEnabled(message, p0, p1, p2, p3, p4, p5, p6)) {
+ public void log(String message, Object p0, Object p1, Object p2, Object p3, Object p4, Object p5, Object p6) {
+ if (isValid() && isEnabled(message, p0, p1, p2, p3, p4, p5, p6)) {
logMessage(logger.getMessageFactory().newMessage(message, p0, p1, p2, p3, p4, p5, p6));
}
}
@Override
public void log(
- 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) {
- if (isEnabled(message, p0, p1, p2, p3, p4, p5, p6, p7)) {
+ String message, Object p0, Object p1, Object p2, Object p3, Object p4, Object p5, Object p6, Object p7) {
+ if (isValid() && isEnabled(message, p0, p1, p2, p3, p4, p5, p6, p7)) {
logMessage(logger.getMessageFactory().newMessage(message, p0, p1, p2, p3, p4, p5, p6, p7));
}
}
@Override
public void log(
- 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) {
- if (isEnabled(message, p0, p1, p2, p3, p4, p5, p6, p7, p8)) {
+ String message,
+ Object p0,
+ Object p1,
+ Object p2,
+ Object p3,
+ Object p4,
+ Object p5,
+ Object p6,
+ Object p7,
+ Object p8) {
+ if (isValid() && isEnabled(message, p0, p1, p2, p3, p4, p5, p6, p7, p8)) {
logMessage(logger.getMessageFactory().newMessage(message, p0, p1, p2, p3, p4, p5, p6, p7, p8));
}
}
@Override
public void log(
- 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) {
- if (isEnabled(message, p0, p1, p2, p3, p4, p5, p6, p7, p8, p9)) {
+ String message,
+ Object p0,
+ Object p1,
+ Object p2,
+ Object p3,
+ Object p4,
+ Object p5,
+ Object p6,
+ Object p7,
+ Object p8,
+ Object p9) {
+ if (isValid() && isEnabled(message, p0, p1, p2, p3, p4, p5, p6, p7, p8, p9)) {
logMessage(logger.getMessageFactory().newMessage(message, p0, p1, p2, p3, p4, p5, p6, p7, p8, p9));
}
}
@Override
public void log() {
- if (isEnabled(EMPTY_MESSAGE)) {
+ if (isValid() && isEnabled(EMPTY_MESSAGE)) {
logMessage(EMPTY_MESSAGE);
}
}
- private void logMessage(final Message message) {
+ private void logMessage(Message message) {
try {
logger.logMessage(level, marker, fqcn, location, message, throwable);
} finally {
- this.level = null;
- this.marker = null;
- this.throwable = null;
- this.location = null;
- if (recycler != null) {
- recycler.release(this);
- }
+ inUse = false;
+ }
+ }
+
+ private boolean isValid() {
+ if (!inUse) {
+ LOGGER.warn("Attempt to reuse LogBuilder was ignored. {}", StackLocatorUtil.getCallerClass(2));
+ return false;
}
+ if (this.threadId != Thread.currentThread().getId()) {
+ LOGGER.warn("LogBuilder can only be used on the owning thread. {}", StackLocatorUtil.getCallerClass(2));
+ return false;
+ }
+ return true;
}
protected boolean isEnabled(Message message) {
@@ -407,9 +395,4 @@ public class DefaultLogBuilder implements BridgeAware, LogBuilder, RecyclerAware
? logger.isEnabled(level, marker, message, p0, p1, p2, p3, p4, p5, p6, p7, p8, p9, throwable)
: logger.isEnabled(level, marker, message, p0, p1, p2, p3, p4, p5, p6, p7, p8, p9);
}
-
- @Override
- public void setRecycler(Recycler<LogBuilder> recycler) {
- this.recycler = recycler;
- }
}
diff --git a/log4j-api/src/main/java/org/apache/logging/log4j/message/AbstractMessageFactory.java b/log4j-api/src/main/java/org/apache/logging/log4j/message/AbstractMessageFactory.java
index cfefaded66..c39c65becc 100644
--- a/log4j-api/src/main/java/org/apache/logging/log4j/message/AbstractMessageFactory.java
+++ b/log4j-api/src/main/java/org/apache/logging/log4j/message/AbstractMessageFactory.java
@@ -16,11 +16,175 @@
*/
package org.apache.logging.log4j.message;
+import java.io.Serializable;
+
/**
- * Provides an abstract superclass for {@link MessageFactory}. This class is now unnecessary as all default
- * methods are provided by the (@link MessageFactory) interface.
- *
- * @deprecated MessageFactory has default methods that implement all the methods that were here.
+ * Provides an abstract superclass for {@link MessageFactory2} implementations with default implementations (and for
+ * {@link MessageFactory} by extension).
+ * <p>
+ * This class is immutable.
+ * </p>
+ * <p>
+ * <strong>Note to implementors:</strong>
+ * </p>
+ * <p>
+ * Subclasses can implement the {@link MessageFactory2} methods when they can most effectively build {@link Message}
+ * instances. If a subclass does not implement {@link MessageFactory2} methods, these calls are routed through
+ * {@link #newMessage(String, Object...)} in this class.
+ * </p>
*/
-@Deprecated(forRemoval = true, since = "3.0")
-public abstract class AbstractMessageFactory implements MessageFactory {}
+public abstract class AbstractMessageFactory implements MessageFactory2, Serializable {
+ private static final long serialVersionUID = -1307891137684031187L;
+
+ @Override
+ public Message newMessage(final CharSequence message) {
+ return new SimpleMessage(message);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.apache.logging.log4j.message.MessageFactory#newMessage(java.lang.Object)
+ */
+ @Override
+ public Message newMessage(final Object message) {
+ return new ObjectMessage(message);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.apache.logging.log4j.message.MessageFactory#newMessage(java.lang.String)
+ */
+ @Override
+ public Message newMessage(final String message) {
+ return new SimpleMessage(message);
+ }
+
+ /**
+ * @since 2.6.1
+ */
+ @Override
+ public Message newMessage(final String message, final Object p0) {
+ return newMessage(message, new Object[] {p0});
+ }
+
+ /**
+ * @since 2.6.1
+ */
+ @Override
+ public Message newMessage(final String message, final Object p0, final Object p1) {
+ return newMessage(message, new Object[] {p0, p1});
+ }
+
+ /**
+ * @since 2.6.1
+ */
+ @Override
+ public Message newMessage(final String message, final Object p0, final Object p1, final Object p2) {
+ return newMessage(message, new Object[] {p0, p1, p2});
+ }
+
+ /**
+ * @since 2.6.1
+ */
+ @Override
+ public Message newMessage(
+ final String message, final Object p0, final Object p1, final Object p2, final Object p3) {
+ return newMessage(message, new Object[] {p0, p1, p2, p3});
+ }
+
+ /**
+ * @since 2.6.1
+ */
+ @Override
+ public Message newMessage(
+ final String message, final Object p0, final Object p1, final Object p2, final Object p3, final Object p4) {
+ return newMessage(message, new Object[] {p0, p1, p2, p3, p4});
+ }
+
+ /**
+ * @since 2.6.1
+ */
+ @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 newMessage(message, new Object[] {p0, p1, p2, p3, p4, p5});
+ }
+
+ /**
+ * @since 2.6.1
+ */
+ @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 newMessage(message, new Object[] {p0, p1, p2, p3, p4, p5, p6});
+ }
+
+ /**
+ * @since 2.6.1
+ */
+ @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 newMessage(message, new Object[] {p0, p1, p2, p3, p4, p5, p6, p7});
+ }
+
+ /**
+ * @since 2.6.1
+ */
+ @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 newMessage(message, new Object[] {p0, p1, p2, p3, p4, p5, p6, p7, p8});
+ }
+
+ /**
+ * @since 2.6.1
+ */
+ @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 newMessage(message, new Object[] {p0, p1, p2, p3, p4, p5, p6, p7, p8, p9});
+ }
+}
diff --git a/log4j-api/src/main/java/org/apache/logging/log4j/message/MessageFactory2.java b/log4j-api/src/main/java/org/apache/logging/log4j/message/MessageFactory2.java
index 5cdbb61cc8..755f89307c 100644
--- a/log4j-api/src/main/java/org/apache/logging/log4j/message/MessageFactory2.java
+++ b/log4j-api/src/main/java/org/apache/logging/log4j/message/MessageFactory2.java
@@ -19,8 +19,182 @@ package org.apache.logging.log4j.message;
/**
* Creates messages. Implementations can provide different message format syntaxes.
*
- * @deprecated MessageFactory now contains all interface methods.
+ * @see ParameterizedMessageFactory
* @since 2.6
*/
-@Deprecated(forRemoval = true, since = "3.0")
-public interface MessageFactory2 extends MessageFactory {}
+public interface MessageFactory2 extends MessageFactory {
+
+ /**
+ * Creates a new message for the specified CharSequence.
+ * @param charSequence the (potentially mutable) CharSequence
+ * @return a new message for the specified CharSequence
+ */
+ Message newMessage(CharSequence charSequence);
+
+ /**
+ * Creates a new parameterized message.
+ *
+ * @param message a message template, the kind of message template depends on the implementation.
+ * @param p0 a message parameter
+ * @return a new message
+ * @see ParameterizedMessageFactory
+ */
+ Message newMessage(String message, Object p0);
+
+ /**
+ * Creates a new parameterized message.
+ *
+ * @param message a message template, the kind of message template depends on the implementation.
+ * @param p0 a message parameter
+ * @param p1 a message parameter
+ * @return a new message
+ * @see ParameterizedMessageFactory
+ */
+ Message newMessage(String message, Object p0, Object p1);
+
+ /**
+ * Creates a new parameterized message.
+ *
+ * @param message a message template, the kind of message template depends on the implementation.
+ * @param p0 a message parameter
+ * @param p1 a message parameter
+ * @param p2 a message parameter
+ * @return a new message
+ * @see ParameterizedMessageFactory
+ */
+ Message newMessage(String message, Object p0, Object p1, Object p2);
+
+ /**
+ * Creates a new parameterized message.
+ *
+ * @param message a message template, the kind of message template depends on the implementation.
+ * @param p0 a message parameter
+ * @param p1 a message parameter
+ * @param p2 a message parameter
+ * @param p3 a message parameter
+ * @return a new message
+ * @see ParameterizedMessageFactory
+ */
+ Message newMessage(String message, Object p0, Object p1, Object p2, Object p3);
+
+ /**
+ * Creates a new parameterized message.
+ *
+ * @param message a message template, the kind of message template depends on the implementation.
+ * @param p0 a message parameter
+ * @param p1 a message parameter
+ * @param p2 a message parameter
+ * @param p3 a message parameter
+ * @param p4 a message parameter
+ * @return a new message
+ * @see ParameterizedMessageFactory
+ */
+ Message newMessage(String message, Object p0, Object p1, Object p2, Object p3, Object p4);
+
+ /**
+ * Creates a new parameterized message.
+ *
+ * @param message a message template, the kind of message template depends on the implementation.
+ * @param p0 a message parameter
+ * @param p1 a message parameter
+ * @param p2 a message parameter
+ * @param p3 a message parameter
+ * @param p4 a message parameter
+ * @param p5 a message parameter
+ * @return a new message
+ * @see ParameterizedMessageFactory
+ */
+ Message newMessage(String message, Object p0, Object p1, Object p2, Object p3, Object p4, Object p5);
+
+ /**
+ * Creates a new parameterized message.
+ *
+ * @param message a message template, the kind of message template depends on the implementation.
+ * @param p0 a message parameter
+ * @param p1 a message parameter
+ * @param p2 a message parameter
+ * @param p3 a message parameter
+ * @param p4 a message parameter
+ * @param p5 a message parameter
+ * @param p6 a message parameter
+ * @return a new message
+ * @see ParameterizedMessageFactory
+ */
+ Message newMessage(String message, Object p0, Object p1, Object p2, Object p3, Object p4, Object p5, Object p6);
+
+ /**
+ * Creates a new parameterized message.
+ *
+ * @param message a message template, the kind of message template depends on the implementation.
+ * @param p0 a message parameter
+ * @param p1 a message parameter
+ * @param p2 a message parameter
+ * @param p3 a message parameter
+ * @param p4 a message parameter
+ * @param p5 a message parameter
+ * @param p6 a message parameter
+ * @param p7 a message parameter
+ * @return a new message
+ * @see ParameterizedMessageFactory
+ */
+ Message newMessage(
+ String message, Object p0, Object p1, Object p2, Object p3, Object p4, Object p5, Object p6, Object p7);
+
+ /**
+ * Creates a new parameterized message.
+ *
+ * @param message a message template, the kind of message template depends on the implementation.
+ * @param p0 a message parameter
+ * @param p1 a message parameter
+ * @param p2 a message parameter
+ * @param p3 a message parameter
+ * @param p4 a message parameter
+ * @param p5 a message parameter
+ * @param p6 a message parameter
+ * @param p7 a message parameter
+ * @param p8 a message parameter
+ * @return a new message
+ * @see ParameterizedMessageFactory
+ */
+ Message newMessage(
+ String message,
+ Object p0,
+ Object p1,
+ Object p2,
+ Object p3,
+ Object p4,
+ Object p5,
+ Object p6,
+ Object p7,
+ Object p8);
+
+ /**
+ * Creates a new parameterized message.
+ *
+ * @param message a message template, the kind of message template depends on the implementation.
+ * @param p0 a message parameter
+ * @param p1 a message parameter
+ * @param p2 a message parameter
+ * @param p3 a message parameter
+ * @param p4 a message parameter
+ * @param p5 a message parameter
+ * @param p6 a message parameter
+ * @param p7 a message parameter
+ * @param p8 a message parameter
+ * @param p9 a message parameter
+ * @return a new message
+ * @see ParameterizedMessageFactory
+ */
+ Message newMessage(
+ String message,
+ Object p0,
+ Object p1,
+ Object p2,
+ Object p3,
+ Object p4,
+ Object p5,
+ Object p6,
+ Object p7,
+ Object p8,
+ Object p9);
+}
diff --git a/log4j-api/src/main/java/org/apache/logging/log4j/message/ParameterizedMessageFactory.java b/log4j-api/src/main/java/org/apache/logging/log4j/message/ParameterizedMessageFactory.java
new file mode 100644
index 0000000000..4df77e3bca
--- /dev/null
+++ b/log4j-api/src/main/java/org/apache/logging/log4j/message/ParameterizedMessageFactory.java
@@ -0,0 +1,192 @@
+/*
+ * 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.message;
+
+/**
+ * Creates {@link FormattedMessage} instances for {@link MessageFactory2} methods (and {@link MessageFactory} by
+ * extension.)
+ * <p>
+ * Enables the use of <code>{}</code> parameter markers in message strings.
+ * </p>
+ * <p>
+ * Creates {@link ParameterizedMessage} instances for {@link #newMessage(String, Object...)}.
+ * </p>
+ * <p>
+ * This class is immutable.
+ * </p>
+ *
+ * <p>
+ * <strong>Note to implementors:</strong>
+ * </p>
+ * <p>
+ * This class implements all {@link MessageFactory2} methods.
+ * </p>
+ */
+public final class ParameterizedMessageFactory extends AbstractMessageFactory {
+ /**
+ * Instance of ParameterizedMessageFactory.
+ */
+ public static final ParameterizedMessageFactory INSTANCE = new ParameterizedMessageFactory();
+
+ private static final long serialVersionUID = -8970940216592525651L;
+
+ /**
+ * Constructs a message factory.
+ */
+ public ParameterizedMessageFactory() {}
+
+ /**
+ * Creates {@link ParameterizedMessage} instances.
+ *
+ * @param message The message pattern.
+ * @param params The message parameters.
+ * @return The Message.
+ *
+ * @see MessageFactory#newMessage(String, Object...)
+ */
+ @Override
+ public Message newMessage(final String message, final Object... params) {
+ return new ParameterizedMessage(message, params);
+ }
+
+ /**
+ * @since 2.6.1
+ */
+ @Override
+ public Message newMessage(final String message, final Object p0) {
+ return new ParameterizedMessage(message, p0);
+ }
+
+ /**
+ * @since 2.6.1
+ */
+ @Override
+ public Message newMessage(final String message, final Object p0, final Object p1) {
+ return new ParameterizedMessage(message, p0, p1);
+ }
+
+ /**
+ * @since 2.6.1
+ */
+ @Override
+ public Message newMessage(final String message, final Object p0, final Object p1, final Object p2) {
+ return new ParameterizedMessage(message, p0, p1, p2);
+ }
+
+ /**
+ * @since 2.6.1
+ */
+ @Override
+ public Message newMessage(
+ final String message, final Object p0, final Object p1, final Object p2, final Object p3) {
+ return new ParameterizedMessage(message, p0, p1, p2, p3);
+ }
+
+ /**
+ * @since 2.6.1
+ */
+ @Override
+ public Message newMessage(
+ final String message, final Object p0, final Object p1, final Object p2, final Object p3, final Object p4) {
+ return new ParameterizedMessage(message, p0, p1, p2, p3, p4);
+ }
+
+ /**
+ * @since 2.6.1
+ */
+ @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 new ParameterizedMessage(message, p0, p1, p2, p3, p4, p5);
+ }
+
+ /**
+ * @since 2.6.1
+ */
+ @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 new ParameterizedMessage(message, p0, p1, p2, p3, p4, p5, p6);
+ }
+
+ /**
+ * @since 2.6.1
+ */
+ @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 new ParameterizedMessage(message, p0, p1, p2, p3, p4, p5, p6, p7);
+ }
+
+ /**
+ * @since 2.6.1
+ */
+ @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 new ParameterizedMessage(message, p0, p1, p2, p3, p4, p5, p6, p7, p8);
+ }
+
+ /**
+ * @since 2.6.1
+ */
+ @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 new ParameterizedMessage(message, p0, p1, p2, p3, p4, p5, p6, p7, p8, p9);
+ }
+}
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 f5210e1452..d9543f17b5 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
@@ -16,21 +16,26 @@
*/
package org.apache.logging.log4j.spi;
+import java.io.Serializable;
import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.LogBuilder;
import org.apache.logging.log4j.LoggingException;
import org.apache.logging.log4j.Marker;
import org.apache.logging.log4j.MarkerManager;
import org.apache.logging.log4j.internal.DefaultLogBuilder;
+import org.apache.logging.log4j.message.DefaultFlowMessageFactory;
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.ParameterizedMessageFactory;
+import org.apache.logging.log4j.message.ReusableMessageFactory;
import org.apache.logging.log4j.message.StringFormattedMessage;
-import org.apache.logging.log4j.spi.recycler.Recycler;
-import org.apache.logging.log4j.spi.recycler.RecyclerFactory;
import org.apache.logging.log4j.status.StatusLogger;
+import org.apache.logging.log4j.util.Constants;
import org.apache.logging.log4j.util.LambdaUtil;
+import org.apache.logging.log4j.util.LoaderUtil;
import org.apache.logging.log4j.util.MessageSupplier;
import org.apache.logging.log4j.util.PerformanceSensitive;
import org.apache.logging.log4j.util.StackLocatorUtil;
@@ -39,7 +44,7 @@ import org.apache.logging.log4j.util.Supplier;
/**
* Base implementation of a Logger. It is highly recommended that any Logger implementation extend this class.
*/
-public abstract class AbstractLogger implements ExtendedLogger {
+public abstract class AbstractLogger implements ExtendedLogger, LocationAwareLogger, Serializable {
// Implementation note: many methods in this class are tuned for performance. MODIFY WITH CARE!
// Specifically, try to keep the hot methods to 35 bytecodes or less:
// this is within the MaxInlineSize threshold on Java 7 and Java 8 Hotspot and makes these methods
@@ -77,21 +82,38 @@ public abstract class AbstractLogger implements ExtendedLogger {
public static final Marker CATCHING_MARKER =
MarkerManager.getMarker("CATCHING").setParents(EXCEPTION_MARKER);
+ /**
+ * The default MessageFactory class.
+ */
+ public static final Class<? extends MessageFactory> DEFAULT_MESSAGE_FACTORY_CLASS =
+ ParameterizedMessageFactory.class;
+
+ /**
+ * The default FlowMessageFactory class.
+ */
+ public static final Class<? extends FlowMessageFactory> DEFAULT_FLOW_MESSAGE_FACTORY_CLASS =
+ DefaultFlowMessageFactory.class;
+
+ private static final long serialVersionUID = 2L;
+
private static final String FQCN = AbstractLogger.class.getName();
private static final String THROWING = "Throwing";
private static final String CATCHING = "Catching";
protected final String name;
- private final MessageFactory messageFactory;
+ private final MessageFactory2 messageFactory;
private final FlowMessageFactory flowMessageFactory;
private static final ThreadLocal<int[]> recursionDepthHolder = new ThreadLocal<>(); // LOG4J2-1518, LOG4J2-2031
- private final Recycler<DefaultLogBuilder> recycler;
+ private static final ThreadLocal<DefaultLogBuilder> logBuilder = ThreadLocal.withInitial(DefaultLogBuilder::new);
/**
* Creates a new logger named after this class (or subclass).
*/
public AbstractLogger() {
- this(null, null, null, null);
+ final String canonicalName = getClass().getCanonicalName();
+ this.name = canonicalName != null ? canonicalName : getClass().getName();
+ this.messageFactory = createDefaultMessageFactory();
+ this.flowMessageFactory = createDefaultFlowMessageFactory();
}
/**
@@ -100,7 +122,7 @@ public abstract class AbstractLogger implements ExtendedLogger {
* @param name the logger name
*/
public AbstractLogger(final String name) {
- this(name, LoggingSystem.getMessageFactory(), null, null);
+ this(name, createDefaultMessageFactory());
}
/**
@@ -110,35 +132,15 @@ public abstract class AbstractLogger implements ExtendedLogger {
* @param messageFactory the message factory, if null then use the default message factory.
*/
public AbstractLogger(final String name, final MessageFactory messageFactory) {
- this(name, messageFactory, null, null);
- }
-
- private AbstractLogger(
- final String name,
- final MessageFactory messageFactory,
- final FlowMessageFactory flowMessageFactory,
- final RecyclerFactory recyclerFactory) {
- this.name = createNameFromClass(name, getClass());
- this.messageFactory = messageFactory != null ? messageFactory : LoggingSystem.getMessageFactory();
- this.flowMessageFactory =
- flowMessageFactory != null ? flowMessageFactory : LoggingSystem.getFlowMessageFactory();
- final RecyclerFactory effectiveRecyclerFactory =
- recyclerFactory != null ? recyclerFactory : LoggingSystem.getRecyclerFactory();
- this.recycler = effectiveRecyclerFactory.create(DefaultLogBuilder::new);
- }
-
- private static String createNameFromClass(String name, Class<?> clazz) {
- if (name != null) {
- return name;
- }
- final String canonicalName = clazz.getCanonicalName();
- return canonicalName != null ? canonicalName : clazz.getName();
+ this.name = name;
+ this.messageFactory = messageFactory == null ? createDefaultMessageFactory() : narrow(messageFactory);
+ this.flowMessageFactory = createDefaultFlowMessageFactory();
}
/**
* Checks that the message factory a logger was created with is the same as the given messageFactory. If they are
* different log a warning to the {@linkplain StatusLogger}. A null MessageFactory translates to the default
- * MessageFactory {@link LoggingSystem#getMessageFactory()}.
+ * MessageFactory {@link #DEFAULT_MESSAGE_FACTORY_CLASS}.
*
* @param logger The logger to check
* @param messageFactory The message factory to check.
@@ -146,7 +148,6 @@ public abstract class AbstractLogger implements ExtendedLogger {
public static void checkMessageFactory(final ExtendedLogger logger, final MessageFactory messageFactory) {
final String name = logger.getName();
final MessageFactory loggerMessageFactory = logger.getMessageFactory();
- final MessageFactory currentMessageFactory = LoggingSystem.getMessageFactory();
if (messageFactory != null && !loggerMessageFactory.equals(messageFactory)) {
StatusLogger.getLogger()
.warn(
@@ -155,7 +156,7 @@ public abstract class AbstractLogger implements ExtendedLogger {
name,
loggerMessageFactory,
messageFactory);
- } else if (messageFactory == null && loggerMessageFactory != currentMessageFactory) {
+ } else if (messageFactory == null && !loggerMessageFactory.getClass().equals(DEFAULT_MESSAGE_FACTORY_CLASS)) {
StatusLogger.getLogger()
.warn(
"The Logger {} was created with the message factory {} and is now requested with a null "
@@ -163,7 +164,7 @@ public abstract class AbstractLogger implements ExtendedLogger {
+ "formatting.",
name,
loggerMessageFactory,
- currentMessageFactory.getClass().getName());
+ DEFAULT_MESSAGE_FACTORY_CLASS.getName());
}
}
@@ -196,6 +197,30 @@ public abstract class AbstractLogger implements ExtendedLogger {
return messageFactory.newMessage(CATCHING);
}
+ private static MessageFactory2 createDefaultMessageFactory() {
+ try {
+ final MessageFactory result = LoaderUtil.newInstanceOf(DEFAULT_MESSAGE_FACTORY_CLASS);
+ return narrow(result);
+ } catch (final ReflectiveOperationException e) {
+ throw new IllegalStateException(e);
+ }
+ }
+
+ private static MessageFactory2 narrow(final MessageFactory result) {
+ if (result instanceof MessageFactory2) {
+ return (MessageFactory2) result;
+ }
+ return new MessageFactory2Adapter(result);
+ }
+
+ private static FlowMessageFactory createDefaultFlowMessageFactory() {
+ try {
+ return LoaderUtil.newInstanceOf(DEFAULT_FLOW_MESSAGE_FACTORY_CLASS);
+ } catch (final ReflectiveOperationException e) {
+ throw new IllegalStateException(e);
+ }
+ }
+
@Override
public void debug(final Marker marker, final CharSequence message) {
logIfEnabled(FQCN, Level.DEBUG, marker, message, null);
@@ -557,7 +582,6 @@ public abstract class AbstractLogger implements ExtendedLogger {
* @param fqcn The fully qualified class name of the <b>caller</b>.
* @param format Format String for the parameters.
* @param paramSuppliers The Suppliers of the parameters.
- * @return The EntryMessage.
*/
@SuppressWarnings("deprecation")
protected EntryMessage enter(final String fqcn, final String format, final Supplier<?>... paramSuppliers) {
@@ -573,13 +597,28 @@ public abstract class AbstractLogger implements ExtendedLogger {
return entryMsg;
}
+ /**
+ * Logs entry to a method with location information.
+ *
+ * @param fqcn The fully qualified class name of the <b>caller</b>.
+ * @param format The format String for the parameters.
+ * @param paramSuppliers The parameters to the method.
+ */
+ @Deprecated
+ protected EntryMessage enter(final String fqcn, final String format, final MessageSupplier... paramSuppliers) {
+ EntryMessage entryMsg = null;
+ if (isEnabled(Level.TRACE, ENTRY_MARKER, (Object) null, null)) {
+ logMessageSafely(fqcn, Level.TRACE, ENTRY_MARKER, entryMsg = entryMsg(format, paramSuppliers), null);
+ }
+ return entryMsg;
+ }
+
/**
* Logs entry to a method with location information.
*
* @param fqcn The fully qualified class name of the <b>caller</b>.
* @param format The format String for the parameters.
* @param params The parameters to the method.
- * @return The EntryMessage.
*/
protected EntryMessage enter(final String fqcn, final String format, final Object... params) {
EntryMessage entryMsg = null;
@@ -598,8 +637,29 @@ public abstract class AbstractLogger implements ExtendedLogger {
* Logs entry to a method with location information.
*
* @param fqcn The fully qualified class name of the <b>caller</b>.
- * @param message the Message.
- * @return The EntryMessage.
+ * @param messageSupplier The Supplier of the Message.
+ */
+ @Deprecated
+ protected EntryMessage enter(final String fqcn, final MessageSupplier messageSupplier) {
+ EntryMessage message = null;
+ if (isEnabled(Level.TRACE, ENTRY_MARKER, (Object) null, null)) {
+ logMessageSafely(
+ fqcn,
+ Level.TRACE,
+ ENTRY_MARKER,
+ message = flowMessageFactory.newEntryMessage(messageSupplier.get()),
+ null);
+ }
+ return message;
+ }
+
+ /**
+ * Logs entry to a method with location information.
+ *
+ * @param fqcn
+ * The fully qualified class name of the <b>caller</b>.
+ * @param message
+ * the Message.
* @since 2.6
*/
protected EntryMessage enter(final String fqcn, final Message message) {
@@ -611,6 +671,18 @@ public abstract class AbstractLogger implements ExtendedLogger {
return flowMessage;
}
+ @Deprecated
+ @Override
+ public void entry() {
+ entry(FQCN, (Object[]) null);
+ }
+
+ @Deprecated
+ @Override
+ public void entry(final Object... params) {
+ entry(FQCN, params);
+ }
+
/**
* Logs entry to a method with location information.
*
@@ -1001,6 +1073,18 @@ public abstract class AbstractLogger implements ExtendedLogger {
logIfEnabled(FQCN, Level.ERROR, null, message, p0, p1, p2, p3, p4, p5, p6, p7, p8, p9);
}
+ @Deprecated
+ @Override
+ public void exit() {
+ exit(FQCN, (Object) null);
+ }
+
+ @Deprecated
+ @Override
+ public <R> R exit(final R result) {
+ return exit(FQCN, result);
+ }
+
/**
* Logs exiting from a method with the result and location information.
*
@@ -1020,7 +1104,6 @@ public abstract class AbstractLogger implements ExtendedLogger {
* Logs exiting from a method with the result and location information.
*
* @param fqcn The fully qualified class name of the <b>caller</b>.
- * @param format The format string.
* @param <R> The type of the parameter and object being returned.
* @param result The result being returned from the method call.
* @return the return value passed to this method.
@@ -2715,7 +2798,7 @@ public abstract class AbstractLogger implements ExtendedLogger {
handleLogMessageException(ex, fqcn, message);
} finally {
decrementRecursionDepth();
- messageFactory.recycle(message);
+ ReusableMessageFactory.release(message);
}
}
@@ -2758,7 +2841,7 @@ public abstract class AbstractLogger implements ExtendedLogger {
logMessageTrackRecursion(fqcn, level, marker, message, throwable);
} finally {
// LOG4J2-1583 prevent scrambled logs when logging calls are nested (logging in toString())
- messageFactory.recycle(message);
+ ReusableMessageFactory.release(message);
}
}
@@ -3754,7 +3837,12 @@ public abstract class AbstractLogger implements ExtendedLogger {
* @since 2.20.0
*/
protected LogBuilder getLogBuilder(final Level level) {
- DefaultLogBuilder builder = recycler.acquire();
- return builder.reset(this, level);
+ if (Constants.ENABLE_THREADLOCALS) {
+ final DefaultLogBuilder builder = logBuilder.get();
+ if (!builder.isInUse()) {
+ return builder.reset(this, level);
+ }
+ }
+ return new DefaultLogBuilder(this, level);
}
}
diff --git a/log4j-api/src/main/java/org/apache/logging/log4j/spi/CleanableThreadContextMap.java b/log4j-api/src/main/java/org/apache/logging/log4j/spi/CleanableThreadContextMap.java
index 9da75e5a37..b5d97454b2 100644
--- a/log4j-api/src/main/java/org/apache/logging/log4j/spi/CleanableThreadContextMap.java
+++ b/log4j-api/src/main/java/org/apache/logging/log4j/spi/CleanableThreadContextMap.java
@@ -17,12 +17,22 @@
package org.apache.logging.log4j.spi;
/**
- * Legacy interface for backward compatibility with extensions to ThreadContextMap.
- * These methods have since been moved to default methods on ThreadContextMap.
+ * Extension service provider interface to implement additional custom MDC behavior for
+ * {@link org.apache.logging.log4j.ThreadContext}.
*
* @see ThreadContextMap
* @since 2.8
- * @deprecated use {@link ThreadContextMap} directly
*/
-@Deprecated(forRemoval = true, since = "3.0")
-public interface CleanableThreadContextMap extends ThreadContextMap2 {}
+public interface CleanableThreadContextMap extends ThreadContextMap2 {
+
+ /**
+ * Removes all given context map keys from the current thread's context map.
+ *
+ * <p>If the current thread does not have a context map it is
+ * created as a side effect.</p>
+ *
+ * @param keys The keys.
+ * @since 2.8
+ */
+ void removeAll(final Iterable<String> keys);
+}
diff --git a/log4j-api/src/main/java/org/apache/logging/log4j/spi/CopyOnWriteSortedArrayThreadContextMap.java b/log4j-api/src/main/java/org/apache/logging/log4j/spi/CopyOnWriteSortedArrayThreadContextMap.java
index 5b3fef2aee..04fc06b9af 100644
--- a/log4j-api/src/main/java/org/apache/logging/log4j/spi/CopyOnWriteSortedArrayThreadContextMap.java
+++ b/log4j-api/src/main/java/org/apache/logging/log4j/spi/CopyOnWriteSortedArrayThreadContextMap.java
@@ -16,10 +16,6 @@
*/
package org.apache.logging.log4j.spi;
-import static org.apache.logging.log4j.spi.LoggingSystem.THREAD_CONTEXT_DEFAULT_INITIAL_CAPACITY;
-import static org.apache.logging.log4j.spi.LoggingSystemProperty.THREAD_CONTEXT_INITIAL_CAPACITY;
-import static org.apache.logging.log4j.spi.LoggingSystemProperty.THREAD_CONTEXT_MAP_INHERITABLE;
-
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
@@ -38,7 +34,23 @@ import org.apache.logging.log4j.util.StringMap;
*
* @since 2.7
*/
-class CopyOnWriteSortedArrayThreadContextMap implements ReadOnlyThreadContextMap, ThreadContextMap, CopyOnWrite {
+class CopyOnWriteSortedArrayThreadContextMap implements ReadOnlyThreadContextMap, ObjectThreadContextMap, CopyOnWrite {
+
+ /**
+ * Property name ({@value} ) for selecting {@code InheritableThreadLocal} (value "true") or plain
+ * {@code ThreadLocal} (value is not "true") in the implementation.
+ */
+ public static final String INHERITABLE_MAP = "isThreadContextMapInheritable";
+
+ /**
+ * The default initial capacity.
+ */
+ protected static final int DEFAULT_INITIAL_CAPACITY = 16;
+
+ /**
+ * System property name that can be used to control the data structure's initial capacity.
+ */
+ protected static final String PROPERTY_NAME_INITIAL_CAPACITY = "log4j2.ThreadContext.initial.capacity";
private static final StringMap EMPTY_CONTEXT_DATA = new SortedArrayStringMap(1);
@@ -47,13 +59,13 @@ class CopyOnWriteSortedArrayThreadContextMap implements ReadOnlyThreadContextMap
}
private final ThreadLocal<StringMap> localMap;
- protected final int initialCapacity;
+ private final int initialCapacity;
public CopyOnWriteSortedArrayThreadContextMap() {
this(
- PropertiesUtil.getProperties().getBooleanProperty(THREAD_CONTEXT_MAP_INHERITABLE),
+ PropertiesUtil.getProperties().getBooleanProperty(INHERITABLE_MAP),
PropertiesUtil.getProperties()
- .getIntegerProperty(THREAD_CONTEXT_INITIAL_CAPACITY, THREAD_CONTEXT_DEFAULT_INITIAL_CAPACITY));
+ .getIntegerProperty(PROPERTY_NAME_INITIAL_CAPACITY, DEFAULT_INITIAL_CAPACITY));
}
CopyOnWriteSortedArrayThreadContextMap(final boolean inheritableMap, final int initialCapacity) {
@@ -61,9 +73,11 @@ class CopyOnWriteSortedArrayThreadContextMap implements ReadOnlyThreadContextMap
this.initialCapacity = initialCapacity;
}
+ // LOG4J2-479: by default, use a plain ThreadLocal, only use InheritableThreadLocal if configured.
+ // (This method is package protected for JUnit tests.)
private ThreadLocal<StringMap> createThreadLocalMap(final boolean inheritableMap) {
if (inheritableMap) {
- return new InheritableThreadLocal<>() {
+ return new InheritableThreadLocal<StringMap>() {
@Override
protected StringMap childValue(final StringMap parentValue) {
if (parentValue == null) {
@@ -147,13 +161,13 @@ class CopyOnWriteSortedArrayThreadContextMap implements ReadOnlyThreadContextMap
@Override
public String get(final String key) {
- return getValue(key);
+ return (String) getValue(key);
}
@Override
public <V> V getValue(final String key) {
final StringMap map = localMap.get();
- return map == null ? null : map.getValue(key);
+ return map == null ? null : map.<V>getValue(key);
}
@Override
diff --git a/log4j-api/src/main/java/org/apache/logging/log4j/spi/DefaultThreadContextMap.java b/log4j-api/src/main/java/org/apache/logging/log4j/spi/DefaultThreadContextMap.java
new file mode 100644
index 0000000000..3c45eee98f
--- /dev/null
+++ b/log4j-api/src/main/java/org/apache/logging/log4j/spi/DefaultThreadContextMap.java
@@ -0,0 +1,244 @@
+/*
+ * 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.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Objects;
+import org.apache.logging.log4j.util.BiConsumer;
+import org.apache.logging.log4j.util.PropertiesUtil;
+import org.apache.logging.log4j.util.ReadOnlyStringMap;
+import org.apache.logging.log4j.util.TriConsumer;
+
+/**
+ * The actual ThreadContext Map. A new ThreadContext Map is created each time it is updated and the Map stored is always
+ * immutable. This means the Map can be passed to other threads without concern that it will be updated. Since it is
+ * expected that the Map will be passed to many more log events than the number of keys it contains the performance
+ * should be much better than if the Map was copied for each event.
+ */
+public class DefaultThreadContextMap implements ThreadContextMap, ReadOnlyStringMap {
+ private static final long serialVersionUID = 8218007901108944053L;
+
+ /**
+ * Property name ({@value} ) for selecting {@code InheritableThreadLocal} (value "true") or plain
+ * {@code ThreadLocal} (value is not "true") in the implementation.
+ */
+ public static final String INHERITABLE_MAP = "isThreadContextMapInheritable";
+
+ private final boolean useMap;
+ private final ThreadLocal<Map<String, String>> localMap;
+
+ private static boolean inheritableMap;
+
+ static {
+ init();
+ }
+
+ // LOG4J2-479: by default, use a plain ThreadLocal, only use InheritableThreadLocal if configured.
+ // (This method is package protected for JUnit tests.)
+ static ThreadLocal<Map<String, String>> createThreadLocalMap(final boolean isMapEnabled) {
+ if (inheritableMap) {
+ return new InheritableThreadLocal<Map<String, String>>() {
+ @Override
+ protected Map<String, String> childValue(final Map<String, String> parentValue) {
+ return parentValue != null && isMapEnabled //
+ ? Collections.unmodifiableMap(new HashMap<>(parentValue)) //
+ : null;
+ }
+ };
+ }
+ // if not inheritable, return plain ThreadLocal with null as initial value
+ return new ThreadLocal<>();
+ }
+
+ static void init() {
+ inheritableMap = PropertiesUtil.getProperties().getBooleanProperty(INHERITABLE_MAP);
+ }
+
+ public DefaultThreadContextMap() {
+ this(true);
+ }
+
+ public DefaultThreadContextMap(final boolean useMap) {
+ this.useMap = useMap;
+ this.localMap = createThreadLocalMap(useMap);
+ }
+
+ @Override
+ public void put(final String key, final String value) {
+ if (!useMap) {
+ return;
+ }
+ Map<String, String> map = localMap.get();
+ map = map == null ? new HashMap<>(1) : new HashMap<>(map);
+ map.put(key, value);
+ localMap.set(Collections.unmodifiableMap(map));
+ }
+
+ public void putAll(final Map<String, String> m) {
+ if (!useMap) {
+ return;
+ }
+ Map<String, String> map = localMap.get();
+ map = map == null ? new HashMap<>(m.size()) : new HashMap<>(map);
+ for (final Map.Entry<String, String> e : m.entrySet()) {
+ map.put(e.getKey(), e.getValue());
+ }
+ localMap.set(Collections.unmodifiableMap(map));
+ }
+
+ @Override
+ public String get(final String key) {
+ final Map<String, String> map = localMap.get();
+ return map == null ? null : map.get(key);
+ }
+
+ @Override
+ public void remove(final String key) {
+ final Map<String, String> map = localMap.get();
+ if (map != null) {
+ final Map<String, String> copy = new HashMap<>(map);
+ copy.remove(key);
+ localMap.set(Collections.unmodifiableMap(copy));
+ }
+ }
+
+ public void removeAll(final Iterable<String> keys) {
+ final Map<String, String> map = localMap.get();
+ if (map != null) {
+ final Map<String, String> copy = new HashMap<>(map);
+ for (final String key : keys) {
+ copy.remove(key);
+ }
+ localMap.set(Collections.unmodifiableMap(copy));
+ }
+ }
+
+ @Override
+ public void clear() {
+ localMap.remove();
+ }
+
+ @Override
+ public Map<String, String> toMap() {
+ return getCopy();
+ }
+
+ @Override
+ public boolean containsKey(final String key) {
+ final Map<String, String> map = localMap.get();
+ return map != null && map.containsKey(key);
+ }
+
+ @Override
+ public <V> void forEach(final BiConsumer<String, ? super V> action) {
+ final Map<String, String> map = localMap.get();
+ if (map == null) {
+ return;
+ }
+ for (final Map.Entry<String, String> entry : map.entrySet()) {
+ // BiConsumer should be able to handle values of any type V. In our case the values are of type String.
+ @SuppressWarnings("unchecked")
+ final V value = (V) entry.getValue();
+ action.accept(entry.getKey(), value);
+ }
+ }
+
+ @Override
+ public <V, S> void forEach(final TriConsumer<String, ? super V, S> action, final S state) {
+ final Map<String, String> map = localMap.get();
+ if (map == null) {
+ return;
+ }
+ for (final Map.Entry<String, String> entry : map.entrySet()) {
+ // TriConsumer should be able to handle values of any type V. In our case the values are of type String.
+ @SuppressWarnings("unchecked")
+ final V value = (V) entry.getValue();
+ action.accept(entry.getKey(), value, state);
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public <V> V getValue(final String key) {
+ final Map<String, String> map = localMap.get();
+ return (V) (map == null ? null : map.get(key));
+ }
+
+ @Override
+ public Map<String, String> getCopy() {
+ final Map<String, String> map = localMap.get();
+ return map == null ? new HashMap<>() : new HashMap<>(map);
+ }
+
+ @Override
+ public Map<String, String> getImmutableMapOrNull() {
+ return localMap.get();
+ }
+
+ @Override
+ public boolean isEmpty() {
+ final Map<String, String> map = localMap.get();
+ return map == null || map.isEmpty();
+ }
+
+ @Override
+ public int size() {
+ final Map<String, String> map = localMap.get();
+ return map == null ? 0 : map.size();
+ }
+
+ @Override
+ public String toString() {
+ final Map<String, String> map = localMap.get();
+ return map == null ? "{}" : map.toString();
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ final Map<String, String> map = this.localMap.get();
+ result = prime * result + ((map == null) ? 0 : map.hashCode());
+ result = prime * result + Boolean.valueOf(this.useMap).hashCode();
+ return result;
+ }
+
+ @Override
+ public boolean equals(final Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null) {
+ return false;
+ }
+ if (obj instanceof DefaultThreadContextMap) {
+ final DefaultThreadContextMap other = (DefaultThreadContextMap) obj;
+ if (this.useMap != other.useMap) {
+ return false;
+ }
+ }
+ if (!(obj instanceof ThreadContextMap)) {
+ return false;
+ }
+ final ThreadContextMap other = (ThreadContextMap) obj;
+ final Map<String, String> map = this.localMap.get();
+ final Map<String, String> otherMap = other.getImmutableMapOrNull();
+ return Objects.equals(map, otherMap);
+ }
+}
diff --git a/log4j-api/src/main/java/org/apache/logging/log4j/spi/GarbageFreeSortedArrayThreadContextMap.java b/log4j-api/src/main/java/org/apache/logging/log4j/spi/GarbageFreeSortedArrayThreadContextMap.java
index 5fe721ee39..d9d823d353 100644
--- a/log4j-api/src/main/java/org/apache/logging/log4j/spi/GarbageFreeSortedArrayThreadContextMap.java
+++ b/log4j-api/src/main/java/org/apache/logging/log4j/spi/GarbageFreeSortedArrayThreadContextMap.java
@@ -16,10 +16,6 @@
*/
package org.apache.logging.log4j.spi;
-import static org.apache.logging.log4j.spi.LoggingSystem.THREAD_CONTEXT_DEFAULT_INITIAL_CAPACITY;
-import static org.apache.logging.log4j.spi.LoggingSystemProperty.THREAD_CONTEXT_INITIAL_CAPACITY;
-import static org.apache.logging.log4j.spi.LoggingSystemProperty.THREAD_CONTEXT_MAP_INHERITABLE;
-
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
@@ -38,16 +34,32 @@ import org.apache.logging.log4j.util.StringMap;
* </p>
* @since 2.7
*/
-class GarbageFreeSortedArrayThreadContextMap implements ReadOnlyThreadContextMap, ThreadContextMap {
+class GarbageFreeSortedArrayThreadContextMap implements ReadOnlyThreadContextMap, ObjectThreadContextMap {
+
+ /**
+ * Property name ({@value} ) for selecting {@code InheritableThreadLocal} (value "true") or plain
+ * {@code ThreadLocal} (value is not "true") in the implementation.
+ */
+ public static final String INHERITABLE_MAP = "isThreadContextMapInheritable";
+
+ /**
+ * The default initial capacity.
+ */
+ protected static final int DEFAULT_INITIAL_CAPACITY = 16;
+
+ /**
+ * System property name that can be used to control the data structure's initial capacity.
+ */
+ protected static final String PROPERTY_NAME_INITIAL_CAPACITY = "log4j2.ThreadContext.initial.capacity";
protected final ThreadLocal<StringMap> localMap;
- protected final int initialCapacity;
+ private final int initialCapacity;
public GarbageFreeSortedArrayThreadContextMap() {
this(
- PropertiesUtil.getProperties().getBooleanProperty(THREAD_CONTEXT_MAP_INHERITABLE),
+ PropertiesUtil.getProperties().getBooleanProperty(INHERITABLE_MAP),
PropertiesUtil.getProperties()
- .getIntegerProperty(THREAD_CONTEXT_INITIAL_CAPACITY, THREAD_CONTEXT_DEFAULT_INITIAL_CAPACITY));
+ .getIntegerProperty(PROPERTY_NAME_INITIAL_CAPACITY, DEFAULT_INITIAL_CAPACITY));
}
GarbageFreeSortedArrayThreadContextMap(final boolean inheritableMap, final int initialCapacity) {
@@ -59,7 +71,7 @@ class GarbageFreeSortedArrayThreadContextMap implements ReadOnlyThreadContextMap
// (This method is package protected for JUnit tests.)
private ThreadLocal<StringMap> createThreadLocalMap(final boolean inheritableMap) {
if (inheritableMap) {
- return new InheritableThreadLocal<>() {
+ return new InheritableThreadLocal<StringMap>() {
@Override
protected StringMap childValue(final StringMap parentValue) {
return parentValue != null ? createStringMap(parentValue) : null;
@@ -78,18 +90,6 @@ class GarbageFreeSortedArrayThreadContextMap implements ReadOnlyThreadContextMap
* @return an implementation of the {@code StringMap} used to back this thread context map
*/
protected StringMap createStringMap() {
- return createStringMap(initialCapacity);
- }
-
- /**
- * Returns an implementation of the {@code StringMap} used to back this thread context map.
- * <p>
- * Subclasses may override.
- * </p>
- * @return an implementation of the {@code StringMap} used to back this thread context map
- * @param initialCapacity initial capacity of the StringMap
- */
- protected StringMap createStringMap(final int initialCapacity) {
return new SortedArrayStringMap(initialCapacity);
}
@@ -149,7 +149,7 @@ class GarbageFreeSortedArrayThreadContextMap implements ReadOnlyThreadContextMap
@Override
public String get(final String key) {
- return getValue(key);
+ return (String) getValue(key);
}
@Override
diff --git a/log4j-api/src/main/java/org/apache/logging/log4j/spi/ObjectThreadContextMap.java b/log4j-api/src/main/java/org/apache/logging/log4j/spi/LocationAwareLogger.java
similarity index 64%
copy from log4j-api/src/main/java/org/apache/logging/log4j/spi/ObjectThreadContextMap.java
copy to log4j-api/src/main/java/org/apache/logging/log4j/spi/LocationAwareLogger.java
index beda028b4c..dd6950eed1 100644
--- a/log4j-api/src/main/java/org/apache/logging/log4j/spi/ObjectThreadContextMap.java
+++ b/log4j-api/src/main/java/org/apache/logging/log4j/spi/LocationAwareLogger.java
@@ -16,13 +16,19 @@
*/
package org.apache.logging.log4j.spi;
+import org.apache.logging.log4j.Level;
+import org.apache.logging.log4j.Marker;
+import org.apache.logging.log4j.message.Message;
+
/**
- * Legacy interface for backward compatibility with extensions to ThreadContextMap.
- * These methods have since been moved to default methods on ThreadContextMap.
- *
- * @see ThreadContextMap
- * @since 2.8
- * @deprecated use {@link ThreadContextMap} directly
+ * Logger that accepts the location of the caller.
*/
-@Deprecated(forRemoval = true, since = "3.0")
-public interface ObjectThreadContextMap extends CleanableThreadContextMap {}
+public interface LocationAwareLogger {
+ void logMessage(
+ final Level level,
+ final Marker marker,
+ final String fqcn,
+ final StackTraceElement location,
+ final Message message,
+ final Throwable throwable);
+}
diff --git a/log4j-api/src/main/java/org/apache/logging/log4j/spi/LoggingSystem.java b/log4j-api/src/main/java/org/apache/logging/log4j/spi/LoggingSystem.java
index 02ad1242e7..e543936a60 100644
--- a/log4j-api/src/main/java/org/apache/logging/log4j/spi/LoggingSystem.java
+++ b/log4j-api/src/main/java/org/apache/logging/log4j/spi/LoggingSystem.java
@@ -70,7 +70,7 @@ public class LoggingSystem {
private static final String PROVIDER_RESOURCE = "META-INF/log4j-provider.properties";
private static final String API_VERSION = "Log4jAPIVersion";
- private static final String[] COMPATIBLE_API_VERSIONS = {"3.0.0"};
+ private static final String[] COMPATIBLE_API_VERSIONS = {"2.6.0", "3.0.0"};
public static final int THREAD_CONTEXT_DEFAULT_INITIAL_CAPACITY = 16;
@@ -202,7 +202,7 @@ public class LoggingSystem {
}
private static List<Provider> loadLegacyProviders() {
- return LoaderUtil.findUrlResources(PROVIDER_RESOURCE).stream()
+ return LoaderUtil.findUrlResources(PROVIDER_RESOURCE, false).stream()
.map(urlResource -> loadLegacyProvider(urlResource.getUrl(), urlResource.getClassLoader()))
.filter(Objects::nonNull)
.collect(Collectors.toList());
diff --git a/log4j-api/src/main/java/org/apache/logging/log4j/spi/MessageFactory2Adapter.java b/log4j-api/src/main/java/org/apache/logging/log4j/spi/MessageFactory2Adapter.java
new file mode 100644
index 0000000000..1e0f4162c0
--- /dev/null
+++ b/log4j-api/src/main/java/org/apache/logging/log4j/spi/MessageFactory2Adapter.java
@@ -0,0 +1,157 @@
+/*
+ * 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.Objects;
+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.SimpleMessage;
+
+/**
+ * Adapts a legacy MessageFactory to the new MessageFactory2 interface.
+ *
+ * @since 2.6
+ */
+public class MessageFactory2Adapter implements MessageFactory2 {
+ private final MessageFactory wrapped;
+
+ public MessageFactory2Adapter(final MessageFactory wrapped) {
+ this.wrapped = Objects.requireNonNull(wrapped);
+ }
+
+ public MessageFactory getOriginal() {
+ return wrapped;
+ }
+
+ @Override
+ public Message newMessage(final CharSequence charSequence) {
+ return new SimpleMessage(charSequence);
+ }
+
+ @Override
+ public Message newMessage(final String message, final Object p0) {
+ return wrapped.newMessage(message, p0);
+ }
+
+ @Override
+ public Message newMessage(final String message, final Object p0, final Object p1) {
+ return wrapped.newMessage(message, p0, p1);
+ }
+
+ @Override
+ public Message newMessage(final String message, final Object p0, final Object p1, final Object p2) {
+ return wrapped.newMessage(message, p0, p1, p2);
+ }
+
+ @Override
+ public Message newMessage(
+ final String message, final Object p0, final Object p1, final Object p2, final Object p3) {
+ return wrapped.newMessage(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 wrapped.newMessage(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 wrapped.newMessage(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 wrapped.newMessage(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 wrapped.newMessage(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 wrapped.newMessage(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 wrapped.newMessage(message, p0, p1, p2, p3, p4, p5, p6, p7, p8, p9);
+ }
+
+ @Override
+ public Message newMessage(final Object message) {
+ return wrapped.newMessage(message);
+ }
+
+ @Override
+ public Message newMessage(final String message) {
+ return wrapped.newMessage(message);
+ }
+
+ @Override
+ public Message newMessage(final String message, final Object... params) {
+ return wrapped.newMessage(message, params);
+ }
+}
diff --git a/log4j-api/src/main/java/org/apache/logging/log4j/spi/NoOpThreadContextMap.java b/log4j-api/src/main/java/org/apache/logging/log4j/spi/NoOpThreadContextMap.java
index 3ff4090eae..155baad10a 100644
--- a/log4j-api/src/main/java/org/apache/logging/log4j/spi/NoOpThreadContextMap.java
+++ b/log4j-api/src/main/java/org/apache/logging/log4j/spi/NoOpThreadContextMap.java
@@ -20,9 +20,8 @@ import java.util.HashMap;
import java.util.Map;
/**
- * {@code ThreadContextMap} implementation used when either of system properties
- * {@link LoggingSystemProperty#THREAD_CONTEXT_MAP_ENABLED} or
- * {@link LoggingSystemProperty#THREAD_CONTEXT_ENABLE} is {@code false}. This implementation does nothing.
+ * {@code ThreadContextMap} implementation used when either of system properties {@code disableThreadContextMap} or .
+ * {@code disableThreadContext} is {@code true}. This implementation does nothing.
*
* @since 2.7
*/
@@ -60,21 +59,4 @@ public class NoOpThreadContextMap implements ThreadContextMap {
@Override
public void remove(final String key) {}
-
- @Override
- public void putAll(final Map<String, String> map) {}
-
- @Override
- public void removeAll(final Iterable<String> keys) {}
-
- @Override
- public <V> V getValue(final String key) {
- return null;
- }
-
- @Override
- public <V> void putValue(final String key, final V value) {}
-
- @Override
- public <V> void putAllValues(final Map<String, V> values) {}
}
diff --git a/log4j-api/src/main/java/org/apache/logging/log4j/spi/ObjectThreadContextMap.java b/log4j-api/src/main/java/org/apache/logging/log4j/spi/ObjectThreadContextMap.java
index beda028b4c..5e312453fb 100644
--- a/log4j-api/src/main/java/org/apache/logging/log4j/spi/ObjectThreadContextMap.java
+++ b/log4j-api/src/main/java/org/apache/logging/log4j/spi/ObjectThreadContextMap.java
@@ -16,13 +16,38 @@
*/
package org.apache.logging.log4j.spi;
+import java.util.Map;
+
/**
- * Legacy interface for backward compatibility with extensions to ThreadContextMap.
- * These methods have since been moved to default methods on ThreadContextMap.
+ * Extension service provider interface to allow putting Object values in the
+ * {@link org.apache.logging.log4j.ThreadContext}.
*
* @see ThreadContextMap
* @since 2.8
- * @deprecated use {@link ThreadContextMap} directly
*/
-@Deprecated(forRemoval = true, since = "3.0")
-public interface ObjectThreadContextMap extends CleanableThreadContextMap {}
+public interface ObjectThreadContextMap extends CleanableThreadContextMap {
+
+ /**
+ * Returns the Object value for the specified key, or {@code null} if the specified key does not exist in this
+ * collection.
+ *
+ * @param key the key whose value to return
+ * @return the value for the specified key or {@code null}
+ */
+ <V> V getValue(String key);
+
+ /**
+ * Puts the specified key-value pair into the collection.
+ *
+ * @param key the key to add or remove. Keys may be {@code null}.
+ * @param value the value to add. Values may be {@code null}.
+ */
+ <V> void putValue(String key, V value);
+
+ /**
+ * Puts all given key-value pairs into the collection.
+ *
+ * @param values the map of key-value pairs to add
+ */
+ <V> void putAllValues(Map<String, V> values);
+}
diff --git a/log4j-api/src/main/java/org/apache/logging/log4j/spi/ReadOnlyThreadContextMap.java b/log4j-api/src/main/java/org/apache/logging/log4j/spi/ReadOnlyThreadContextMap.java
index aafbacfeb8..de9ae028aa 100644
--- a/log4j-api/src/main/java/org/apache/logging/log4j/spi/ReadOnlyThreadContextMap.java
+++ b/log4j-api/src/main/java/org/apache/logging/log4j/spi/ReadOnlyThreadContextMap.java
@@ -25,9 +25,8 @@ import org.apache.logging.log4j.util.StringMap;
* <p>
* {@code ThreadContextMap} implementations that also implement this interface can be accessed
* by applications via the {@link ThreadContext#getThreadContextMap()} method.
- * <p>
- * <strong>Warning:</strong> part of the API, not the SPI, since it is used by {@link ThreadContext#getThreadContextMap()}.
* </p>
+ *
* @see ThreadContext#getThreadContextMap()
* @since 2.8
*/
diff --git a/log4j-api/src/main/java/org/apache/logging/log4j/spi/ThreadContextMap.java b/log4j-api/src/main/java/org/apache/logging/log4j/spi/ThreadContextMap.java
index e958827d6d..f0af90ddbb 100644
--- a/log4j-api/src/main/java/org/apache/logging/log4j/spi/ThreadContextMap.java
+++ b/log4j-api/src/main/java/org/apache/logging/log4j/spi/ThreadContextMap.java
@@ -18,9 +18,6 @@ package org.apache.logging.log4j.spi;
import java.util.Map;
import org.apache.logging.log4j.ThreadContext;
-import org.apache.logging.log4j.util.Cast;
-import org.apache.logging.log4j.util.SortedArrayStringMap;
-import org.apache.logging.log4j.util.StringMap;
/**
* Service provider interface to implement custom MDC behavior for {@link org.apache.logging.log4j.ThreadContext}.
@@ -28,9 +25,6 @@ import org.apache.logging.log4j.util.StringMap;
* Since 2.8, {@code ThreadContextMap} implementations that implement the {@link ReadOnlyThreadContextMap} interface
* are accessible to applications via the {@link ThreadContext#getThreadContextMap()} method.
* </p>
- * <p>
- * Since 3.0.0, {@code ThreadContextMap} combines all its extension interfaces into default methods on this interface.
- * </p>
*/
public interface ThreadContextMap {
@@ -91,85 +85,4 @@ public interface ThreadContextMap {
* @param key The key to remove.
*/
void remove(final String key);
-
- /**
- * Puts all given context map entries into the current thread's
- * context map.
- *
- * <p>If the current thread does not have a context map it is
- * created as a side effect.</p>
- * @param map The map.
- * @since 3.0.0
- */
- default void putAll(Map<String, String> map) {
- map.forEach(this::put);
- }
-
- /**
- * Returns the context data for reading. Note that regardless of whether the returned context data has been
- * {@linkplain StringMap#freeze() frozen} (made read-only) or not, callers should not attempt to modify
- * the returned data structure.
- *
- * @return the {@code StringMap}
- * @since 3.0.0
- */
- default StringMap getReadOnlyContextData() {
- if (this instanceof ReadOnlyThreadContextMap) {
- return ((ReadOnlyThreadContextMap) this).getReadOnlyContextData();
- }
- final Map<String, String> copy = getCopy();
- final StringMap map = new SortedArrayStringMap(copy.size());
- copy.forEach(map::putValue);
- map.freeze();
- return map;
- }
-
- /**
- * Removes all given context map keys from the current thread's context map.
- *
- * <p>If the current thread does not have a context map it is
- * created as a side effect.</p>
- *
- * @param keys The keys.
- * @since 3.0.0
- */
- default void removeAll(Iterable<String> keys) {
- keys.forEach(this::remove);
- }
-
- /**
- * Returns the Object value for the specified key, or {@code null} if the specified key does not exist in this
- * collection.
- *
- * @param key the key whose value to return
- * @param <V> The type of the returned value.
- * @return the value for the specified key or {@code null}
- * @since 3.0.0
- */
- default <V> V getValue(String key) {
- return Cast.cast(get(key));
- }
-
- /**
- * Puts the specified key-value pair into the collection.
- *
- * @param key the key to add or remove. Keys may be {@code null}.
- * @param <V> The type of the stored and returned value.
- * @param value the value to add. Values may be {@code null}.
- * @since 3.0.0
- */
- default <V> void putValue(String key, V value) {
- put(key, value != null ? value.toString() : null);
- }
-
- /**
- * Puts all given key-value pairs into the collection.
- *
- * @param values the map of key-value pairs to add
- * @param <V> The type of the value being added.
- * @since 3.0.0
- */
- default <V> void putAllValues(Map<String, V> values) {
- values.forEach(this::putValue);
- }
}
diff --git a/log4j-api/src/main/java/org/apache/logging/log4j/spi/ThreadContextMap2.java b/log4j-api/src/main/java/org/apache/logging/log4j/spi/ThreadContextMap2.java
index 35841feb4a..ccd7f86b67 100644
--- a/log4j-api/src/main/java/org/apache/logging/log4j/spi/ThreadContextMap2.java
+++ b/log4j-api/src/main/java/org/apache/logging/log4j/spi/ThreadContextMap2.java
@@ -16,13 +16,37 @@
*/
package org.apache.logging.log4j.spi;
+import java.util.Map;
+import org.apache.logging.log4j.util.StringMap;
+
/**
- * Legacy interface for backward compatibility with extensions to ThreadContextMap.
- * These methods have since been moved to default methods on ThreadContextMap.
+ * Extension service provider interface to implement additional custom MDC behavior for
+ * {@link org.apache.logging.log4j.ThreadContext}.
+ *
+ * Consider implementing {@link CleanableThreadContextMap} instead.
*
* @see ThreadContextMap
* @since 2.7
- * @deprecated use {@link ThreadContextMap} directly
*/
-@Deprecated(forRemoval = true, since = "3.0")
-public interface ThreadContextMap2 extends ThreadContextMap {}
+public interface ThreadContextMap2 extends ThreadContextMap {
+
+ /**
+ * Puts all given context map entries into the current thread's
+ * context map.
+ *
+ * <p>If the current thread does not have a context map it is
+ * created as a side effect.</p>
+ * @param map The map.
+ * @since 2.7
+ */
+ void putAll(final Map<String, String> map);
+
+ /**
+ * Returns the context data for reading. Note that regardless of whether the returned context data has been
+ * {@linkplain StringMap#freeze() frozen} (made read-only) or not, callers should not attempt to modify
+ * the returned data structure.
+ *
+ * @return the {@code StringMap}
+ */
+ StringMap getReadOnlyContextData();
+}
diff --git a/log4j-api/src/main/java/org/apache/logging/log4j/spi/ThreadContextMapFactory.java b/log4j-api/src/main/java/org/apache/logging/log4j/spi/ThreadContextMapFactory.java
new file mode 100644
index 0000000000..970e757c70
--- /dev/null
+++ b/log4j-api/src/main/java/org/apache/logging/log4j/spi/ThreadContextMapFactory.java
@@ -0,0 +1,146 @@
+/*
+ * 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 org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.apache.logging.log4j.ThreadContext;
+import org.apache.logging.log4j.status.StatusLogger;
+import org.apache.logging.log4j.util.Constants;
+import org.apache.logging.log4j.util.LoaderUtil;
+import org.apache.logging.log4j.util.PropertiesUtil;
+import org.apache.logging.log4j.util.ProviderUtil;
+
+/**
+ * Creates the ThreadContextMap instance used by the ThreadContext.
+ * <p>
+ * If {@link Constants#ENABLE_THREADLOCALS Log4j can use ThreadLocals}, a garbage-free StringMap-based context map can
+ * be installed by setting system property {@code log4j2.garbagefree.threadContextMap} to {@code true}.
+ * </p><p>
+ * Furthermore, any custom {@code ThreadContextMap} can be installed by setting system property
+ * {@code log4j2.threadContextMap} to the fully qualified class name of the class implementing the
+ * {@code ThreadContextMap} interface. (Also implement the {@code ReadOnlyThreadContextMap} interface if your custom
+ * {@code ThreadContextMap} implementation should be accessible to applications via the
+ * {@link ThreadContext#getThreadContextMap()} method.)
+ * </p><p>
+ * Instead of system properties, the above can also be specified in a properties file named
+ * {@code log4j2.component.properties} in the classpath.
+ * </p>
+ *
+ * @see ThreadContextMap
+ * @see ReadOnlyThreadContextMap
+ * @see org.apache.logging.log4j.ThreadContext
+ * @since 2.7
+ */
+public final class ThreadContextMapFactory {
+ private static final Logger LOGGER = StatusLogger.getLogger();
+ private static final String THREAD_CONTEXT_KEY = "log4j2.threadContextMap";
+ private static final String GC_FREE_THREAD_CONTEXT_KEY = "log4j2.garbagefree.threadContextMap";
+
+ private static boolean GcFreeThreadContextKey;
+ private static String ThreadContextMapName;
+ private static final String GARBAGE_FREE_CONTEXT_MAP =
+ "org.apache.logging.log4j.spi" + ".GarbageFreeSortedArrayThreadContextMap";
+ private static final String COPY_ON_WRITE_CONTEXT_MAP =
+ "org.apache.logging.log4j.spi" + ".CopyOnWriteSortedArrayThreadContextMap";
+
+ static {
+ initPrivate();
+ }
+
+ /**
+ * Initializes static variables based on system properties. Normally called when this class is initialized by the VM
+ * and when Log4j is reconfigured.
+ */
+ public static void init() {
+ DefaultThreadContextMap.init();
+ initPrivate();
+ }
+
+ /**
+ * Initializes static variables based on system properties. Normally called when this class is initialized by the VM
+ * and when Log4j is reconfigured.
+ */
+ private static void initPrivate() {
+ final PropertiesUtil properties = PropertiesUtil.getProperties();
+ ThreadContextMapName = properties.getStringProperty(THREAD_CONTEXT_KEY);
+ GcFreeThreadContextKey = properties.getBooleanProperty(GC_FREE_THREAD_CONTEXT_KEY);
+ }
+
+ private ThreadContextMapFactory() {}
+
+ public static ThreadContextMap createThreadContextMap() {
+ final ClassLoader cl = ProviderUtil.findClassLoader();
+ ThreadContextMap result = null;
+ if (ThreadContextMapName != null) {
+ /*
+ * Two implementation are package-private classes, so we instantiate them directly.
+ * Other implementation must be publicly accessible (through `LoaderUtil`).
+ */
+ switch (ThreadContextMapName) {
+ case GARBAGE_FREE_CONTEXT_MAP:
+ result = new GarbageFreeSortedArrayThreadContextMap();
+ break;
+ case COPY_ON_WRITE_CONTEXT_MAP:
+ result = new CopyOnWriteSortedArrayThreadContextMap();
+ break;
+ default:
+ try {
+ result = LoaderUtil.newCheckedInstanceOf(ThreadContextMapName, ThreadContextMap.class);
+ } catch (final ClassNotFoundException cnfe) {
+ LOGGER.error("Unable to locate configured ThreadContextMap {}", ThreadContextMapName);
+ } catch (final Exception ex) {
+ LOGGER.error("Unable to create configured ThreadContextMap {}", ThreadContextMapName, ex);
+ }
+ }
+ }
+ if (result == null && ProviderUtil.hasProviders() && LogManager.getFactory() != null) { // LOG4J2-1658
+ final String factoryClassName = LogManager.getFactory().getClass().getName();
+ for (final Provider provider : ProviderUtil.getProviders()) {
+ if (factoryClassName.equals(provider.getClassName())) {
+ final Class<? extends ThreadContextMap> clazz = provider.loadThreadContextMap();
+ if (clazz != null) {
+ try {
+ result = LoaderUtil.newInstanceOf(clazz);
+ break;
+ } catch (final Exception e) {
+ LOGGER.error(
+ "Unable to locate or load configured ThreadContextMap {}",
+ provider.getThreadContextMap(),
+ e);
+ result = createDefaultThreadContextMap();
+ }
+ }
+ }
+ }
+ }
+ if (result == null) {
+ result = createDefaultThreadContextMap();
+ }
+ return result;
+ }
+
+ private static ThreadContextMap createDefaultThreadContextMap() {
+ if (Constants.ENABLE_THREADLOCALS) {
+ if (GcFreeThreadContextKey) {
+ return new GarbageFreeSortedArrayThreadContextMap();
+ }
+ return new CopyOnWriteSortedArrayThreadContextMap();
+ }
+ return new DefaultThreadContextMap(true);
+ }
+}
diff --git a/log4j-api/src/main/java/org/apache/logging/log4j/util/Constants.java b/log4j-api/src/main/java/org/apache/logging/log4j/util/Constants.java
index 78fd2eccc1..a354c48b37 100644
--- a/log4j-api/src/main/java/org/apache/logging/log4j/util/Constants.java
+++ b/log4j-api/src/main/java/org/apache/logging/log4j/util/Constants.java
@@ -23,7 +23,6 @@ import org.apache.logging.log4j.spi.LoggingSystemProperty;
*
* @since 2.6.2
*/
-@InternalApi
public final class Constants {
private static final LazyBoolean isWebApp = new LazyBoolean(() -> PropertiesUtil.getProperties()
@@ -37,6 +36,11 @@ public final class Constants {
* "log4j2.is.webapp", or (if this system property is not set) whether the {@code javax.servlet.Servlet} class
* is present in the classpath.
*/
+ public static final boolean IS_WEB_APP = PropertiesUtil.getProperties()
+ .getBooleanProperty(
+ "log4j2.is.webapp",
+ isClassAvailable("javax.servlet.Servlet") || isClassAvailable("jakarta.servlet.Servlet"));
+
public static boolean isWebApp() {
return isWebApp.getAsBoolean();
}
@@ -49,6 +53,19 @@ public final class Constants {
isWebApp.reset();
}
+ /**
+ * Kill switch for object pooling in ThreadLocals that enables much of the LOG4J2-1270 no-GC behaviour.
+ * <p>
+ * {@code True} for non-{@link #IS_WEB_APP web apps}, disable by setting system property
+ * "log4j2.enable.threadlocals" to "false".
+ * </p>
+ */
+ public static final boolean ENABLE_THREADLOCALS =
+ !IS_WEB_APP && PropertiesUtil.getProperties().getBooleanProperty("log4j2.enable.threadlocals", true);
+
+ /**
+ * Java major version.
+ */
public static final int JAVA_MAJOR_VERSION = getMajorVersion();
/**
@@ -56,17 +73,41 @@ public final class Constants {
* After a large message has been delivered to the appenders, the StringBuilder is trimmed to this size.
* <p>
* The default value is 518, which allows the StringBuilder to resize three times from its initial size.
- * Users can override with system property {@link LoggingSystemProperty#GC_REUSABLE_MESSAGE_MAX_SIZE}.
+ * Users can override with system property "log4j.maxReusableMsgSize".
* </p>
* @since 2.9
*/
- public static final int MAX_REUSABLE_MESSAGE_SIZE =
- size(LoggingSystemProperty.GC_REUSABLE_MESSAGE_MAX_SIZE, (128 * 2 + 2) * 2 + 2);
+ public static final int MAX_REUSABLE_MESSAGE_SIZE = size("log4j.maxReusableMsgSize", (128 * 2 + 2) * 2 + 2);
+
+ /**
+ * Name of the system property that will turn on TRACE level internal log4j2 status logging.
+ * <p>
+ * If system property {@value} is either defined empty or its value equals to {@code true} (ignoring case), all internal log4j2 logging will be
+ * printed to the console. The presence of this system property overrides any value set in the configuration's
+ * {@code <Configuration status="<level>" ...>} status attribute, as well as any value set for
+ * system property {@code org.apache.logging.log4j.simplelog.StatusLogger.level}.
+ * </p>
+ */
+ public static final String LOG4J2_DEBUG = "log4j2.debug";
- private static int size(final PropertyKey property, final int defaultValue) {
+ private static int size(final String property, final int defaultValue) {
return PropertiesUtil.getProperties().getIntegerProperty(property, defaultValue);
}
+ /**
+ * Determines if a named Class can be loaded or not.
+ *
+ * @param className The class name.
+ * @return {@code true} if the class could be found or {@code false} otherwise.
+ */
+ private static boolean isClassAvailable(final String className) {
+ try {
+ return LoaderUtil.loadClass(className) != null;
+ } catch (final Throwable e) {
+ return false;
+ }
+ }
+
/**
* The empty array.
*/
diff --git a/log4j-api/src/main/java/org/apache/logging/log4j/util/LoaderUtil.java b/log4j-api/src/main/java/org/apache/logging/log4j/util/LoaderUtil.java
index fd834b17a4..027bcee7a0 100644
--- a/log4j-api/src/main/java/org/apache/logging/log4j/util/LoaderUtil.java
+++ b/log4j-api/src/main/java/org/apache/logging/log4j/util/LoaderUtil.java
@@ -20,12 +20,13 @@ import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.net.URL;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
import java.util.Collection;
import java.util.Enumeration;
import java.util.LinkedHashSet;
import java.util.Objects;
import java.util.function.Supplier;
-import org.apache.logging.log4j.spi.LoggingSystemProperty;
/**
* <em>Consider this class private.</em> Utility class for ClassLoaders.
@@ -40,17 +41,43 @@ public final class LoaderUtil {
private static final ClassLoader[] EMPTY_CLASS_LOADER_ARRAY = {};
+ /**
+ * System property to set to ignore the thread context ClassLoader.
+ *
+ * @since 2.1
+ */
+ public static final String IGNORE_TCCL_PROPERTY = "log4j.ignoreTCL";
+
// this variable must be lazily loaded; otherwise, we get a nice circular class loading problem where LoaderUtil
// wants to use PropertiesUtil, but then PropertiesUtil wants to use LoaderUtil.
private static Boolean ignoreTCCL;
static Boolean forceTcclOnly;
- static {
- if (System.getSecurityManager() != null) {
- LowLevelLogUtil.log(
- "A custom SecurityManager was detected; Log4j no longer supports security permissions.");
+ static final RuntimePermission GET_CLASS_LOADER = new RuntimePermission("getClassLoader");
+ static final LazyBoolean GET_CLASS_LOADER_DISABLED = new LazyBoolean(() -> {
+ if (System.getSecurityManager() == null) {
+ return false;
}
- }
+ try {
+ AccessController.checkPermission(GET_CLASS_LOADER);
+ // seems like we'll be ok
+ return false;
+ } catch (final SecurityException ignored) {
+ try {
+ // let's see if we can obtain that permission
+ AccessController.doPrivileged((PrivilegedAction<Void>) () -> {
+ AccessController.checkPermission(GET_CLASS_LOADER);
+ return null;
+ });
+ return false;
+ } catch (final SecurityException ignore) {
+ // no chance
+ return true;
+ }
+ }
+ });
+
+ private static final PrivilegedAction<ClassLoader> TCCL_GETTER = new ThreadContextClassLoaderGetter();
private LoaderUtil() {}
@@ -65,13 +92,18 @@ public final class LoaderUtil {
// TODO: this method could use some explanation
public static ClassLoader getClassLoader(final Class<?> class1, final Class<?> class2) {
- final ClassLoader loader1 = class1 == null ? null : class1.getClassLoader();
- final ClassLoader loader2 = class2 == null ? null : class2.getClassLoader();
- final ClassLoader referenceLoader = Thread.currentThread().getContextClassLoader();
- if (isChild(referenceLoader, loader1)) {
- return isChild(referenceLoader, loader2) ? referenceLoader : loader2;
- }
- return isChild(loader1, loader2) ? loader1 : loader2;
+ PrivilegedAction<ClassLoader> action = () -> {
+ final ClassLoader loader1 = class1 == null ? null : class1.getClassLoader();
+ final ClassLoader loader2 = class2 == null ? null : class2.getClassLoader();
+ final ClassLoader referenceLoader = GET_CLASS_LOADER_DISABLED.getAsBoolean()
+ ? getThisClassLoader()
+ : Thread.currentThread().getContextClassLoader();
+ if (isChild(referenceLoader, loader1)) {
+ return isChild(referenceLoader, loader2) ? referenceLoader : loader2;
+ }
+ return isChild(loader1, loader2) ? loader1 : loader2;
+ };
+ return runPrivileged(action);
}
/**
@@ -95,22 +127,45 @@ public final class LoaderUtil {
}
/**
- * Looks up the ClassLoader for this current thread falling back to either the
+ * Looks up the ClassLoader for this current thread. If this class does not have the runtime permission
+ * {@code getClassLoader}, then the only ClassLoader this attempts to look up is the loader behind this
+ * class. When a SecurityManager is installed, this attempts to make a privileged call to get the current
+ * {@linkplain Thread#getContextClassLoader() thread context ClassLoader}, falling back to either the
* ClassLoader of this class or the {@linkplain ClassLoader#getSystemClassLoader() system ClassLoader}.
+ * When no SecurityManager is present, the same lookups are performed without use of {@link AccessController}.
* If none of these strategies can obtain a ClassLoader, then this returns {@code null}.
*
* @return the current thread's ClassLoader, a fallback loader, or null if no fallback can be determined
*/
public static ClassLoader getThreadContextClassLoader() {
- final ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
- if (contextClassLoader != null) {
- return contextClassLoader;
+ try {
+ return GET_CLASS_LOADER_DISABLED.getAsBoolean() ? getThisClassLoader() : runPrivileged(TCCL_GETTER);
+ } catch (final SecurityException ignored) {
+ return null;
}
- final ClassLoader thisClassLoader = getThisClassLoader();
- if (thisClassLoader != null) {
- return thisClassLoader;
+ }
+
+ private static ClassLoader getThisClassLoader() {
+ return LoaderUtil.class.getClassLoader();
+ }
+
+ private static <T> T runPrivileged(final PrivilegedAction<T> action) {
+ return System.getSecurityManager() != null ? AccessController.doPrivileged(action) : action.run();
+ }
+
+ private static class ThreadContextClassLoaderGetter implements PrivilegedAction<ClassLoader> {
+ @Override
+ public ClassLoader run() {
+ final ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
+ if (contextClassLoader != null) {
+ return contextClassLoader;
+ }
+ final ClassLoader thisClassLoader = getThisClassLoader();
+ if (thisClassLoader != null || GET_CLASS_LOADER_DISABLED.getAsBoolean()) {
+ return thisClassLoader;
+ }
+ return ClassLoader.getSystemClassLoader();
}
- return ClassLoader.getSystemClassLoader();
}
public static ClassLoader[] getClassLoaders() {
@@ -140,10 +195,6 @@ public final class LoaderUtil {
return classLoaders.toArray(EMPTY_CLASS_LOADER_ARRAY);
}
- private static ClassLoader getThisClassLoader() {
- return LoaderUtil.class.getClassLoader();
- }
-
private static void accumulateLayerClassLoaders(
final ModuleLayer layer, final Collection<ClassLoader> classLoaders) {
for (final Module module : layer.modules()) {
@@ -157,8 +208,8 @@ public final class LoaderUtil {
}
/**
- * Adds the provided loader to the loaders collection, and traverses up the tree until either a null
- * value or a classloader which has already been added is encountered.
+ * Adds the provided loader to the loaders collection, and traverses up the tree until either a null value or a
+ * classloader which has already been added is encountered.
*/
private static void accumulateClassLoaders(final ClassLoader loader, final Collection<ClassLoader> loaders) {
// Some implementations may use null to represent the bootstrap class loader.
@@ -180,16 +231,15 @@ public final class LoaderUtil {
return true;
} catch (final ClassNotFoundException | LinkageError e) {
return false;
- } catch (final Throwable e) {
- LowLevelLogUtil.logException("Unknown error checking for existence of class: " + className, e);
+ } catch (final Throwable error) {
+ // LOGGER.error("Unknown error while checking existence of class `{}`", className, error);
return false;
}
}
/**
- * Loads and initializes a class given its fully qualified class name. This method respects the
- * {@link LoggingSystemProperty#LOADER_IGNORE_THREAD_CONTEXT_LOADER} Log4j property. If this property is specified
- * and set to anything besides {@code false}, then this class's ClassLoader will be used.
+ * Loads a class by name. This method respects the {@link #IGNORE_TCCL_PROPERTY} Log4j property. If this property is
+ * specified and set to anything besides {@code false}, then the default ClassLoader will be used.
*
* @param className fully qualified class name to load
* @return the loaded class
@@ -307,6 +357,58 @@ public final class LoaderUtil {
return newInstanceOf(clazz);
}
+ /**
+ * Loads and instantiates a class given by a property name.
+ *
+ * @param propertyName The property name to look up a class name for.
+ * @param clazz The class to cast it to.
+ * @param <T> The type to cast it to.
+ * @return new instance of the class given in the property or {@code null} if the property was unset.
+ * @throws ClassNotFoundException if the class isn't available to the usual ClassLoaders
+ * @throws ExceptionInInitializerError if an exception was thrown while initializing the class
+ * @throws LinkageError if the linkage of the class fails for any other reason
+ * @throws ClassCastException if the class is not compatible with the generic type parameter provided
+ * @throws NoSuchMethodException if no zero-arg constructor exists
+ * @throws SecurityException if this class is not allowed to access declared members of the provided class
+ * @throws IllegalAccessException if the class can't be instantiated through a public constructor
+ * @throws InstantiationException if the provided class is abstract or an interface
+ * @throws InvocationTargetException if an exception is thrown by the constructor
+ * @since 2.5
+ */
+ public static <T> T newCheckedInstanceOfProperty(final String propertyName, final Class<T> clazz)
+ throws ReflectiveOperationException {
+ return newCheckedInstanceOfProperty(propertyName, clazz, () -> null);
+ }
+
+ /**
+ * Loads and instantiates a class given by a property name.
+ *
+ * @param propertyName The property name to look up a class name for.
+ * @param clazz The class to cast it to.
+ * @param defaultSupplier Supplier of a default value if the property is not present.
+ * @param <T> The type to cast it to.
+ * @return new instance of the class given in the property or {@code null} if the property was unset.
+ * @throws ClassNotFoundException if the class isn't available to the usual ClassLoaders
+ * @throws ExceptionInInitializerError if an exception was thrown while initializing the class
+ * @throws LinkageError if the linkage of the class fails for any other reason
+ * @throws ClassCastException if the class is not compatible with the generic type parameter provided
+ * @throws NoSuchMethodException if no zero-arg constructor exists
+ * @throws SecurityException if this class is not allowed to access declared members of the provided class
+ * @throws IllegalAccessException if the class can't be instantiated through a public constructor
+ * @throws InstantiationException if the provided class is abstract or an interface
+ * @throws InvocationTargetException if an exception is thrown by the constructor
+ * @since 2.22
+ */
+ public static <T> T newCheckedInstanceOfProperty(
+ final String propertyName, final Class<T> clazz, final Supplier<T> defaultSupplier)
+ throws ReflectiveOperationException {
+ final String className = PropertiesUtil.getProperties().getStringProperty(propertyName);
+ if (className == null) {
+ return defaultSupplier.get();
+ }
+ return newCheckedInstanceOf(className, clazz);
+ }
+
/**
* Loads and instantiates a class given by a property name.
*
@@ -410,8 +512,7 @@ public final class LoaderUtil {
private static boolean isIgnoreTccl() {
// we need to lazily initialize this, but concurrent access is not an issue
if (ignoreTCCL == null) {
- final String ignoreTccl = PropertiesUtil.getProperties()
- .getStringProperty(LoggingSystemProperty.LOADER_IGNORE_THREAD_CONTEXT_LOADER, null);
+ final String ignoreTccl = PropertiesUtil.getProperties().getStringProperty(IGNORE_TCCL_PROPERTY, null);
ignoreTCCL = ignoreTccl != null && !"false".equalsIgnoreCase(ignoreTccl.trim());
}
return ignoreTCCL;
@@ -420,7 +521,7 @@ public final class LoaderUtil {
private static boolean isForceTccl() {
if (forceTcclOnly == null) {
// PropertiesUtil.getProperties() uses that code path so don't use that!
- forceTcclOnly = Boolean.getBoolean(LoggingSystemProperty.LOADER_FORCE_THREAD_CONTEXT_LOADER.getSystemKey());
+ forceTcclOnly = Boolean.getBoolean("log4j2.Loader.forceTCLOnly");
}
return forceTcclOnly;
}
@@ -433,7 +534,11 @@ public final class LoaderUtil {
* @since 2.1
*/
public static Collection<URL> findResources(final String resource) {
- final Collection<UrlResource> urlResources = findUrlResources(resource);
+ return findResources(resource, true);
+ }
+
+ static Collection<URL> findResources(final String resource, final boolean useTccl) {
+ final Collection<UrlResource> urlResources = findUrlResources(resource, useTccl);
final Collection<URL> resources = new LinkedHashSet<>(urlResources.size());
for (final UrlResource urlResource : urlResources) {
resources.add(urlResource.getUrl());
@@ -441,25 +546,24 @@ public final class LoaderUtil {
return resources;
}
- /**
- * This method will only find resources that follow the JPMS rules for encapsulation. Resources
- * on the class path should be found as normal along with resources with no package name in all
- * modules. Resources within packages in modules must declare those resources open to org.apache.logging.log4j.
- *
- * @param resource The resource to locate.
- * @return The located resources.
- */
- public static Collection<UrlResource> findUrlResources(final String resource) {
+ public static Collection<UrlResource> findUrlResources(final String resource, final boolean useTccl) {
+ // @formatter:off
+ final ClassLoader[] candidates = {
+ useTccl ? getThreadContextClassLoader() : null,
+ LoaderUtil.class.getClassLoader(),
+ GET_CLASS_LOADER_DISABLED.getAsBoolean() ? null : ClassLoader.getSystemClassLoader()
+ };
+ // @formatter:on
final Collection<UrlResource> resources = new LinkedHashSet<>();
- for (final ClassLoader cl : getClassLoaders()) {
+ for (final ClassLoader cl : candidates) {
if (cl != null) {
try {
final Enumeration<URL> resourceEnum = cl.getResources(resource);
while (resourceEnum.hasMoreElements()) {
resources.add(new UrlResource(cl, resourceEnum.nextElement()));
}
- } catch (final IOException e) {
- LowLevelLogUtil.logException(e);
+ } catch (final IOException error) {
+ // LOGGER.error("failed to collect resources of name `{}`", resource, error);
}
}
}
diff --git a/log4j-api/src/main/java/org/apache/logging/log4j/util/MessageSupplier.java b/log4j-api/src/main/java/org/apache/logging/log4j/util/MessageSupplier.java
index 2904668e15..8b13571350 100644
--- a/log4j-api/src/main/java/org/apache/logging/log4j/util/MessageSupplier.java
+++ b/log4j-api/src/main/java/org/apache/logging/log4j/util/MessageSupplier.java
@@ -16,7 +16,6 @@
*/
package org.apache.logging.log4j.util;
-import org.apache.logging.log4j.Marker;
import org.apache.logging.log4j.message.Message;
/**
@@ -27,10 +26,19 @@ import org.apache.logging.log4j.message.Message;
*
* <p>Implementors are free to cache values or return a new or distinct value each time the supplier is invoked.
*
- * <p>
- * <strong>Warning:</strong> part of the public API, since it is used by {@link org.apache.logging.log4j.Logger#debug(Marker, MessageSupplier)}.
+ * <p><strong>DEPRECATED:</strong> this class should not normally be used outside a Java 8+ lambda syntax. Instead,
+ * {@link Supplier Supplier<Message>} should be used as an anonymous class. Both this and {@link Supplier} will be
+ * removed in 3.0.
* </p>
+ *
* @since 2.4
*/
-@Deprecated(forRemoval = true, since = "3.0")
-public interface MessageSupplier extends java.util.function.Supplier<Message> {}
+public interface MessageSupplier {
+
+ /**
+ * Gets a Message.
+ *
+ * @return a Message
+ */
+ Message get();
+}
diff --git a/log4j-api/src/main/java/org/apache/logging/log4j/util/PropertiesUtil.java b/log4j-api/src/main/java/org/apache/logging/log4j/util/PropertiesUtil.java
index dcb1a4fbad..f2239b0108 100644
--- a/log4j-api/src/main/java/org/apache/logging/log4j/util/PropertiesUtil.java
+++ b/log4j-api/src/main/java/org/apache/logging/log4j/util/PropertiesUtil.java
@@ -51,7 +51,7 @@ import org.apache.logging.log4j.internal.CopyOnWriteNavigableSet;
* <p>
* Provides utility methods for managing {@link Properties} instances as well as access to the global configuration
* system. Properties by default are loaded from the system properties, system environment, and a classpath resource
- * file named {@value #LOG4J_SYSTEM_PROPERTIES_FILE_NAME}. Additional properties can be loaded by implementing a custom
+ * file named {@value #LOG4J_PROPERTIES_FILE_NAME}. Additional properties can be loaded by implementing a custom
* {@link PropertySource} service and specifying it via a {@link ServiceLoader} file called
* {@code META-INF/services/org.apache.logging.log4j.util.PropertySource} with a list of fully qualified class names
* implementing that interface.
@@ -63,6 +63,7 @@ import org.apache.logging.log4j.internal.CopyOnWriteNavigableSet;
@ServiceConsumer(value = PropertySource.class, cardinality = Cardinality.MULTIPLE)
public class PropertiesUtil implements PropertyEnvironment {
+ private static final String LOG4J_PROPERTIES_FILE_NAME = "log4j2.component.properties";
private static final String LOG4J_NAMESPACE = "component";
private static final String LOG4J_CONTEXT_PREFIX = "log4j2.context.";
private static final String DELIMITER = ".";
@@ -146,6 +147,31 @@ public class PropertiesUtil implements PropertyEnvironment {
this.environment = new Environment(contextName, sources);
}
+ /**
+ * Loads and closes the given property input stream. If an error occurs, log to the status logger.
+ *
+ * @param in a property input stream.
+ * @param source a source object describing the source, like a resource string or a URL.
+ * @return a new Properties object
+ */
+ static Properties loadClose(final InputStream in, final Object source) {
+ final Properties props = new Properties();
+ if (null != in) {
+ try {
+ props.load(in);
+ } catch (final IOException error) {
+ // LOGGER.error("Unable to read source `{}`", source, error);
+ } finally {
+ try {
+ in.close();
+ } catch (final IOException error) {
+ // LOGGER.error("Unable to close source `{}`", source, error);
+ }
+ }
+ }
+ return props;
+ }
+
public static boolean hasThreadProperties() {
return environments.get() != null;
}
@@ -731,6 +757,21 @@ public class PropertiesUtil implements PropertyEnvironment {
if (literal.containsKey(key)) {
return literal.get(key);
}
+ // This part is temporarily copied from 2.x to prepare
+ // the removal of Log4j API 3.x
+ final List<CharSequence> tokens = PropertySource.Util.tokenize(key);
+ final boolean hasTokens = !tokens.isEmpty();
+ for (final PropertySource source : sources) {
+ if (hasTokens) {
+ final String normalKey = Objects.toString(source.getNormalForm(tokens), null);
+ if (normalKey != null && source.containsProperty(normalKey)) {
+ return source.getProperty(normalKey);
+ }
+ }
+ if (source.containsProperty(key)) {
+ return source.getProperty(key);
+ }
+ }
String result = null;
final String contextKey = getContextKey(key);
if (contextName != null && !contextName.equals(PropertySource.SYSTEM_CONTEXT)) {
diff --git a/log4j-api/src/main/java/org/apache/logging/log4j/util/ProviderActivator.java b/log4j-api/src/main/java/org/apache/logging/log4j/util/ProviderActivator.java
new file mode 100644
index 0000000000..bbb6966485
--- /dev/null
+++ b/log4j-api/src/main/java/org/apache/logging/log4j/util/ProviderActivator.java
@@ -0,0 +1,53 @@
+/*
+ * 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.util;
+
+import java.util.Hashtable;
+import org.apache.logging.log4j.spi.Provider;
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceRegistration;
+
+/**
+ * Utility class to register Log4j2 providers in an OSGI environment.
+ */
+public abstract class ProviderActivator implements BundleActivator {
+
+ public static final String API_VERSION = "APIVersion";
+
+ private final Provider provider;
+ private ServiceRegistration<Provider> providerRegistration = null;
+
+ protected ProviderActivator(final Provider provider) {
+ this.provider = provider;
+ }
+
+ @Override
+ @SuppressWarnings("JdkObsolete")
+ public void start(final BundleContext context) throws Exception {
+ final Hashtable<String, String> props = new Hashtable<>();
+ props.put(API_VERSION, provider.getVersions());
+ providerRegistration = context.registerService(Provider.class, provider, props);
+ }
+
+ @Override
+ public void stop(final BundleContext context) throws Exception {
+ if (providerRegistration != null) {
+ providerRegistration.unregister();
+ }
+ }
+}
diff --git a/log4j-api/src/main/java/org/apache/logging/log4j/util/ProviderUtil.java b/log4j-api/src/main/java/org/apache/logging/log4j/util/ProviderUtil.java
new file mode 100644
index 0000000000..cde96d6356
--- /dev/null
+++ b/log4j-api/src/main/java/org/apache/logging/log4j/util/ProviderUtil.java
@@ -0,0 +1,178 @@
+/*
+ * 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.util;
+
+import aQute.bnd.annotation.Cardinality;
+import aQute.bnd.annotation.Resolution;
+import aQute.bnd.annotation.baseline.BaselineIgnore;
+import aQute.bnd.annotation.spi.ServiceConsumer;
+import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
+import java.io.IOException;
+import java.net.URL;
+import java.util.Collection;
+import java.util.Enumeration;
+import java.util.HashSet;
+import java.util.Properties;
+import java.util.ServiceLoader;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
+import org.apache.logging.log4j.Logger;
+import org.apache.logging.log4j.spi.Provider;
+import org.apache.logging.log4j.status.StatusLogger;
+
+/**
+ * <em>Consider this class private.</em> Utility class for Log4j {@link Provider}s. When integrating with an application
+ * container framework, any Log4j Providers not accessible through standard classpath scanning should
+ * {@link #loadProvider(java.net.URL, ClassLoader)} a classpath accordingly.
+ */
+@InternalApi
+@BaselineIgnore("2.22.0")
+@ServiceConsumer(value = Provider.class, resolution = Resolution.OPTIONAL, cardinality = Cardinality.MULTIPLE)
+public final class ProviderUtil {
+
+ /**
+ * Resource name for a Log4j 2 provider properties file.
+ */
+ static final String PROVIDER_RESOURCE = "META-INF/log4j-provider.properties";
+
+ /**
+ * Loaded providers.
+ */
+ static final Collection<Provider> PROVIDERS = new HashSet<>();
+
+ /**
+ * Guards the ProviderUtil singleton instance from lazy initialization. This is primarily used for OSGi support.
+ *
+ * @since 2.1
+ */
+ static final Lock STARTUP_LOCK = new ReentrantLock();
+
+ private static final String API_VERSION = "Log4jAPIVersion";
+ private static final String[] COMPATIBLE_API_VERSIONS = {"2.6.0"};
+ private static final Logger LOGGER = StatusLogger.getLogger();
+
+ // STARTUP_LOCK guards INSTANCE for lazy initialization; this allows the OSGi Activator to pause the startup and
+ // wait for a Provider to be installed. See LOG4J2-373
+ private static volatile ProviderUtil instance;
+
+ private ProviderUtil() {
+ ServiceLoaderUtil.safeStream(ServiceLoader.load(Provider.class, ProviderUtil.class.getClassLoader()))
+ .filter(provider -> validVersion(provider.getVersions()))
+ .forEach(PROVIDERS::add);
+
+ for (final LoaderUtil.UrlResource resource : LoaderUtil.findUrlResources(PROVIDER_RESOURCE, false)) {
+ loadProvider(resource.getUrl(), resource.getClassLoader());
+ }
+ }
+
+ static void addProvider(final Provider provider) {
+ PROVIDERS.add(provider);
+ LOGGER.debug("Loaded Provider {}", provider);
+ }
+
+ /**
+ * Loads an individual Provider implementation. This method is really only useful for the OSGi bundle activator and
+ * this class itself.
+ *
+ * @param url the URL to the provider properties file
+ * @param cl the ClassLoader to load the provider classes with
+ */
+ @SuppressFBWarnings(
+ value = "URLCONNECTION_SSRF_FD",
+ justification = "Uses a fixed URL that ends in 'META-INF/log4j-provider.properties'.")
+ static void loadProvider(final URL url, final ClassLoader cl) {
+ try {
+ final Properties props = PropertiesUtil.loadClose(url.openStream(), url);
+ if (validVersion(props.getProperty(API_VERSION))) {
+ final Provider provider = new Provider(props, url, cl);
+ PROVIDERS.add(provider);
+ LOGGER.debug("Loaded Provider {}", provider);
+ }
+ } catch (final IOException e) {
+ LOGGER.error("Unable to open {}", url, e);
+ }
+ }
+
+ /**
+ *
+ * @param classLoader null can be used to mark the bootstrap class loader.
+ */
+ static void loadProviders(final ClassLoader classLoader) {
+ ServiceLoaderUtil.safeStream(ServiceLoader.load(Provider.class, classLoader))
+ .filter(provider -> validVersion(provider.getVersions()))
+ .forEach(PROVIDERS::add);
+ }
+
+ /**
+ * @deprecated Use {@link #loadProvider(java.net.URL, ClassLoader)} instead. Will be removed in 3.0.
+ */
+ @Deprecated
+ static void loadProviders(final Enumeration<URL> urls, final ClassLoader cl) {
+ if (urls != null) {
+ while (urls.hasMoreElements()) {
+ loadProvider(urls.nextElement(), cl);
+ }
+ }
+ }
+
+ public static Iterable<Provider> getProviders() {
+ lazyInit();
+ return PROVIDERS;
+ }
+
+ public static boolean hasProviders() {
+ lazyInit();
+ return !PROVIDERS.isEmpty();
+ }
+
+ /**
+ * Lazily initializes the ProviderUtil singleton.
+ *
+ * @since 2.1
+ */
+ static void lazyInit() {
+ // noinspection DoubleCheckedLocking
+ if (instance == null) {
+ try {
+ STARTUP_LOCK.lockInterruptibly();
+ try {
+ if (instance == null) {
+ instance = new ProviderUtil();
+ }
+ } finally {
+ STARTUP_LOCK.unlock();
+ }
+ } catch (final InterruptedException e) {
+ LOGGER.fatal("Interrupted before Log4j Providers could be loaded.", e);
+ Thread.currentThread().interrupt();
+ }
+ }
+ }
+
+ public static ClassLoader findClassLoader() {
+ return LoaderUtil.getThreadContextClassLoader();
+ }
+
+ private static boolean validVersion(final String version) {
+ for (final String v : COMPATIBLE_API_VERSIONS) {
+ if (version.startsWith(v)) {
+ return true;
+ }
+ }
+ return false;
+ }
+}
diff --git a/log4j-api/src/main/java/org/apache/logging/log4j/util/Supplier.java b/log4j-api/src/main/java/org/apache/logging/log4j/util/Supplier.java
index c207f0076e..aaa4d9b89a 100644
--- a/log4j-api/src/main/java/org/apache/logging/log4j/util/Supplier.java
+++ b/log4j-api/src/main/java/org/apache/logging/log4j/util/Supplier.java
@@ -25,11 +25,20 @@ package org.apache.logging.log4j.util;
*
* <p>Implementors are free to cache values or return a new or distinct value each time the supplier is invoked.
*
- * <p><strong>DEPRECATED:</strong> this class is deprecated and will be replaced with the Java 8 interface.</p>
+ * <p><strong>DEPRECATED:</strong> this class will be removed in 3.0 to be replaced with the Java 8 interface.</p>
*
* @param <T> the type of values returned by this supplier
*
* @since 2.4
*/
-@Deprecated(forRemoval = true, since = "2.22")
-public interface Supplier<T> extends java.util.function.Supplier<T> {}
+@InternalApi
+@Deprecated
+public interface Supplier<T> {
+
+ /**
+ * Gets a value.
+ *
+ * @return a value
+ */
+ T get();
+}
diff --git a/log4j-async-logger/src/test/java/org/apache/logging/log4j/async/logger/AsyncThreadContextTest.java b/log4j-async-logger/src/test/java/org/apache/logging/log4j/async/logger/AsyncThreadContextTest.java
index c8d57007bc..3c55fda33e 100644
--- a/log4j-async-logger/src/test/java/org/apache/logging/log4j/async/logger/AsyncThreadContextTest.java
+++ b/log4j-async-logger/src/test/java/org/apache/logging/log4j/async/logger/AsyncThreadContextTest.java
@@ -32,12 +32,10 @@ import org.apache.logging.log4j.core.Logger;
import org.apache.logging.log4j.core.LoggerContext;
import org.apache.logging.log4j.core.ThreadContextTestAccess;
import org.apache.logging.log4j.core.impl.Log4jContextFactory;
-import org.apache.logging.log4j.core.impl.Log4jPropertyKey;
import org.apache.logging.log4j.core.selector.ClassLoaderContextSelector;
import org.apache.logging.log4j.core.selector.ContextSelector;
import org.apache.logging.log4j.core.test.CoreLoggerContexts;
import org.apache.logging.log4j.plugins.di.DI;
-import org.apache.logging.log4j.spi.LoggingSystemProperty;
import org.apache.logging.log4j.spi.ReadOnlyThreadContextMap;
import org.apache.logging.log4j.test.TestProperties;
import org.apache.logging.log4j.test.junit.InitializesThreadContext;
@@ -50,12 +48,9 @@ import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvSource;
@Tag("sleepy")
-@SetTestProperty(
- key = Log4jPropertyKey.Constant.ASYNC_LOGGER_RING_BUFFER_SIZE,
- value = "128") // minimum ringbuffer size
-@SetTestProperty(
- key = Log4jPropertyKey.Constant.ASYNC_CONFIG_RING_BUFFER_SIZE,
- value = "128") // minimum ringbuffer size
+@SetTestProperty(key = "log4j2.enable.threadlocals", value = "false")
+@SetTestProperty(key = "AsyncLogger.ringBufferSize", value = "128") // minimum ringbuffer size
+@SetTestProperty(key = "AsyncLoggerConfig.ringBufferSize", value = "128") // minimum ringbuffer size
@InitializesThreadContext
public class AsyncThreadContextTest {
@@ -92,10 +87,13 @@ public class AsyncThreadContextTest {
COPY_ON_WRITE;
void init() {
- System.clearProperty(LoggingSystemProperty.Constant.THREAD_CONTEXT_MAP_CLASS);
final String PACKAGE = "org.apache.logging.log4j.spi.";
- System.setProperty(
- LoggingSystemProperty.Constant.THREAD_CONTEXT_MAP_CLASS, PACKAGE + implClassSimpleName());
+ final String implClass = implClassSimpleName();
+ if (implClass != null) {
+ System.setProperty("log4j2.threadContextMap", PACKAGE + implClass);
+ } else {
+ System.clearProperty("log4j2.threadContextMap");
+ }
PropertiesUtil.getProperties().reload();
ThreadContextTestAccess.init();
}
diff --git a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/impl/ThreadContextDataInjectorTest.java b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/impl/ThreadContextDataInjectorTest.java
index 5bb6691169..39258f5003 100644
--- a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/impl/ThreadContextDataInjectorTest.java
+++ b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/impl/ThreadContextDataInjectorTest.java
@@ -16,6 +16,7 @@
*/
package org.apache.logging.log4j.core.impl;
+import static java.util.Arrays.asList;
import static java.util.concurrent.Executors.newSingleThreadExecutor;
import static org.apache.logging.log4j.ThreadContext.getThreadContextMap;
import static org.apache.logging.log4j.core.impl.ContextDataInjectorFactory.createInjector;
@@ -27,23 +28,19 @@ import static org.hamcrest.Matchers.hasEntry;
import static org.hamcrest.Matchers.hasKey;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.not;
-import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
-import static org.junit.jupiter.api.Assertions.assertTrue;
-import java.lang.reflect.Constructor;
+import java.util.Collection;
import java.util.concurrent.ExecutionException;
-import java.util.function.Function;
import org.apache.logging.log4j.ThreadContext;
import org.apache.logging.log4j.core.ContextDataInjector;
-import org.apache.logging.log4j.spi.LoggingSystem;
import org.apache.logging.log4j.spi.ReadOnlyThreadContextMap;
-import org.apache.logging.log4j.spi.ThreadContextMap;
+import org.apache.logging.log4j.test.ThreadContextUtilityClass;
+import org.apache.logging.log4j.util.PropertiesUtil;
import org.apache.logging.log4j.util.SortedArrayStringMap;
import org.apache.logging.log4j.util.StringMap;
import org.junit.After;
+import org.junit.Before;
import org.junit.Test;
-import org.junit.platform.commons.function.Try;
-import org.junit.platform.commons.support.ReflectionSupport;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameter;
@@ -51,36 +48,38 @@ import org.junit.runners.Parameterized.Parameters;
@RunWith(Parameterized.class)
public class ThreadContextDataInjectorTest {
-
- public static final String THREAD_LOCAL_MAP_CLASS_NAME =
- "org.apache.logging.log4j.spi.CopyOnWriteSortedArrayThreadContextMap";
- public static final String GARBAGE_FREE_MAP_CLASS_NAME =
- "org.apache.logging.log4j.spi.GarbageFreeSortedArrayThreadContextMap";
-
- @Parameters(name = "{1}")
- public static Object[][] threadContextMapConstructorsAndReadOnlyNames() {
- return new Object[][] {
+ @Parameters(name = "{0}")
+ public static Collection<String[]> threadContextMapClassNames() {
+ return asList(new String[][] {
{
- (Function<Boolean, ThreadContextMap>) ThreadContextDataInjectorTest::createThreadLocalMap,
- THREAD_LOCAL_MAP_CLASS_NAME
+ "org.apache.logging.log4j.spi.CopyOnWriteSortedArrayThreadContextMap",
+ "org.apache.logging.log4j.spi.CopyOnWriteSortedArrayThreadContextMap"
},
{
- (Function<Boolean, ThreadContextMap>) ThreadContextDataInjectorTest::createGarbageFreeMap,
- GARBAGE_FREE_MAP_CLASS_NAME
- }
- };
+ "org.apache.logging.log4j.spi.GarbageFreeSortedArrayThreadContextMap",
+ "org.apache.logging.log4j.spi.GarbageFreeSortedArrayThreadContextMap"
+ },
+ {"org.apache.logging.log4j.spi.DefaultThreadContextMap", null}
+ });
}
- @Parameter(0)
- public Function<Boolean, ThreadContextMap> threadContextMapConstructor;
+ @Parameter
+ public String threadContextMapClassName;
- @Parameter(1)
- public String readOnlyThreadContextMapClassName;
+ @Parameter(value = 1)
+ public String readOnlythreadContextMapClassName;
+
+ @Before
+ public void before() {
+ System.setProperty("log4j2.threadContextMap", threadContextMapClassName);
+ }
@After
public void after() {
ThreadContext.remove("foo");
ThreadContext.remove("baz");
+ System.clearProperty("log4j2.threadContextMap");
+ System.clearProperty("log4j2.isThreadContextMapInheritable");
}
private void testContextDataInjector() {
@@ -90,7 +89,7 @@ public class ThreadContextDataInjectorTest {
(readOnlythreadContextMap == null)
? null
: readOnlythreadContextMap.getClass().getName(),
- is(equalTo(readOnlyThreadContextMapClassName)));
+ is(equalTo(readOnlythreadContextMapClassName)));
final ContextDataInjector contextDataInjector = createInjector();
final StringMap stringMap = contextDataInjector.injectContextData(null, new SortedArrayStringMap());
@@ -122,9 +121,9 @@ public class ThreadContextDataInjectorTest {
}
private void prepareThreadContext(final boolean isThreadContextMapInheritable) {
- LoggingSystem.getInstance()
- .setThreadContextMapFactory(() -> threadContextMapConstructor.apply(isThreadContextMapInheritable));
- ThreadContext.init();
+ System.setProperty("log4j2.isThreadContextMapInheritable", Boolean.toString(isThreadContextMapInheritable));
+ PropertiesUtil.getProperties().reload();
+ ThreadContextUtilityClass.reset();
ThreadContext.remove("baz");
ThreadContext.put("foo", "bar");
}
@@ -140,31 +139,8 @@ public class ThreadContextDataInjectorTest {
prepareThreadContext(true);
try {
newSingleThreadExecutor().submit(this::testContextDataInjector).get();
- } catch (ExecutionException ee) {
+ } catch (final ExecutionException ee) {
throw ee.getCause();
}
}
-
- private static ThreadContextMap createThreadLocalMap(final boolean inheritable) {
- final Try<Class<?>> loadedClass = ReflectionSupport.tryToLoadClass(THREAD_LOCAL_MAP_CLASS_NAME);
- final Class<? extends ThreadContextMap> mapClass =
- assertDoesNotThrow(loadedClass::get).asSubclass(ThreadContextMap.class);
- return newInstanceWithCapacityArg(mapClass, inheritable);
- }
-
- private static ThreadContextMap createGarbageFreeMap(final boolean inheritable) {
- final Try<Class<?>> loadedClass = ReflectionSupport.tryToLoadClass(GARBAGE_FREE_MAP_CLASS_NAME);
- final Class<? extends ThreadContextMap> mapClass =
- assertDoesNotThrow(loadedClass::get).asSubclass(ThreadContextMap.class);
- return newInstanceWithCapacityArg(mapClass, inheritable);
- }
-
- private static ThreadContextMap newInstanceWithCapacityArg(
- final Class<? extends ThreadContextMap> mapClass, final boolean inheritable) {
- final Constructor<? extends ThreadContextMap> constructor =
- assertDoesNotThrow(() -> mapClass.getDeclaredConstructor(Boolean.TYPE, Integer.TYPE));
- assertTrue(constructor.trySetAccessible(), () -> "Unable to access constructor for " + mapClass);
- return assertDoesNotThrow(
- () -> constructor.newInstance(inheritable, LoggingSystem.THREAD_CONTEXT_DEFAULT_INITIAL_CAPACITY));
- }
}
diff --git a/log4j-perf-test/src/main/java/org/apache/logging/log4j/spi/CopyOnWriteOpenHashMapThreadContextMap.java b/log4j-perf-test/src/main/java/org/apache/logging/log4j/spi/CopyOnWriteOpenHashMapThreadContextMap.java
index 4b5827be74..0787a8325a 100644
--- a/log4j-perf-test/src/main/java/org/apache/logging/log4j/spi/CopyOnWriteOpenHashMapThreadContextMap.java
+++ b/log4j-perf-test/src/main/java/org/apache/logging/log4j/spi/CopyOnWriteOpenHashMapThreadContextMap.java
@@ -17,6 +17,7 @@
package org.apache.logging.log4j.spi;
import org.apache.logging.log4j.perf.nogc.OpenHashStringMap;
+import org.apache.logging.log4j.util.PropertiesUtil;
import org.apache.logging.log4j.util.ReadOnlyStringMap;
import org.apache.logging.log4j.util.StringMap;
@@ -32,6 +33,9 @@ public class CopyOnWriteOpenHashMapThreadContextMap extends CopyOnWriteSortedArr
/** Constant used in benchmark code */
public static final Class<? extends ThreadContextMap> SUPER = CopyOnWriteSortedArrayThreadContextMap.class;
+ private static final int initialCapacity =
+ PropertiesUtil.getProperties().getIntegerProperty(PROPERTY_NAME_INITIAL_CAPACITY, DEFAULT_INITIAL_CAPACITY);
+
@Override
protected StringMap createStringMap() {
return new OpenHashStringMap<>(initialCapacity);
diff --git a/log4j-perf-test/src/main/java/org/apache/logging/log4j/spi/GarbageFreeOpenHashMapThreadContextMap.java b/log4j-perf-test/src/main/java/org/apache/logging/log4j/spi/GarbageFreeOpenHashMapThreadContextMap.java
index a40dec0d62..aae7457348 100644
--- a/log4j-perf-test/src/main/java/org/apache/logging/log4j/spi/GarbageFreeOpenHashMapThreadContextMap.java
+++ b/log4j-perf-test/src/main/java/org/apache/logging/log4j/spi/GarbageFreeOpenHashMapThreadContextMap.java
@@ -16,9 +16,6 @@
*/
package org.apache.logging.log4j.spi;
-import static org.apache.logging.log4j.spi.LoggingSystem.THREAD_CONTEXT_DEFAULT_INITIAL_CAPACITY;
-import static org.apache.logging.log4j.spi.LoggingSystemProperty.THREAD_CONTEXT_INITIAL_CAPACITY;
-
import org.apache.logging.log4j.perf.nogc.OpenHashStringMap;
import org.apache.logging.log4j.util.PropertiesUtil;
import org.apache.logging.log4j.util.ReadOnlyStringMap;
@@ -39,10 +36,12 @@ public class GarbageFreeOpenHashMapThreadContextMap extends GarbageFreeSortedArr
/** Constant used in benchmark code */
public static final Class<? extends ThreadContextMap> SUPER = GarbageFreeSortedArrayThreadContextMap.class;
+ private static final int initialCapacity =
+ PropertiesUtil.getProperties().getIntegerProperty(PROPERTY_NAME_INITIAL_CAPACITY, DEFAULT_INITIAL_CAPACITY);
+
@Override
- protected StringMap createStringMap(final int initialCapacity) {
- return new OpenHashStringMap<>(PropertiesUtil.getProperties()
- .getIntegerProperty(THREAD_CONTEXT_INITIAL_CAPACITY, THREAD_CONTEXT_DEFAULT_INITIAL_CAPACITY));
+ protected StringMap createStringMap() {
+ return new OpenHashStringMap<>(initialCapacity);
}
@Override
diff --git a/log4j-to-slf4j/pom.xml b/log4j-to-slf4j/pom.xml
index f92ff4d06d..ef7e783c93 100644
--- a/log4j-to-slf4j/pom.xml
+++ b/log4j-to-slf4j/pom.xml
@@ -44,6 +44,11 @@
</bnd-extra-module-options>
</properties>
<dependencies>
+ <dependency>
+ <groupId>org.osgi</groupId>
+ <artifactId>org.osgi.framework</artifactId>
+ <scope>provided</scope>
+ </dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
diff --git a/log4j-to-slf4j/src/main/java/org/apache/logging/slf4j/SLF4JProvider.java b/log4j-to-slf4j/src/main/java/org/apache/logging/slf4j/Activator.java
similarity index 66%
copy from log4j-to-slf4j/src/main/java/org/apache/logging/slf4j/SLF4JProvider.java
copy to log4j-to-slf4j/src/main/java/org/apache/logging/slf4j/Activator.java
index c340d30c40..5080869f5d 100644
--- a/log4j-to-slf4j/src/main/java/org/apache/logging/slf4j/SLF4JProvider.java
+++ b/log4j-to-slf4j/src/main/java/org/apache/logging/slf4j/Activator.java
@@ -16,16 +16,16 @@
*/
package org.apache.logging.slf4j;
-import aQute.bnd.annotation.Resolution;
-import aQute.bnd.annotation.spi.ServiceProvider;
-import org.apache.logging.log4j.spi.Provider;
+import org.apache.logging.log4j.util.ProviderActivator;
+import org.osgi.annotation.bundle.Header;
-/**
- * Bind the Log4j API to SLF4J.
- */
-@ServiceProvider(value = Provider.class, resolution = Resolution.OPTIONAL)
-public class SLF4JProvider extends Provider {
- public SLF4JProvider() {
- super(15, "3.0.0", SLF4JLoggerContextFactory.class, MDCContextMap.class);
+@Header(name = org.osgi.framework.Constants.BUNDLE_ACTIVATOR, value = "${@class}")
+@Header(
+ name = org.osgi.framework.Constants.BUNDLE_ACTIVATIONPOLICY,
+ value = org.osgi.framework.Constants.ACTIVATION_LAZY)
+public class Activator extends ProviderActivator {
+
+ public Activator() {
+ super(new SLF4JProvider());
}
}
diff --git a/log4j-to-slf4j/src/main/java/org/apache/logging/slf4j/MDCContextMap.java b/log4j-to-slf4j/src/main/java/org/apache/logging/slf4j/MDCContextMap.java
index f2e3463e7e..4f2ef190bb 100644
--- a/log4j-to-slf4j/src/main/java/org/apache/logging/slf4j/MDCContextMap.java
+++ b/log4j-to-slf4j/src/main/java/org/apache/logging/slf4j/MDCContextMap.java
@@ -19,7 +19,7 @@ package org.apache.logging.slf4j;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
-import org.apache.logging.log4j.spi.ThreadContextMap;
+import org.apache.logging.log4j.spi.CleanableThreadContextMap;
import org.apache.logging.log4j.util.SortedArrayStringMap;
import org.apache.logging.log4j.util.StringMap;
import org.slf4j.MDC;
@@ -27,7 +27,7 @@ import org.slf4j.MDC;
/**
* Bind the ThreadContextMap to the SLF4J MDC.
*/
-public class MDCContextMap implements ThreadContextMap {
+public class MDCContextMap implements CleanableThreadContextMap {
private static final StringMap EMPTY_CONTEXT_DATA = new SortedArrayStringMap(1);
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 919cf2dee5..26e94c67b3 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
@@ -23,14 +23,14 @@ import org.apache.logging.log4j.message.LoggerNameAwareMessage;
import org.apache.logging.log4j.message.Message;
import org.apache.logging.log4j.message.MessageFactory;
import org.apache.logging.log4j.spi.AbstractLogger;
-import org.apache.logging.log4j.spi.LoggingSystem;
-import org.apache.logging.log4j.spi.recycler.Recycler;
+import org.apache.logging.log4j.util.Constants;
import org.slf4j.LoggerFactory;
import org.slf4j.MarkerFactory;
import org.slf4j.spi.LocationAwareLogger;
public class SLF4JLogger extends AbstractLogger {
+ private static final long serialVersionUID = 1L;
/**
* Logback supports turbo filters, that can override the logger's level.
* Therefore we can never return a no-op builder.
@@ -38,11 +38,9 @@ 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> LOG_BUILDER_RECYCLER =
- LoggingSystem.getRecyclerFactory().create(SLF4JLogBuilder::new);
+ private static final ThreadLocal<SLF4JLogBuilder> logBuilder = ThreadLocal.withInitial(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) {
@@ -366,8 +364,10 @@ public class SLF4JLogger extends AbstractLogger {
@Override
protected LogBuilder getLogBuilder(final Level level) {
- final SLF4JLogBuilder builder = LOG_BUILDER_RECYCLER.acquire();
- return builder.reset(this, level);
+ final SLF4JLogBuilder builder = logBuilder.get();
+ return Constants.ENABLE_THREADLOCALS && !builder.isInUse()
+ ? builder.reset(this, level)
+ : new SLF4JLogBuilder(this, level);
}
@Override
diff --git a/log4j-to-slf4j/src/main/java/org/apache/logging/slf4j/SLF4JProvider.java b/log4j-to-slf4j/src/main/java/org/apache/logging/slf4j/SLF4JProvider.java
index c340d30c40..b8514c4f63 100644
--- a/log4j-to-slf4j/src/main/java/org/apache/logging/slf4j/SLF4JProvider.java
+++ b/log4j-to-slf4j/src/main/java/org/apache/logging/slf4j/SLF4JProvider.java
@@ -26,6 +26,6 @@ import org.apache.logging.log4j.spi.Provider;
@ServiceProvider(value = Provider.class, resolution = Resolution.OPTIONAL)
public class SLF4JProvider extends Provider {
public SLF4JProvider() {
- super(15, "3.0.0", SLF4JLoggerContextFactory.class, MDCContextMap.class);
+ super(15, "2.6.0", SLF4JLoggerContextFactory.class, MDCContextMap.class);
}
}
diff --git a/log4j-to-slf4j/src/test/java/org/apache/logging/slf4j/LoggerTest.java b/log4j-to-slf4j/src/test/java/org/apache/logging/slf4j/LoggerTest.java
index ad14d17aed..5f89d5a526 100644
--- a/log4j-to-slf4j/src/test/java/org/apache/logging/slf4j/LoggerTest.java
+++ b/log4j-to-slf4j/src/test/java/org/apache/logging/slf4j/LoggerTest.java
@@ -25,7 +25,6 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
@@ -41,9 +40,10 @@ import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.ThreadContext;
import org.apache.logging.log4j.message.MessageFactory;
-import org.apache.logging.log4j.message.ReusableMessageFactory;
+import org.apache.logging.log4j.message.ParameterizedMessageFactory;
import org.apache.logging.log4j.message.StringFormatterMessageFactory;
-import org.apache.logging.log4j.spi.LoggingSystem;
+import org.apache.logging.log4j.spi.AbstractLogger;
+import org.apache.logging.log4j.spi.MessageFactory2Adapter;
import org.apache.logging.log4j.test.junit.UsingStatusListener;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
@@ -53,6 +53,7 @@ import org.slf4j.MDC;
@LoggerContextSource
public class LoggerTest {
+ private static final Object OBJ = new Object();
// Log4j objects
private Logger logger;
// Logback objects
@@ -79,9 +80,23 @@ public class LoggerTest {
assertThat(list.strList, hasSize(2));
}
+ @Test
+ public void basicFlowDepreacted() {
+ logger.entry();
+ logger.exit();
+ assertThat(list.strList, hasSize(2));
+ }
+
+ @Test
+ public void simpleFlowDeprecated() {
+ logger.entry(OBJ);
+ logger.exit(0);
+ assertThat(list.strList, hasSize(2));
+ }
+
@Test
public void simpleFlow() {
- logger.traceEntry("foo");
+ logger.entry(OBJ);
logger.traceExit(0);
assertThat(list.strList, hasSize(2));
}
@@ -113,7 +128,7 @@ public class LoggerTest {
final Logger testLogger = testMessageFactoryMismatch(
"getLogger_String_MessageFactoryMismatch",
StringFormatterMessageFactory.INSTANCE,
- new ReusableMessageFactory());
+ ParameterizedMessageFactory.INSTANCE);
testLogger.debug("%,d", Integer.MAX_VALUE);
assertThat(list.strList, hasSize(1));
assertThat(list.strList, hasItem(String.format("%,d", Integer.MAX_VALUE)));
@@ -138,12 +153,17 @@ public class LoggerTest {
return testLogger;
}
- private static void checkMessageFactory(final MessageFactory messageFactory, final Logger testLogger) {
- if (messageFactory == null) {
- assertSame(LoggingSystem.getMessageFactory(), testLogger.getMessageFactory());
+ private static void checkMessageFactory(final MessageFactory messageFactory1, final Logger testLogger1) {
+ if (messageFactory1 == null) {
+ assertEquals(
+ AbstractLogger.DEFAULT_MESSAGE_FACTORY_CLASS,
+ testLogger1.getMessageFactory().getClass());
} else {
- final MessageFactory actual = testLogger.getMessageFactory();
- assertEquals(messageFactory, actual);
+ MessageFactory actual = testLogger1.getMessageFactory();
+ if (actual instanceof MessageFactory2Adapter) {
+ actual = ((MessageFactory2Adapter) actual).getOriginal();
+ }
+ assertEquals(messageFactory1, actual);
}
}