Merge pull request #50779 from dhruv-15-03

* fix-jul-bridge-uninstall-asymmetry:
  Polish "Avoid removing application-managed JUL bridge handler"
  Avoid removing application-managed JUL bridge handler

Closes gh-50779
This commit is contained in:
Stéphane Nicoll
2026-07-14 18:21:46 +02:00
4 changed files with 57 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,35 @@ class Log4J2LoggingSystemTests extends AbstractLoggingSystemTests {
assertThat(logger.getLevel()).isEqualTo(Level.FINE);
}
@Test
void cleanUpCleansBridgeHandlerInstalledByTheLoggingSystem() {
this.loggingSystem.beforeInitialize();
java.util.logging.Logger rootLogger = java.util.logging.Logger.getLogger("");
assertThat(rootLogger.getHandlers()).hasAtLeastOneElementOfType(Log4jBridgeHandler.class);
this.loggingSystem.cleanUp();
java.util.logging.Logger rootLoggerAfterCleanUp = java.util.logging.Logger.getLogger("");
assertThat(rootLoggerAfterCleanUp.getHandlers()).doesNotHaveAnyElementsOfTypes(Log4jBridgeHandler.class);
}
@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();