Skip to content

Commit 533f301

Browse files
committed
Merge branch 'gh-32109'
2 parents 73fd760 + 3eb3d79 commit 533f301

File tree

8 files changed

+361
-121
lines changed

8 files changed

+361
-121
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
/*
2+
* Copyright 2012-2022 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.boot.autoconfigure.logging;
18+
19+
import java.util.function.Supplier;
20+
21+
import org.apache.commons.logging.Log;
22+
import org.apache.commons.logging.LogFactory;
23+
24+
import org.springframework.boot.autoconfigure.condition.ConditionEvaluationReport;
25+
import org.springframework.boot.logging.LogLevel;
26+
import org.springframework.util.Assert;
27+
28+
/**
29+
* Logs the {@link ConditionEvaluationReport}.
30+
*
31+
* @author Greg Turnquist
32+
* @author Dave Syer
33+
* @author Phillip Webb
34+
* @author Andy Wilkinson
35+
* @author Madhura Bhave
36+
*/
37+
class ConditionEvaluationReportLogger {
38+
39+
private final Log logger = LogFactory.getLog(getClass());
40+
41+
private final Supplier<ConditionEvaluationReport> reportSupplier;
42+
43+
private final LogLevel logLevel;
44+
45+
ConditionEvaluationReportLogger(LogLevel logLevel, Supplier<ConditionEvaluationReport> reportSupplier) {
46+
Assert.isTrue(isInfoOrDebug(logLevel), "LogLevel must be INFO or DEBUG");
47+
this.logLevel = logLevel;
48+
this.reportSupplier = reportSupplier;
49+
}
50+
51+
private boolean isInfoOrDebug(LogLevel logLevelForReport) {
52+
return LogLevel.INFO.equals(logLevelForReport) || LogLevel.DEBUG.equals(logLevelForReport);
53+
}
54+
55+
void logReport(boolean isCrashReport) {
56+
ConditionEvaluationReport report = this.reportSupplier.get();
57+
if (report == null) {
58+
this.logger.info("Unable to provide the condition evaluation report");
59+
return;
60+
}
61+
if (!report.getConditionAndOutcomesBySource().isEmpty()) {
62+
if (this.logLevel.equals(LogLevel.INFO)) {
63+
if (this.logger.isInfoEnabled()) {
64+
this.logger.info(new ConditionEvaluationReportMessage(report));
65+
}
66+
else if (isCrashReport) {
67+
logMessage("info");
68+
}
69+
}
70+
else {
71+
if (this.logger.isDebugEnabled()) {
72+
this.logger.debug(new ConditionEvaluationReportMessage(report));
73+
}
74+
else if (isCrashReport) {
75+
logMessage("debug");
76+
}
77+
}
78+
}
79+
}
80+
81+
private void logMessage(String logLevel) {
82+
this.logger.info(String.format("%n%nError starting ApplicationContext. To display the "
83+
+ "condition evaluation report re-run your application with '" + logLevel + "' enabled."));
84+
}
85+
86+
}

spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/logging/ConditionEvaluationReportLoggingListener.java

Lines changed: 29 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,7 @@
1616

1717
package org.springframework.boot.autoconfigure.logging;
1818

19-
import org.apache.commons.logging.Log;
20-
import org.apache.commons.logging.LogFactory;
19+
import java.util.function.Supplier;
2120

