Skip to content

Commit 258eb29

Browse files
nosanphilwebb
authored andcommitted
Register Logback StatusListener when using custom Logback file
Update `LogbackLoggingSystem` so that the `OnErrorConsoleStatusListener` is also registered when loading a custom Logback configuration file. See gh-43931 Signed-off-by: Dmytro Nosan <dimanosan@gmail.com>
1 parent b9f3d52 commit 258eb29

File tree

6 files changed

+74
-21
lines changed

6 files changed

+74
-21
lines changed

spring-boot-project/spring-boot/src/main/java/org/springframework/boot/logging/logback/LogbackLoggingSystem.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -216,6 +216,7 @@ private boolean initializeFromAotGeneratedArtifactsIfPossible(LoggingInitializat
216216
LoggerContext loggerContext = getLoggerContext();
217217
stopAndReset(loggerContext);
218218
withLoggingSuppressed(() -> putInitializationContextObjects(loggerContext, initializationContext));
219+
addOnErrorConsoleStatusListener(loggerContext);
219220
SpringBootJoranConfigurator configurator = new SpringBootJoranConfigurator(initializationContext);
220221
configurator.setContext(loggerContext);
221222
boolean configuredUsingAotGeneratedArtifacts = configurator.configureUsingAotGeneratedArtifacts();
@@ -261,6 +262,7 @@ protected void loadConfiguration(LoggingInitializationContext initializationCont
261262
if (initializationContext != null) {
262263
applySystemProperties(initializationContext.getEnvironment(), logFile);
263264
}
265+
addOnErrorConsoleStatusListener(loggerContext);
264266
try {
265267
Resource resource = ApplicationResourceLoader.get().getResource(location);
266268
configureByResourceUrl(initializationContext, loggerContext, resource.getURL());
@@ -493,7 +495,7 @@ private void withLoggingSuppressed(Runnable action) {
493495

494496
private void addOnErrorConsoleStatusListener(LoggerContext context) {
495497
FilteringStatusListener listener = new FilteringStatusListener(new OnErrorConsoleStatusListener(),
496-
Status.ERROR);
498+
Status.WARN);
497499
listener.setContext(context);
498500
if (context.getStatusManager().add(listener)) {
499501
listener.start();

spring-boot-project/spring-boot/src/test/java/org/springframework/boot/logging/logback/LogbackLoggingSystemTests.java

Lines changed: 36 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,6 @@
4343
import ch.qos.logback.core.status.OnConsoleStatusListener;
4444
import ch.qos.logback.core.status.OnErrorConsoleStatusListener;
4545
import ch.qos.logback.core.status.Status;
46-
import ch.qos.logback.core.status.StatusListener;
4746
import ch.qos.logback.core.util.DynamicClassLoadingException;
4847
import org.junit.jupiter.api.AfterEach;
4948
import org.junit.jupiter.api.BeforeEach;
@@ -656,10 +655,8 @@ void logbackDebugPropertyIsHonored(CapturedOutput output) {
656655
.contains("SizeAndTimeBasedFileNamingAndTriggeringPolicy")
657656
.contains("DebugLogbackConfigurator");
658657
LoggerContext loggerContext = this.logger.getLoggerContext();
659-
List<StatusListener> statusListeners = loggerContext.getStatusManager().getCopyOfStatusListenerList();
660-
assertThat(statusListeners).hasSize(1);
661-
StatusListener statusListener = statusListeners.get(0);
662-
assertThat(statusListener).isInstanceOf(OnConsoleStatusListener.class);
658+
assertThat(loggerContext.getStatusManager().getCopyOfStatusListenerList())
659+
.allSatisfy((listener) -> assertThat(listener).isInstanceOf(OnConsoleStatusListener.class));
663660
}
664661
finally {
665662
System.clearProperty("logback.debug");
@@ -671,25 +668,35 @@ void logbackErrorStatusListenerShouldBeRegistered(CapturedOutput output) {
671668
this.loggingSystem.beforeInitialize();
672669
initialize(this.initializationContext, null, getLogFile(tmpDir() + "/tmp.log", null));
673670
LoggerContext loggerContext = this.logger.getLoggerContext();
674-
List<StatusListener> statusListeners = loggerContext.getStatusManager().getCopyOfStatusListenerList();
675-
assertThat(statusListeners).hasSize(1);
676-
StatusListener statusListener = statusListeners.get(0);
677-
assertThat(statusListener).isInstanceOf(FilteringStatusListener.class);
678-
assertThat(statusListener).hasFieldOrPropertyWithValue("levelThreshold", Status.ERROR);
679-
assertThat(statusListener).extracting("delegate").isInstanceOf(OnErrorConsoleStatusListener.class);
680-
AppenderBase<ILoggingEvent> appender = new AppenderBase<>() {
681-
682-
@Override
683-
protected void append(ILoggingEvent eventObject) {
684-
throw new IllegalStateException("Fail to append");
685-
}
686-
687-
};
671+
assertThat(loggerContext.getStatusManager().getCopyOfStatusListenerList()).allSatisfy((listener) -> {
672+
assertThat(listener).isInstanceOf(FilteringStatusListener.class);
673+
assertThat(listener).hasFieldOrPropertyWithValue("levelThreshold", Status.WARN);
674+
assertThat(listener).extracting("delegate").isInstanceOf(OnErrorConsoleStatusListener.class);
675+
});
676+
AlwaysFailAppender appender = new AlwaysFailAppender();
677+
appender.setContext(loggerContext);
678+
appender.start();
688679
this.logger.addAppender(appender);
680+
this.logger.info("Hello world");
681+
assertThat(output).contains("Always Fail Appender").contains("Hello world");
682+
}
683+
684+
@Test
685+
void logbackErrorStatusListenerShouldBeRegisteredWhenUsingCustomLogbackXml(CapturedOutput output) {
686+
this.loggingSystem.beforeInitialize();
687+
initialize(this.initializationContext, "classpath:logback-include-defaults.xml", null);
688+
LoggerContext loggerContext = this.logger.getLoggerContext();
689+
assertThat(loggerContext.getStatusManager().getCopyOfStatusListenerList()).allSatisfy((listener) -> {
690+
assertThat(listener).isInstanceOf(FilteringStatusListener.class);
691+
assertThat(listener).hasFieldOrPropertyWithValue("levelThreshold", Status.WARN);
692+
assertThat(listener).extracting("delegate").isInstanceOf(OnErrorConsoleStatusListener.class);
693+
});
694+
AlwaysFailAppender appender = new AlwaysFailAppender();
689695
appender.setContext(loggerContext);
690696
appender.start();
697+
this.logger.addAppender(appender);
691698
this.logger.info("Hello world");
692-
assertThat(output).contains("Fail to append").contains("Hello world");
699+
assertThat(output).contains("Always Fail Appender").contains("Hello world");
693700
}
694701

695702
@Test
@@ -1042,4 +1049,13 @@ private static SizeAndTimeBasedRollingPolicy<?> getRollingPolicy() {
10421049
return (SizeAndTimeBasedRollingPolicy<?>) getFileAppender().getRollingPolicy();
10431050
}
10441051

1052+
private static final class AlwaysFailAppender extends AppenderBase<ILoggingEvent> {
1053+
1054+
@Override
1055+
protected void append(ILoggingEvent eventObject) {
1056+
throw new RuntimeException("Always Fail Appender");
1057+
}
1058+
1059+
}
1060+
10451061
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<configuration>
3+
<statusListener class="ch.qos.logback.core.status.OnConsoleStatusListener"/>
4+
<include resource="org/springframework/boot/logging/logback/defaults.xml"/>
5+
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
6+
<encoder>
7+
<pattern>[%p] - %m%n</pattern>
8+
</encoder>
9+
</appender>
10+
<root level="INFO">
11+
<appender-ref ref="CONSOLE"/>
12+
</root>
13+
</configuration>

spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-structured-logging/src/main/resources/application.properties

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,7 @@ logging.structured.format.console=smoketest.structuredlogging.CustomStructuredLo
1010
#---
1111
spring.config.activate.on-profile=on-error
1212
logging.structured.json.customizer=smoketest.structuredlogging.DuplicateJsonMembersCustomizer
13+
#---
14+
logging.config=classpath:custom-logback.xml
15+
spring.config.activate.on-profile=on-error-custom-logback-file
16+
logging.structured.json.customizer=smoketest.structuredlogging.DuplicateJsonMembersCustomizer
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<configuration>
2+
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
3+
<encoder class="org.springframework.boot.logging.logback.StructuredLogEncoder">
4+
<format>ecs</format>
5+
<charset>UTF-8</charset>
6+
</encoder>
7+
</appender>
8+
<root level="INFO">
9+
<appender-ref ref="STDOUT"/>
10+
</root>
11+
</configuration>

spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-structured-logging/src/test/java/smoketest/structuredlogging/SampleStructuredLoggingApplicationTests.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,4 +71,11 @@ void shouldCaptureCustomizerError(CapturedOutput output) {
7171
assertThat(output).contains("The name 'test' has already been written");
7272
}
7373

74+
@Test
75+
void shouldCaptureCustomizerErrorWhenUsingCustomLogbackFile(CapturedOutput output) {
76+
SampleStructuredLoggingApplication
77+
.main(new String[] { "--spring.profiles.active=on-error-custom-logback-file" });
78+
assertThat(output).contains("The name 'test' has already been written");
79+
}
80+
7481
}

0 commit comments

Comments
 (0)