You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@logging.apache.org by ra...@apache.org on 2026/10/01 17:51:02 UTC

(logging-log4j2) 33/33: Restore UNCACHED thread name updates in AsyncLogger (#4280)

This is an automated email from the ASF dual-hosted git repository.

ramanathan1504 pushed a commit to branch port/2.27.0-fixes-to-2.26.x
in repository https://gitbox.apache.org/repos/asf/logging-log4j2.git

commit 27c3cac85d5c1321ddfa091ffdd6eab39e7fab8b
Author: Andrew <an...@gmail.com>
AuthorDate: Thu Oct 1 18:21:47 2026 +0300

    Restore UNCACHED thread name updates in AsyncLogger (#4280)
    
    Fix stale thread names in fully asynchronous loggers using the `UNCACHED` strategy.
    
    ---------
    
    Co-authored-by: Ramanathan <ra...@apache.org>
    (cherry picked from commit e076aa5f230c77f0a873655c4308913dbdc35b28)
---
 .../async/AsyncLoggerTestUncachedThreadName.java   |  76 --------------
 .../async/AsyncLoggerUncachedThreadNameTest.java   | 113 +++++++++++++++++++++
 .../logging/log4j/core/async/AsyncLogger.java      |   8 ++
 .../core/async/RingBufferLogEventTranslator.java   |   5 +-
 .../4275_fix_uncached_async_logger_thread_name.xml |  13 +++
 5 files changed, 138 insertions(+), 77 deletions(-)

diff --git a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/async/AsyncLoggerTestUncachedThreadName.java b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/async/AsyncLoggerTestUncachedThreadName.java
deleted file mode 100644
index 12fa5b07e3..0000000000
--- a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/async/AsyncLoggerTestUncachedThreadName.java
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to you under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.logging.log4j.core.async;
-
-import static org.junit.jupiter.api.Assertions.assertNotNull;
-import static org.junit.jupiter.api.Assertions.assertTrue;
-
-import java.io.BufferedReader;
-import java.io.File;
-import java.io.FileReader;
-import org.apache.logging.log4j.LogManager;
-import org.apache.logging.log4j.Logger;
-import org.apache.logging.log4j.core.config.ConfigurationFactory;
-import org.apache.logging.log4j.core.test.CoreLoggerContexts;
-import org.apache.logging.log4j.core.test.junit.Tags;
-import org.apache.logging.log4j.core.util.Constants;
-import org.apache.logging.log4j.util.Strings;
-import org.junit.jupiter.api.AfterAll;
-import org.junit.jupiter.api.BeforeAll;
-import org.junit.jupiter.api.Tag;
-import org.junit.jupiter.api.Test;
-
-@Tag(Tags.ASYNC_LOGGERS)
-class AsyncLoggerTestUncachedThreadName {
-
-    @BeforeAll
-    static void beforeClass() {
-        System.setProperty("AsyncLogger.ThreadNameStrategy", "UNCACHED");
-        System.setProperty(Constants.LOG4J_CONTEXT_SELECTOR, AsyncLoggerContextSelector.class.getName());
-        System.setProperty(ConfigurationFactory.CONFIGURATION_FILE_PROPERTY, "AsyncLoggerTest.xml");
-    }
-
-    @AfterAll
-    static void afterClass() {
-        System.setProperty(Constants.LOG4J_CONTEXT_SELECTOR, Strings.EMPTY);
-    }
-
-    @Test
-    void testAsyncLogUsesCurrentThreadName() throws Exception {
-        final File file = new File("target", "AsyncLoggerTest.log");
-        // System.out.println(f.getAbsolutePath());
-        file.delete();
-        final Logger log = LogManager.getLogger("com.foo.Bar");
-        final String msg = "Async logger msg";
-        log.info(msg);
-        Thread.currentThread().setName("MODIFIED-THREADNAME");
-        log.info(msg);
-        CoreLoggerContexts.stopLoggerContext(file); // stop async thread
-
-        final BufferedReader reader = new BufferedReader(new FileReader(file));
-        final String line1 = reader.readLine();
-        final String line2 = reader.readLine();
-        // System.out.println(line1);
-        // System.out.println(line2);
-        reader.close();
-        file.delete();
-        assertNotNull(line1, "line1");
-        assertNotNull(line2, "line2");
-        assertTrue(line1.endsWith(" INFO c.f.Bar [main]   Async logger msg "), "line1");
-        assertTrue(line2.endsWith(" INFO c.f.Bar [MODIFIED-THREADNAME]   Async logger msg "), "line2");
-    }
-}
diff --git a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/async/AsyncLoggerUncachedThreadNameTest.java b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/async/AsyncLoggerUncachedThreadNameTest.java
new file mode 100644
index 0000000000..25e3a26b51
--- /dev/null
+++ b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/async/AsyncLoggerUncachedThreadNameTest.java
@@ -0,0 +1,113 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.logging.log4j.core.async;
+
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertSame;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileReader;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicReference;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.apache.logging.log4j.core.test.CoreLoggerContexts;
+import org.apache.logging.log4j.core.test.junit.Tags;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Tag;
+import org.junit.jupiter.api.Test;
+
+@Tag(Tags.ASYNC_LOGGERS)
+class AsyncLoggerUncachedThreadNameTest {
+
+    @BeforeAll
+    static void beforeClass() {
+        System.setProperty("log4j2.asyncLoggerThreadNameStrategy", "UNCACHED");
+        System.setProperty("log4j2.contextSelector", AsyncLoggerContextSelector.class.getName());
+        System.setProperty("log4j2.configurationFile", "AsyncLoggerTest.xml");
+    }
+
+    @AfterAll
+    static void afterClass() {
+        System.clearProperty("log4j2.asyncLoggerThreadNameStrategy");
+        System.clearProperty("log4j2.contextSelector");
+        System.clearProperty("log4j2.configurationFile");
+    }
+
+    @Test
+    void testAsyncLogUsesCurrentThreadName() throws Exception {
+        final File file = new File("target", "AsyncLoggerTest.log");
+        file.delete();
+        final Logger log = LogManager.getLogger("com.foo.Bar");
+
+        final String msg = "Async logger msg";
+        final AtomicReference<Thread> firstTaskThreadRef = new AtomicReference<>();
+        final AtomicReference<Thread> secondTaskThreadRef = new AtomicReference<>();
+        final ExecutorService executor = Executors.newSingleThreadExecutor();
+        try {
+            final Future<?> firstTask = executor.submit(() -> {
+                final Thread currentThread = Thread.currentThread();
+                final String originalThreadName = currentThread.getName();
+                final String expectedThreadName = "MODIFIED-THREADNAME-1";
+                firstTaskThreadRef.set(currentThread);
+                try {
+                    currentThread.setName(expectedThreadName);
+                    log.info(msg);
+                } finally {
+                    currentThread.setName(originalThreadName);
+                }
+                return expectedThreadName;
+            });
+            final Future<?> secondTask = executor.submit(() -> {
+                final Thread currentThread = Thread.currentThread();
+                final String originalThreadName = currentThread.getName();
+                final String expectedThreadName = "MODIFIED-THREADNAME-2";
+                secondTaskThreadRef.set(currentThread);
+                try {
+                    currentThread.setName(expectedThreadName);
+                    log.info(msg);
+                } finally {
+                    currentThread.setName(originalThreadName);
+                }
+                return expectedThreadName;
+            });
+            final String firstTaskThreadName = (String) firstTask.get();
+            final String secondTaskThreadName = (String) secondTask.get();
+            assertSame(firstTaskThreadRef.get(), secondTaskThreadRef.get(), "same async worker thread");
+        } finally {
+            executor.shutdown();
+            executor.awaitTermination(5, TimeUnit.SECONDS);
+        }
+
+        CoreLoggerContexts.stopLoggerContext(file);
+
+        try (final BufferedReader reader = new BufferedReader(new FileReader(file))) {
+            final String line1 = reader.readLine();
+            final String line2 = reader.readLine();
+            assertNotNull(line1, "line1");
+            assertNotNull(line2, "line2");
+            assertTrue(line1.contains("MODIFIED-THREADNAME-1"), "line1");
+            assertTrue(line2.contains("MODIFIED-THREADNAME-2"), "line2");
+        }
+    }
+}
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/async/AsyncLogger.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/async/AsyncLogger.java
index ff142abe8a..cbf6dca522 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/async/AsyncLogger.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/async/AsyncLogger.java
@@ -239,6 +239,7 @@ public class AsyncLogger extends Logger implements EventTranslatorVararg<RingBuf
 
         final RingBufferLogEventTranslator translator = getCachedTranslator();
         initTranslator(translator, fqcn, level, marker, message, thrown);
+        initTranslatorThreadValues(translator);
         publish(translator);
     }
 
@@ -266,6 +267,7 @@ public class AsyncLogger extends Logger implements EventTranslatorVararg<RingBuf
 
         final RingBufferLogEventTranslator translator = getCachedTranslator();
         initTranslator(translator, fqcn, location, level, marker, message, thrown);
+        initTranslatorThreadValues(translator);
         publish(translator);
     }
 
