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 <dhruvrastogi2004@gmail.com>
This commit is contained in:
dhruv-15-03
2026-07-14 18:11:50 +02:00
committed by Stéphane Nicoll
parent 2eedeabe9a
commit fe5164521d
4 changed files with 47 additions and 2 deletions
@@ -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();
@@ -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);
@@ -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(
@@ -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();