From fe5164521d858a280cefdd07a48eef4b74b2c8bd Mon Sep 17 00:00:00 2001 From: dhruv-15-03 Date: Tue, 16 Jun 2026 22:48:12 +0530 Subject: [PATCH] Avoid removing application-managed JUL bridge handler `LogbackLoggingSystem` and `Log4J2LoggingSystem` install a JUL bridge handler only when the application is not already managing `java.util.logging`. However, `cleanUp()` removed the bridge handler whenever the bridge class was present on the classpath, so Spring Boot uninstalled a bridge handler that an application had installed and managed itself. Track whether the bridge handler was installed by Spring Boot and only remove it during cleanup when that is the case. See gh-50779 Signed-off-by: dhruv-15-03 --- .../logging/log4j2/Log4J2LoggingSystem.java | 7 ++++++- .../logging/logback/LogbackLoggingSystem.java | 7 ++++++- .../log4j2/Log4J2LoggingSystemTests.java | 20 +++++++++++++++++++ .../logback/LogbackLoggingSystemTests.java | 15 ++++++++++++++ 4 files changed, 47 insertions(+), 2 deletions(-) diff --git a/core/spring-boot/src/main/java/org/springframework/boot/logging/log4j2/Log4J2LoggingSystem.java b/core/spring-boot/src/main/java/org/springframework/boot/logging/log4j2/Log4J2LoggingSystem.java index e093a4ce6a3..8ca48215727 100644 --- a/core/spring-boot/src/main/java/org/springframework/boot/logging/log4j2/Log4J2LoggingSystem.java +++ b/core/spring-boot/src/main/java/org/springframework/boot/logging/log4j2/Log4J2LoggingSystem.java @@ -76,6 +76,7 @@ import org.springframework.util.StringUtils; * @author Ben Hale * @author Ralph Goers * @author Piotr P. Karwasz + * @author Dhruv Rastogi * @since 1.2.0 */ public class Log4J2LoggingSystem extends AbstractLoggingSystem { @@ -118,6 +119,8 @@ public class Log4J2LoggingSystem extends AbstractLoggingSystem { private final LoggerContext loggerContext; + private boolean bridgeHandlerInstalled; + /** * Create a new {@link Log4J2LoggingSystem} instance. * @param classLoader the class loader to use. @@ -189,6 +192,7 @@ public class Log4J2LoggingSystem extends AbstractLoggingSystem { && isLog4jBridgeHandlerAvailable()) { removeDefaultRootHandler(); Log4jBridgeHandler.install(false, null, true); + this.bridgeHandlerInstalled = true; return true; } } @@ -454,8 +458,9 @@ public class Log4J2LoggingSystem extends AbstractLoggingSystem { @Override public void cleanUp() { - if (isLog4jBridgeHandlerAvailable()) { + if (this.bridgeHandlerInstalled) { removeLog4jBridgeHandler(); + this.bridgeHandlerInstalled = false; } super.cleanUp(); LoggerContext loggerContext = getLoggerContext(); diff --git a/core/spring-boot/src/main/java/org/springframework/boot/logging/logback/LogbackLoggingSystem.java b/core/spring-boot/src/main/java/org/springframework/boot/logging/logback/LogbackLoggingSystem.java index 3c991980ad5..b772c0885e3 100644 --- a/core/spring-boot/src/main/java/org/springframework/boot/logging/logback/LogbackLoggingSystem.java +++ b/core/spring-boot/src/main/java/org/springframework/boot/logging/logback/LogbackLoggingSystem.java @@ -75,6 +75,7 @@ import org.springframework.util.StringUtils; * @author Dave Syer * @author Andy Wilkinson * @author Ben Hale + * @author Dhruv Rastogi * @since 1.0.0 */ public class LogbackLoggingSystem extends AbstractLoggingSystem implements BeanFactoryInitializationAotProcessor { @@ -111,6 +112,8 @@ public class LogbackLoggingSystem extends AbstractLoggingSystem implements BeanF private final StatusPrinter2 statusPrinter = new StatusPrinter2(); + private boolean bridgeHandlerInstalled; + public LogbackLoggingSystem(ClassLoader classLoader) { super(classLoader); } @@ -141,6 +144,7 @@ public class LogbackLoggingSystem extends AbstractLoggingSystem implements BeanF if (isBridgeJulIntoSlf4j()) { removeJdkLoggingBridgeHandler(); SLF4JBridgeHandler.install(); + this.bridgeHandlerInstalled = true; } } catch (Throwable ex) { @@ -334,8 +338,9 @@ public class LogbackLoggingSystem extends AbstractLoggingSystem implements BeanF LoggerContext context = getLoggerContext(); markAsUninitialized(context); super.cleanUp(); - if (isBridgeHandlerAvailable()) { + if (this.bridgeHandlerInstalled) { removeJdkLoggingBridgeHandler(); + this.bridgeHandlerInstalled = false; } context.getStatusManager().clear(); context.getTurboFilterList().remove(SUPPRESS_ALL_FILTER); diff --git a/core/spring-boot/src/test/java/org/springframework/boot/logging/log4j2/Log4J2LoggingSystemTests.java b/core/spring-boot/src/test/java/org/springframework/boot/logging/log4j2/Log4J2LoggingSystemTests.java index ecb14ddbd98..da92666cc7c 100644 --- a/core/spring-boot/src/test/java/org/springframework/boot/logging/log4j2/Log4J2LoggingSystemTests.java +++ b/core/spring-boot/src/test/java/org/springframework/boot/logging/log4j2/Log4J2LoggingSystemTests.java @@ -88,6 +88,7 @@ import static org.mockito.Mockito.times; * @author Ben Hale * @author Madhura Bhave * @author Piotr P. Karwasz + * @author Dhruv Rastogi */ @ExtendWith(OutputCaptureExtension.class) @ClassPathExclusions("logback-*.jar") @@ -406,6 +407,25 @@ class Log4J2LoggingSystemTests extends AbstractLoggingSystemTests { assertThat(logger.getLevel()).isEqualTo(Level.FINE); } + @Test + void cleanUpLeavesBridgeHandlerInstalledByTheApplicationInPlace() { + java.util.logging.Logger rootLogger = java.util.logging.Logger.getLogger(""); + Log4jBridgeHandler.install(false, null, true); + try { + assertThat(rootLogger.getHandlers()).hasAtLeastOneElementOfType(Log4jBridgeHandler.class); + this.loggingSystem.beforeInitialize(); + this.loggingSystem.cleanUp(); + assertThat(rootLogger.getHandlers()).hasAtLeastOneElementOfType(Log4jBridgeHandler.class); + } + finally { + for (Handler handler : rootLogger.getHandlers()) { + if (handler instanceof Log4jBridgeHandler) { + rootLogger.removeHandler(handler); + } + } + } + } + @Test void shutdownHookIsDisabled() { assertThat( diff --git a/core/spring-boot/src/test/java/org/springframework/boot/logging/logback/LogbackLoggingSystemTests.java b/core/spring-boot/src/test/java/org/springframework/boot/logging/logback/LogbackLoggingSystemTests.java index 6adaeb4d8de..02d9a1d39b4 100644 --- a/core/spring-boot/src/test/java/org/springframework/boot/logging/logback/LogbackLoggingSystemTests.java +++ b/core/spring-boot/src/test/java/org/springframework/boot/logging/logback/LogbackLoggingSystemTests.java @@ -107,6 +107,7 @@ import static org.mockito.Mockito.times; * @author Scott Frederick * @author Jonatan Ivanov * @author Moritz Halbritter + * @author Dhruv Rastogi */ @ExtendWith(OutputCaptureExtension.class) @ClassPathExclusions({ "log4j-core-*.jar", "log4j-api-*.jar" }) @@ -340,6 +341,20 @@ class LogbackLoggingSystemTests extends AbstractLoggingSystemTests { assertThat(bridgeHandlerInstalled()).isFalse(); } + @Test + void cleanUpLeavesBridgeHandlerInstalledByTheApplicationInPlace() { + SLF4JBridgeHandler.install(); + try { + assertThat(bridgeHandlerInstalled()).isTrue(); + this.loggingSystem.beforeInitialize(); + this.loggingSystem.cleanUp(); + assertThat(bridgeHandlerInstalled()).isTrue(); + } + finally { + SLF4JBridgeHandler.uninstall(); + } + } + @Test void standardConfigLocations() { String[] locations = this.loggingSystem.getStandardConfigLocations();