@@ -357,6 +359,12 @@ public class AsyncLogger extends Logger implements EventTranslatorVararg<RingBuf
                 );
     }
 
+    private void initTranslatorThreadValues(final RingBufferLogEventTranslator translator) {
+        if (THREAD_NAME_CACHING_STRATEGY == ThreadNameCachingStrategy.UNCACHED) {
+            translator.updateThreadValues();
+        }
+    }
+
     /**
      * Returns the caller location if requested, {@code null} otherwise.
      *
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/async/RingBufferLogEventTranslator.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/async/RingBufferLogEventTranslator.java
index f6fee9fcf5..db8567db43 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/async/RingBufferLogEventTranslator.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/async/RingBufferLogEventTranslator.java
@@ -130,8 +130,11 @@ public class RingBufferLogEventTranslator implements EventTranslator<RingBufferL
     }
 
     /**
+     * Updates the cached thread values from the current thread.
+     *
      * @deprecated since 2.25.0. {@link RingBufferLogEventTranslator} instances should only be used on the thread that
-     * created it.
+     * created them. This method is retained for internal use to refresh the thread name after
+     * {@link Thread#setName(String)}.
      */
     @Deprecated
     public void updateThreadValues() {
diff --git a/src/changelog/.2.x.x/4275_fix_uncached_async_logger_thread_name.xml b/src/changelog/.2.x.x/4275_fix_uncached_async_logger_thread_name.xml
new file mode 100644
index 0000000000..95b628ce51
--- /dev/null
+++ b/src/changelog/.2.x.x/4275_fix_uncached_async_logger_thread_name.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<entry xmlns="https://logging.apache.org/xml/ns"
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xsi:schemaLocation="
+           https://logging.apache.org/xml/ns
+           https://logging.apache.org/xml/ns/log4j-changelog-0.xsd"
+       type="fixed">
+  <issue id="4275" link="https://github.com/apache/logging-log4j2/issues/4275"/>
+  <issue id="4280" link="https://github.com/apache/logging-log4j2/pull/4280"/>
+  <description format="asciidoc">
+    Fix stale thread names in fully asynchronous loggers using the `UNCACHED` strategy.
+  </description>
+</entry>