2221
import org.springframework.boot.autoconfigure.condition.ConditionEvaluationReport;
2322
import org.springframework.boot.context.event.ApplicationFailedEvent;
@@ -51,12 +50,6 @@
5150
public class ConditionEvaluationReportLoggingListener
5251
implements ApplicationContextInitializer<ConfigurableApplicationContext> {
5352

54-
private final Log logger = LogFactory.getLog(getClass());
55-
56-
private ConfigurableApplicationContext applicationContext;
57-
58-
private ConditionEvaluationReport report;
59-
6053
private final LogLevel logLevelForReport;
6154

6255
public ConditionEvaluationReportLoggingListener() {
@@ -84,71 +77,36 @@ public static ConditionEvaluationReportLoggingListener forLogLevel(LogLevel logL
8477
return new ConditionEvaluationReportLoggingListener(logLevelForReport);
8578
}
8679

87-
public LogLevel getLogLevelForReport() {
88-
return this.logLevelForReport;
89-
}
90-
9180
@Override
9281
public void initialize(ConfigurableApplicationContext applicationContext) {
93-
this.applicationContext = applicationContext;
94-
applicationContext.addApplicationListener(new ConditionEvaluationReportListener());
95-
if (applicationContext instanceof GenericApplicationContext) {
96-
// Get the report early in case the context fails to load
97-
this.report = ConditionEvaluationReport.get(this.applicationContext.getBeanFactory());
98-
}
82+
applicationContext.addApplicationListener(new ConditionEvaluationReportListener(applicationContext));
9983
}
10084

101-
protected void onApplicationEvent(ApplicationEvent event) {
102-
ConfigurableApplicationContext initializerApplicationContext = this.applicationContext;
103-
if (event instanceof ContextRefreshedEvent contextRefreshedEvent) {
104-
if (contextRefreshedEvent.getApplicationContext() == initializerApplicationContext) {
105-
logAutoConfigurationReport();
106-
}
107-
}
108-
else if (event instanceof ApplicationFailedEvent applicationFailedEvent
109-
&& applicationFailedEvent.getApplicationContext() == initializerApplicationContext) {
110-
logAutoConfigurationReport(true);
111-
}
112-
}
85+
private final class ConditionEvaluationReportListener implements GenericApplicationListener {
11386

114-
private void logAutoConfigurationReport() {
115-
logAutoConfigurationReport(!this.applicationContext.isActive());
116-
}
87+
private final ConfigurableApplicationContext context;
11788

118-
public void logAutoConfigurationReport(boolean isCrashReport) {
119-
if (this.report == null) {
120-
if (this.applicationContext == null) {
121-
this.logger.info("Unable to provide the conditions report due to missing ApplicationContext");
122-
return;
123-
}
124-
this.report = ConditionEvaluationReport.get(this.applicationContext.getBeanFactory());
125-
}
126-
if (!this.report.getConditionAndOutcomesBySource().isEmpty()) {
127-
if (getLogLevelForReport().equals(LogLevel.INFO)) {
128-
if (this.logger.isInfoEnabled()) {
129-
this.logger.info(new ConditionEvaluationReportMessage(this.report));
130-
}
131-
else if (isCrashReport) {
132-
logMessage("info");
133-
}
89+
private final ConditionEvaluationReportLogger logger;
90+
91+
private ConditionEvaluationReportListener(ConfigurableApplicationContext context) {
92+
this.context = context;
93+
Supplier<ConditionEvaluationReport> reportSupplier;
94+
if (context instanceof GenericApplicationContext) {
95+
// Get the report early when the context allows early access to the bean
96+
// factory in case the context subsequently fails to load
97+
ConditionEvaluationReport report = getReport();
98+
reportSupplier = () -> report;
13499
}
135100
else {
136-
if (this.logger.isDebugEnabled()) {
137-
this.logger.debug(new ConditionEvaluationReportMessage(this.report));
138-
}
139-
else if (isCrashReport) {
140-
logMessage("debug");
141-
}
101+
reportSupplier = this::getReport;
142102
}
103+
this.logger = new ConditionEvaluationReportLogger(
104+
ConditionEvaluationReportLoggingListener.this.logLevelForReport, reportSupplier);
143105
}
144-
}
145106

146-
private void logMessage(String logLevel) {
147-
this.logger.info(String.format("%n%nError starting ApplicationContext. To display the "
148-
+ "conditions report re-run your application with '" + logLevel + "' enabled."));
149-
}
150-
151-
private class ConditionEvaluationReportListener implements GenericApplicationListener {
107+
private ConditionEvaluationReport getReport() {
108+
return ConditionEvaluationReport.get(this.context.getBeanFactory());
109+
}
152110

153111
@Override
154112
public int getOrder() {
@@ -172,7 +130,15 @@ public boolean supportsSourceType(Class<?> sourceType) {
172130

173131
@Override
174132
public void onApplicationEvent(ApplicationEvent event) {
175-
ConditionEvaluationReportLoggingListener.this.onApplicationEvent(event);
133+
if (event instanceof ContextRefreshedEvent contextRefreshedEvent) {
134+
if (contextRefreshedEvent.getApplicationContext() == this.context) {
135+
this.logger.logReport(false);
136+
}
137+
}
138+
else if (event instanceof ApplicationFailedEvent applicationFailedEvent
139+
&& applicationFailedEvent.getApplicationContext() == this.context) {
140+
this.logger.logReport(false);
141+
}
176142
}
177143

178144
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
/*
2+
* Copyright 2012-2022 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.boot.autoconfigure.logging;
18+
19+
import org.springframework.beans.factory.aot.BeanFactoryInitializationAotContribution;
20+
import org.springframework.beans.factory.aot.BeanFactoryInitializationAotProcessor;
21+
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
22+
import org.springframework.boot.autoconfigure.condition.ConditionEvaluationReport;
23+
import org.springframework.boot.logging.LogLevel;
24+
25+
/**
26+
* {@link BeanFactoryInitializationAotProcessor} that logs the
27+
* {@link ConditionEvaluationReport} during ahead-of-time processing.
28+
*
29+
* @author Andy Wilkinson
30+
*/
31+
class ConditionEvaluationReportLoggingProcessor implements BeanFactoryInitializationAotProcessor {
32+
33+
@Override
34+
public BeanFactoryInitializationAotContribution processAheadOfTime(ConfigurableListableBeanFactory beanFactory) {
35+
logConditionEvaluationReport(beanFactory);
36+
return null;
37+
}
38+
39+
private void logConditionEvaluationReport(ConfigurableListableBeanFactory beanFactory) {
40+
new ConditionEvaluationReportLogger(LogLevel.DEBUG, () -> ConditionEvaluationReport.get(beanFactory))
41+
.logReport(false);
42+
}
43+
44+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,5 @@
11
org.springframework.aot.hint.RuntimeHintsRegistrar=\
22
org.springframework.boot.autoconfigure.template.TemplateRuntimeHints
3+
4+
org.springframework.beans.factory.aot.BeanFactoryInitializationAotProcessor=\
5+
org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingProcessor
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
/*
2+
* Copyright 2012-2022 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.boot.autoconfigure.logging;
18+
19+
import java.util.Arrays;
20+
21+
import ch.qos.logback.classic.Level;
22+
import ch.qos.logback.classic.Logger;
23+
import ch.qos.logback.classic.LoggerContext;
24+
import org.junit.jupiter.api.Test;
25+
import org.junit.jupiter.api.extension.ExtendWith;
26+
import org.slf4j.LoggerFactory;
27+
28+
import org.springframework.boot.autoconfigure.condition.ConditionEvaluationReport;
29+
import org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListenerTests.Config;
30+
import org.springframework.boot.logging.LogLevel;
31+
import org.springframework.boot.test.system.CapturedOutput;
32+
import org.springframework.boot.test.system.OutputCaptureExtension;
33+
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
34+
35+
import static org.assertj.core.api.Assertions.assertThat;
36+
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
37+
38+
/**
39+
* Tests for {@link ConditionEvaluationReportLogger}.
40+
*
41+
* @author Andy Wilkinson
42+
*/
43+
@ExtendWith(OutputCaptureExtension.class)
44+
class ConditionEvaluationReportLoggerTests {
45+
46+
@Test
47+
void noErrorIfNotInitialized(CapturedOutput output) {
48+
new ConditionEvaluationReportLogger(LogLevel.INFO, () -> null).logReport(true);
49+
assertThat(output).contains("Unable to provide the condition evaluation report");
50+
}
51+
52+
@Test
53+
void supportsOnlyInfoAndDebugLogLevels() {
54+
assertThatIllegalArgumentException()
55+
.isThrownBy(() -> new ConditionEvaluationReportLogger(LogLevel.TRACE, () -> null))
56+
.withMessageContaining("LogLevel must be INFO or DEBUG");
57+
}
58+
59+
@Test
60+
void loggerWithInfoLevelShouldLogAtInfo(CapturedOutput output) {
61+
try (AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext()) {
62+
ConditionEvaluationReportLogger logger = new ConditionEvaluationReportLogger(LogLevel.INFO,
63+
() -> ConditionEvaluationReport.get(context.getBeanFactory()));
64+
context.register(Config.class);
65+
context.refresh();
66+
logger.logReport(false);
67+
assertThat(output).contains("CONDITIONS EVALUATION REPORT");
68+
}
69+
}
70+
71+
@Test
72+
void loggerWithDebugLevelShouldLogAtDebug(CapturedOutput output) {
73+
try (AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext()) {
74+
ConditionEvaluationReportLogger logger = new ConditionEvaluationReportLogger(LogLevel.DEBUG,
75+
() -> ConditionEvaluationReport.get(context.getBeanFactory()));
76+
context.register(Config.class);
77+
context.refresh();
78+
logger.logReport(false);
79+
assertThat(output).doesNotContain("CONDITIONS EVALUATION REPORT");
80+
withDebugLogging(() -> logger.logReport(false));
81+
assertThat(output).contains("CONDITIONS EVALUATION REPORT");
82+
}
83+
}
84+
85+
@Test
86+
void logsInfoOnErrorIfDebugDisabled(CapturedOutput output) {
87+
try (AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext()) {
88+
ConditionEvaluationReportLogger logger = new ConditionEvaluationReportLogger(LogLevel.DEBUG,
89+
() -> ConditionEvaluationReport.get(context.getBeanFactory()));
90+
context.register(Config.class);
91+
context.refresh();
92+
logger.logReport(true);
93+
assertThat(output).contains("Error starting ApplicationContext. To display the condition "
94+
+ "evaluation report re-run your application with 'debug' enabled.");
95+
}
96+
}
97+
98+
@Test
99+
void logsOutput(CapturedOutput output) {
100+
try (AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext()) {
101+
ConditionEvaluationReportLogger logger = new ConditionEvaluationReportLogger(LogLevel.DEBUG,
102+
() -> ConditionEvaluationReport.get(context.getBeanFactory()));
103+
context.register(Config.class);
104+
ConditionEvaluationReport.get(context.getBeanFactory()).recordExclusions(Arrays.asList("com.foo.Bar"));
105+
context.refresh();
106+
withDebugLogging(() -> logger.logReport(false));
107+
assertThat(output).contains("not a servlet web application (OnWebApplicationCondition)");
108+
}
109+
}
110+
111+
private void withDebugLogging(Runnable runnable) {
112+
Logger logger = ((LoggerContext) LoggerFactory.getILoggerFactory())
113+
.getLogger(ConditionEvaluationReportLogger.class);
114+
Level currentLevel = logger.getLevel();
115+
logger.setLevel(Level.DEBUG);
116+
try {
117+
runnable.run();
118+
}
119+
finally {
120+
logger.setLevel(currentLevel);
121+
}
122+
}
123+
124+
}

0 commit comments

Comments
 (0)