Register Log42J StatusListener

Update Log42JLoggingSystem to register Boot's StatusListener
instead of resetting the fallbackListener during initialization.

This fixes an issue when resetting the fallback listener leads to
closing standard streams.

See gh-44380

Signed-off-by: Dmytro Nosan <dimanosan@gmail.com>
This commit is contained in:
Dmytro Nosan
2025-02-03 22:01:13 +02:00
committed by Andy Wilkinson
parent 575655ca39
commit 22958097e3
2 changed files with 53 additions and 19 deletions

View File

@@ -47,6 +47,7 @@ import org.apache.logging.log4j.core.net.ssl.SslConfigurationFactory;
import org.apache.logging.log4j.core.util.AuthorizationProvider;
import org.apache.logging.log4j.core.util.NameUtil;
import org.apache.logging.log4j.jul.Log4jBridgeHandler;
import org.apache.logging.log4j.status.StatusConsoleListener;
import org.apache.logging.log4j.status.StatusLogger;
import org.apache.logging.log4j.util.PropertiesUtil;
@@ -93,6 +94,9 @@ public class Log4J2LoggingSystem extends AbstractLoggingSystem {
static final String ENVIRONMENT_KEY = Conventions.getQualifiedAttributeName(Log4J2LoggingSystem.class,
"environment");
static final String STATUS_LISTENER_KEY = Conventions.getQualifiedAttributeName(Log4J2LoggingSystem.class,
"statusListener");
private static final LogLevels<Level> LEVELS = new LogLevels<>();
static {
@@ -214,10 +218,12 @@ public class Log4J2LoggingSystem extends AbstractLoggingSystem {
if (isAlreadyInitialized(loggerContext)) {
return;
}
resetFallbackListenerStream(StatusLogger.getLogger());
StatusConsoleListener listener = new StatusConsoleListener(Level.WARN);
StatusLogger.getLogger().registerListener(listener);
loggerContext.putObject(STATUS_LISTENER_KEY, listener);
Environment environment = initializationContext.getEnvironment();
if (environment != null) {
getLoggerContext().putObject(ENVIRONMENT_KEY, environment);
loggerContext.putObject(ENVIRONMENT_KEY, environment);
Log4J2LoggingSystem.propertySource.setEnvironment(environment);
PropertiesUtil.getProperties().addPropertySource(Log4J2LoggingSystem.propertySource);
}
@@ -226,21 +232,6 @@ public class Log4J2LoggingSystem extends AbstractLoggingSystem {
markAsInitialized(loggerContext);
}
/**
* Reset the stream used by the fallback listener to the current system out. This
* allows the fallback listener to work with any captured output streams in a similar
* way to the {@code follow} attribute of the {@code Console} appender.
* @param statusLogger the status logger to update
*/
private void resetFallbackListenerStream(StatusLogger statusLogger) {
try {
statusLogger.getFallbackListener().setStream(System.out);
}
catch (NoSuchMethodError ex) {
// Ignore for older versions of Log4J
}
}
@Override
protected void loadDefaults(LoggingInitializationContext initializationContext, LogFile logFile) {
String location = getPackagedConfigFile((logFile != null) ? "log4j2-file.xml" : "log4j2.xml");
@@ -454,9 +445,14 @@ public class Log4J2LoggingSystem extends AbstractLoggingSystem {
super.cleanUp();
LoggerContext loggerContext = getLoggerContext();
markAsUninitialized(loggerContext);
StatusConsoleListener listener = (StatusConsoleListener) getLoggerContext().getObject(STATUS_LISTENER_KEY);
if (listener != null) {
StatusLogger.getLogger().removeListener(listener);
loggerContext.removeObject(STATUS_LISTENER_KEY);
}
loggerContext.getConfiguration().removeFilter(FILTER);
Log4J2LoggingSystem.propertySource.setEnvironment(null);
getLoggerContext().removeObject(ENVIRONMENT_KEY);
loggerContext.removeObject(ENVIRONMENT_KEY);
}
private LoggerConfig getLogger(String name) {

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2012-2024 the original author or authors.
* Copyright 2012-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -41,6 +41,8 @@ import org.apache.logging.log4j.core.config.composite.CompositeConfiguration;
import org.apache.logging.log4j.core.config.plugins.util.PluginRegistry;
import org.apache.logging.log4j.core.util.ShutdownCallbackRegistry;
import org.apache.logging.log4j.jul.Log4jBridgeHandler;
import org.apache.logging.log4j.status.StatusListener;
import org.apache.logging.log4j.status.StatusLogger;
import org.apache.logging.log4j.util.PropertiesUtil;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
@@ -469,6 +471,42 @@ class Log4J2LoggingSystemTests extends AbstractLoggingSystemTests {
assertThat(environment).isSameAs(this.environment);
}
@Test
void initializeRegisterStatusListenerAndAttachToLoggerContext() {
this.loggingSystem.beforeInitialize();
this.loggingSystem.initialize(this.initializationContext, null, null);
LoggerContext loggerContext = (LoggerContext) LogManager.getContext(false);
StatusListener statusListener = (StatusListener) loggerContext
.getObject(Log4J2LoggingSystem.STATUS_LISTENER_KEY);
assertThat(statusListener).isNotNull();
assertThat(StatusLogger.getLogger().getListeners()).contains(statusListener);
}
@Test
void statusListenerIsUpdatedUponReinitialization() {
this.loggingSystem.beforeInitialize();
this.loggingSystem.initialize(this.initializationContext, null, null);
// listener should be registered
LoggerContext loggerContext = (LoggerContext) LogManager.getContext(false);
StatusListener statusListener = (StatusListener) loggerContext
.getObject(Log4J2LoggingSystem.STATUS_LISTENER_KEY);
assertThat(statusListener).isNotNull();
assertThat(StatusLogger.getLogger().getListeners()).contains(statusListener);
this.loggingSystem.cleanUp();
// listener should be removed from context and StatusLogger
assertThat(StatusLogger.getLogger().getListeners()).doesNotContain(statusListener);
assertThat(loggerContext.getObject(Log4J2LoggingSystem.STATUS_LISTENER_KEY)).isNull();
// a new listener should be registered
this.loggingSystem.beforeInitialize();
this.loggingSystem.initialize(this.initializationContext, null, null);
StatusListener statusListener1 = (StatusListener) loggerContext
.getObject(Log4J2LoggingSystem.STATUS_LISTENER_KEY);
assertThat(statusListener1).isNotNull();
assertThat(StatusLogger.getLogger().getListeners()).contains(statusListener1);
}
@Test
void initializeAddsSpringEnvironmentPropertySource() {
this.environment.setProperty("spring", "boot");