From 8e35f2ae928f22cf895e298c72d3c1a7fee247bc Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Wed, 5 Oct 2022 10:35:00 +0100 Subject: [PATCH 1/2] Separate report logging from context initialization and events See gh-32109 --- .../ConditionEvaluationReportLogger.java | 86 ++++++++++++ ...ditionEvaluationReportLoggingListener.java | 92 ++++--------- .../ConditionEvaluationReportLoggerTests.java | 124 ++++++++++++++++++ ...nEvaluationReportLoggingListenerTests.java | 62 +-------- src/checkstyle/checkstyle-suppressions.xml | 1 + 5 files changed, 244 insertions(+), 121 deletions(-) create mode 100644 spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/logging/ConditionEvaluationReportLogger.java create mode 100644 spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/logging/ConditionEvaluationReportLoggerTests.java diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/logging/ConditionEvaluationReportLogger.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/logging/ConditionEvaluationReportLogger.java new file mode 100644 index 0000000000..49a3036fd6 --- /dev/null +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/logging/ConditionEvaluationReportLogger.java @@ -0,0 +1,86 @@ +/* + * Copyright 2012-2022 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. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.boot.autoconfigure.logging; + +import java.util.function.Supplier; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import org.springframework.boot.autoconfigure.condition.ConditionEvaluationReport; +import org.springframework.boot.logging.LogLevel; +import org.springframework.util.Assert; + +/** + * Logs the {@link ConditionEvaluationReport}. + * + * @author Greg Turnquist + * @author Dave Syer + * @author Phillip Webb + * @author Andy Wilkinson + * @author Madhura Bhave + */ +class ConditionEvaluationReportLogger { + + private final Log logger = LogFactory.getLog(getClass()); + + private final Supplier reportSupplier; + + private final LogLevel logLevel; + + ConditionEvaluationReportLogger(LogLevel logLevel, Supplier reportSupplier) { + Assert.isTrue(isInfoOrDebug(logLevel), "LogLevel must be INFO or DEBUG"); + this.logLevel = logLevel; + this.reportSupplier = reportSupplier; + } + + private boolean isInfoOrDebug(LogLevel logLevelForReport) { + return LogLevel.INFO.equals(logLevelForReport) || LogLevel.DEBUG.equals(logLevelForReport); + } + + void logReport(boolean isCrashReport) { + ConditionEvaluationReport report = this.reportSupplier.get(); + if (report == null) { + this.logger.info("Unable to provide the condition evaluation report"); + return; + } + if (!report.getConditionAndOutcomesBySource().isEmpty()) { + if (this.logLevel.equals(LogLevel.INFO)) { + if (this.logger.isInfoEnabled()) { + this.logger.info(new ConditionEvaluationReportMessage(report)); + } + else if (isCrashReport) { + logMessage("info"); + } + } + else { + if (this.logger.isDebugEnabled()) { + this.logger.debug(new ConditionEvaluationReportMessage(report)); + } + else if (isCrashReport) { + logMessage("debug"); + } + } + } + } + + private void logMessage(String logLevel) { + this.logger.info(String.format("%n%nError starting ApplicationContext. To display the " + + "condition evaluation report re-run your application with '" + logLevel + "' enabled.")); + } + +} diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/logging/ConditionEvaluationReportLoggingListener.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/logging/ConditionEvaluationReportLoggingListener.java index a81b68a4b2..3dcbd5dafd 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/logging/ConditionEvaluationReportLoggingListener.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/logging/ConditionEvaluationReportLoggingListener.java @@ -16,8 +16,7 @@ package org.springframework.boot.autoconfigure.logging; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; +import java.util.function.Supplier; import org.springframework.boot.autoconfigure.condition.ConditionEvaluationReport; import org.springframework.boot.context.event.ApplicationFailedEvent; @@ -51,12 +50,6 @@ import org.springframework.util.Assert; public class ConditionEvaluationReportLoggingListener implements ApplicationContextInitializer { - private final Log logger = LogFactory.getLog(getClass()); - - private ConfigurableApplicationContext applicationContext; - - private ConditionEvaluationReport report; - private final LogLevel logLevelForReport; public ConditionEvaluationReportLoggingListener() { @@ -84,71 +77,36 @@ public class ConditionEvaluationReportLoggingListener return new ConditionEvaluationReportLoggingListener(logLevelForReport); } - public LogLevel getLogLevelForReport() { - return this.logLevelForReport; - } - @Override public void initialize(ConfigurableApplicationContext applicationContext) { - this.applicationContext = applicationContext; - applicationContext.addApplicationListener(new ConditionEvaluationReportListener()); - if (applicationContext instanceof GenericApplicationContext) { - // Get the report early in case the context fails to load - this.report = ConditionEvaluationReport.get(this.applicationContext.getBeanFactory()); - } + applicationContext.addApplicationListener(new ConditionEvaluationReportListener(applicationContext)); } - protected void onApplicationEvent(ApplicationEvent event) { - ConfigurableApplicationContext initializerApplicationContext = this.applicationContext; - if (event instanceof ContextRefreshedEvent contextRefreshedEvent) { - if (contextRefreshedEvent.getApplicationContext() == initializerApplicationContext) { - logAutoConfigurationReport(); - } - } - else if (event instanceof ApplicationFailedEvent applicationFailedEvent - && applicationFailedEvent.getApplicationContext() == initializerApplicationContext) { - logAutoConfigurationReport(true); - } - } + private final class ConditionEvaluationReportListener implements GenericApplicationListener { - private void logAutoConfigurationReport() { - logAutoConfigurationReport(!this.applicationContext.isActive()); - } + private final ConfigurableApplicationContext context; - public void logAutoConfigurationReport(boolean isCrashReport) { - if (this.report == null) { - if (this.applicationContext == null) { - this.logger.info("Unable to provide the conditions report due to missing ApplicationContext"); - return; - } - this.report = ConditionEvaluationReport.get(this.applicationContext.getBeanFactory()); - } - if (!this.report.getConditionAndOutcomesBySource().isEmpty()) { - if (getLogLevelForReport().equals(LogLevel.INFO)) { - if (this.logger.isInfoEnabled()) { - this.logger.info(new ConditionEvaluationReportMessage(this.report)); - } - else if (isCrashReport) { - logMessage("info"); - } + private final ConditionEvaluationReportLogger logger; + + private ConditionEvaluationReportListener(ConfigurableApplicationContext context) { + this.context = context; + Supplier reportSupplier; + if (context instanceof GenericApplicationContext) { + // Get the report early when the context allows early access to the bean + // factory in case the context subsequently fails to load + ConditionEvaluationReport report = getReport(); + reportSupplier = () -> report; } else { - if (this.logger.isDebugEnabled()) { - this.logger.debug(new ConditionEvaluationReportMessage(this.report)); - } - else if (isCrashReport) { - logMessage("debug"); - } + reportSupplier = this::getReport; } + this.logger = new ConditionEvaluationReportLogger( + ConditionEvaluationReportLoggingListener.this.logLevelForReport, reportSupplier); } - } - private void logMessage(String logLevel) { - this.logger.info(String.format("%n%nError starting ApplicationContext. To display the " - + "conditions report re-run your application with '" + logLevel + "' enabled.")); - } - - private class ConditionEvaluationReportListener implements GenericApplicationListener { + private ConditionEvaluationReport getReport() { + return ConditionEvaluationReport.get(this.context.getBeanFactory()); + } @Override public int getOrder() { @@ -172,7 +130,15 @@ public class ConditionEvaluationReportLoggingListener @Override public void onApplicationEvent(ApplicationEvent event) { - ConditionEvaluationReportLoggingListener.this.onApplicationEvent(event); + if (event instanceof ContextRefreshedEvent contextRefreshedEvent) { + if (contextRefreshedEvent.getApplicationContext() == this.context) { + this.logger.logReport(false); + } + } + else if (event instanceof ApplicationFailedEvent applicationFailedEvent + && applicationFailedEvent.getApplicationContext() == this.context) { + this.logger.logReport(false); + } } } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/logging/ConditionEvaluationReportLoggerTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/logging/ConditionEvaluationReportLoggerTests.java new file mode 100644 index 0000000000..690e74584b --- /dev/null +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/logging/ConditionEvaluationReportLoggerTests.java @@ -0,0 +1,124 @@ +/* + * Copyright 2012-2022 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. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.boot.autoconfigure.logging; + +import java.util.Arrays; + +import ch.qos.logback.classic.Level; +import ch.qos.logback.classic.Logger; +import ch.qos.logback.classic.LoggerContext; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.slf4j.LoggerFactory; + +import org.springframework.boot.autoconfigure.condition.ConditionEvaluationReport; +import org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListenerTests.Config; +import org.springframework.boot.logging.LogLevel; +import org.springframework.boot.test.system.CapturedOutput; +import org.springframework.boot.test.system.OutputCaptureExtension; +import org.springframework.context.annotation.AnnotationConfigApplicationContext; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; + +/** + * Tests for {@link ConditionEvaluationReportLogger}. + * + * @author Andy Wilkinson + */ +@ExtendWith(OutputCaptureExtension.class) +class ConditionEvaluationReportLoggerTests { + + @Test + void noErrorIfNotInitialized(CapturedOutput output) { + new ConditionEvaluationReportLogger(LogLevel.INFO, () -> null).logReport(true); + assertThat(output).contains("Unable to provide the condition evaluation report"); + } + + @Test + void supportsOnlyInfoAndDebugLogLevels() { + assertThatIllegalArgumentException() + .isThrownBy(() -> new ConditionEvaluationReportLogger(LogLevel.TRACE, () -> null)) + .withMessageContaining("LogLevel must be INFO or DEBUG"); + } + + @Test + void loggerWithInfoLevelShouldLogAtInfo(CapturedOutput output) { + try (AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext()) { + ConditionEvaluationReportLogger logger = new ConditionEvaluationReportLogger(LogLevel.INFO, + () -> ConditionEvaluationReport.get(context.getBeanFactory())); + context.register(Config.class); + context.refresh(); + logger.logReport(false); + assertThat(output).contains("CONDITIONS EVALUATION REPORT"); + } + } + + @Test + void loggerWithDebugLevelShouldLogAtDebug(CapturedOutput output) { + try (AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext()) { + ConditionEvaluationReportLogger logger = new ConditionEvaluationReportLogger(LogLevel.DEBUG, + () -> ConditionEvaluationReport.get(context.getBeanFactory())); + context.register(Config.class); + context.refresh(); + logger.logReport(false); + assertThat(output).doesNotContain("CONDITIONS EVALUATION REPORT"); + withDebugLogging(() -> logger.logReport(false)); + assertThat(output).contains("CONDITIONS EVALUATION REPORT"); + } + } + + @Test + void logsInfoOnErrorIfDebugDisabled(CapturedOutput output) { + try (AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext()) { + ConditionEvaluationReportLogger logger = new ConditionEvaluationReportLogger(LogLevel.DEBUG, + () -> ConditionEvaluationReport.get(context.getBeanFactory())); + context.register(Config.class); + context.refresh(); + logger.logReport(true); + assertThat(output).contains("Error starting ApplicationContext. To display the condition " + + "evaluation report re-run your application with 'debug' enabled."); + } + } + + @Test + void logsOutput(CapturedOutput output) { + try (AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext()) { + ConditionEvaluationReportLogger logger = new ConditionEvaluationReportLogger(LogLevel.DEBUG, + () -> ConditionEvaluationReport.get(context.getBeanFactory())); + context.register(Config.class); + ConditionEvaluationReport.get(context.getBeanFactory()).recordExclusions(Arrays.asList("com.foo.Bar")); + context.refresh(); + withDebugLogging(() -> logger.logReport(false)); + assertThat(output).contains("not a servlet web application (OnWebApplicationCondition)"); + } + } + + private void withDebugLogging(Runnable runnable) { + Logger logger = ((LoggerContext) LoggerFactory.getILoggerFactory()) + .getLogger(ConditionEvaluationReportLogger.class); + Level currentLevel = logger.getLevel(); + logger.setLevel(Level.DEBUG); + try { + runnable.run(); + } + finally { + logger.setLevel(currentLevel); + } + } + +} diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/logging/ConditionEvaluationReportLoggingListenerTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/logging/ConditionEvaluationReportLoggingListenerTests.java index 3f897e93ea..7bc8fb2db7 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/logging/ConditionEvaluationReportLoggingListenerTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/logging/ConditionEvaluationReportLoggingListenerTests.java @@ -16,8 +16,6 @@ package org.springframework.boot.autoconfigure.logging; -import java.util.Arrays; - import ch.qos.logback.classic.Level; import ch.qos.logback.classic.Logger; import ch.qos.logback.classic.LoggerContext; @@ -31,7 +29,6 @@ import org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoCon import org.springframework.boot.autoconfigure.http.HttpMessageConvertersAutoConfiguration; import org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration; import org.springframework.boot.context.event.ApplicationFailedEvent; -import org.springframework.boot.logging.LogLevel; import org.springframework.boot.test.system.CapturedOutput; import org.springframework.boot.test.system.OutputCaptureExtension; import org.springframework.boot.web.servlet.context.AnnotationConfigServletWebApplicationContext; @@ -39,12 +36,10 @@ import org.springframework.context.annotation.AnnotationConfigApplicationContext import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; -import org.springframework.context.event.ContextRefreshedEvent; import org.springframework.mock.web.MockServletContext; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatExceptionOfType; -import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; /** * Tests for {@link ConditionEvaluationReportLoggingListener}. @@ -63,44 +58,21 @@ class ConditionEvaluationReportLoggingListenerTests { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); this.initializer.initialize(context); context.register(Config.class); - context.refresh(); - withDebugLogging(() -> this.initializer.onApplicationEvent(new ContextRefreshedEvent(context))); + withDebugLogging(() -> context.refresh()); assertThat(output).contains("CONDITIONS EVALUATION REPORT"); } @Test - void logsDebugOnError(CapturedOutput output) { + void logsDebugOnApplicationFailedEvent(CapturedOutput output) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); this.initializer.initialize(context); context.register(ErrorConfig.class); assertThatExceptionOfType(Exception.class).isThrownBy(context::refresh) - .satisfies((ex) -> withDebugLogging(() -> this.initializer.onApplicationEvent( + .satisfies((ex) -> withDebugLogging(() -> context.publishEvent( new ApplicationFailedEvent(new SpringApplication(), new String[0], context, ex)))); assertThat(output).contains("CONDITIONS EVALUATION REPORT"); } - @Test - void logsInfoOnErrorIfDebugDisabled(CapturedOutput output) { - AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); - this.initializer.initialize(context); - context.register(ErrorConfig.class); - assertThatExceptionOfType(Exception.class).isThrownBy(context::refresh).satisfies((ex) -> this.initializer - .onApplicationEvent(new ApplicationFailedEvent(new SpringApplication(), new String[0], context, ex))); - assertThat(output).contains("Error starting ApplicationContext. To display the conditions report re-run" - + " your application with 'debug' enabled."); - } - - @Test - void logsOutput(CapturedOutput output) { - AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); - this.initializer.initialize(context); - context.register(Config.class); - ConditionEvaluationReport.get(context.getBeanFactory()).recordExclusions(Arrays.asList("com.foo.Bar")); - context.refresh(); - withDebugLogging(() -> this.initializer.onApplicationEvent(new ContextRefreshedEvent(context))); - assertThat(output).contains("not a servlet web application (OnWebApplicationCondition)"); - } - @Test void canBeUsedInApplicationContext() { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); @@ -120,35 +92,9 @@ class ConditionEvaluationReportLoggingListenerTests { assertThat(context.getBean(ConditionEvaluationReport.class)).isNotNull(); } - @Test - void listenerWithInfoLevelShouldLogAtInfo(CapturedOutput output) { - AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); - ConditionEvaluationReportLoggingListener initializer = ConditionEvaluationReportLoggingListener - .forLogLevel(LogLevel.INFO); - initializer.initialize(context); - context.register(Config.class); - context.refresh(); - initializer.onApplicationEvent(new ContextRefreshedEvent(context)); - assertThat(output).contains("CONDITIONS EVALUATION REPORT"); - } - - @Test - void listenerSupportsOnlyInfoAndDebug() { - assertThatIllegalArgumentException() - .isThrownBy(() -> ConditionEvaluationReportLoggingListener.forLogLevel(LogLevel.TRACE)) - .withMessageContaining("LogLevel must be INFO or DEBUG"); - } - - @Test - void noErrorIfNotInitialized(CapturedOutput output) { - this.initializer.onApplicationEvent(new ApplicationFailedEvent(new SpringApplication(), new String[0], null, - new RuntimeException("Planned"))); - assertThat(output).contains("Unable to provide the conditions report"); - } - private void withDebugLogging(Runnable runnable) { Logger logger = ((LoggerContext) LoggerFactory.getILoggerFactory()) - .getLogger(ConditionEvaluationReportLoggingListener.class); + .getLogger(ConditionEvaluationReportLogger.class); Level currentLevel = logger.getLevel(); logger.setLevel(Level.DEBUG); try { diff --git a/src/checkstyle/checkstyle-suppressions.xml b/src/checkstyle/checkstyle-suppressions.xml index e75706316c..219a950738 100644 --- a/src/checkstyle/checkstyle-suppressions.xml +++ b/src/checkstyle/checkstyle-suppressions.xml @@ -3,6 +3,7 @@ "-//Checkstyle//DTD SuppressionFilter Configuration 1.2//EN" "https://checkstyle.org/dtds/suppressions_1_2.dtd"> + From 3eb3d79104017c3937d20f4baccc077b0c7b935c Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Fri, 7 Oct 2022 14:57:56 +0100 Subject: [PATCH 2/2] Log condition evaluation report during AOT processing Closes gh-32109 --- ...itionEvaluationReportLoggingProcessor.java | 44 ++++++++++++ .../resources/META-INF/spring/aot.factories | 3 + ...EvaluationReportLoggingProcessorTests.java | 69 +++++++++++++++++++ src/checkstyle/checkstyle-suppressions.xml | 1 + 4 files changed, 117 insertions(+) create mode 100644 spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/logging/ConditionEvaluationReportLoggingProcessor.java create mode 100644 spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/logging/ConditionEvaluationReportLoggingProcessorTests.java diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/logging/ConditionEvaluationReportLoggingProcessor.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/logging/ConditionEvaluationReportLoggingProcessor.java new file mode 100644 index 0000000000..f6c62634b2 --- /dev/null +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/logging/ConditionEvaluationReportLoggingProcessor.java @@ -0,0 +1,44 @@ +/* + * Copyright 2012-2022 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. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.boot.autoconfigure.logging; + +import org.springframework.beans.factory.aot.BeanFactoryInitializationAotContribution; +import org.springframework.beans.factory.aot.BeanFactoryInitializationAotProcessor; +import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; +import org.springframework.boot.autoconfigure.condition.ConditionEvaluationReport; +import org.springframework.boot.logging.LogLevel; + +/** + * {@link BeanFactoryInitializationAotProcessor} that logs the + * {@link ConditionEvaluationReport} during ahead-of-time processing. + * + * @author Andy Wilkinson + */ +class ConditionEvaluationReportLoggingProcessor implements BeanFactoryInitializationAotProcessor { + + @Override + public BeanFactoryInitializationAotContribution processAheadOfTime(ConfigurableListableBeanFactory beanFactory) { + logConditionEvaluationReport(beanFactory); + return null; + } + + private void logConditionEvaluationReport(ConfigurableListableBeanFactory beanFactory) { + new ConditionEvaluationReportLogger(LogLevel.DEBUG, () -> ConditionEvaluationReport.get(beanFactory)) + .logReport(false); + } + +} diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/resources/META-INF/spring/aot.factories b/spring-boot-project/spring-boot-autoconfigure/src/main/resources/META-INF/spring/aot.factories index 2db0bcfa76..064b3107ba 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/resources/META-INF/spring/aot.factories +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/resources/META-INF/spring/aot.factories @@ -1,2 +1,5 @@ org.springframework.aot.hint.RuntimeHintsRegistrar=\ org.springframework.boot.autoconfigure.template.TemplateRuntimeHints + +org.springframework.beans.factory.aot.BeanFactoryInitializationAotProcessor=\ +org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingProcessor diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/logging/ConditionEvaluationReportLoggingProcessorTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/logging/ConditionEvaluationReportLoggingProcessorTests.java new file mode 100644 index 0000000000..38c647e490 --- /dev/null +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/logging/ConditionEvaluationReportLoggingProcessorTests.java @@ -0,0 +1,69 @@ +/* + * Copyright 2012-2022 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. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.boot.autoconfigure.logging; + +import ch.qos.logback.classic.Level; +import ch.qos.logback.classic.Logger; +import ch.qos.logback.classic.LoggerContext; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.slf4j.LoggerFactory; + +import org.springframework.beans.factory.support.DefaultListableBeanFactory; +import org.springframework.boot.autoconfigure.condition.ConditionEvaluationReport; +import org.springframework.boot.autoconfigure.condition.ConditionOutcome; +import org.springframework.boot.test.system.CapturedOutput; +import org.springframework.boot.test.system.OutputCaptureExtension; +import org.springframework.context.annotation.Condition; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; + +/** + * Tests for {@link ConditionEvaluationReportLoggingProcessor}. + * + * @author Andy Wilkinson + */ +@ExtendWith(OutputCaptureExtension.class) +class ConditionEvaluationReportLoggingProcessorTests { + + @Test + void logsDebugOnProcessAheadOfTime(CapturedOutput output) { + DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory(); + ConditionEvaluationReport.get(beanFactory).recordConditionEvaluation("test", mock(Condition.class), + ConditionOutcome.match()); + ConditionEvaluationReportLoggingProcessor processor = new ConditionEvaluationReportLoggingProcessor(); + processor.processAheadOfTime(beanFactory); + assertThat(output).doesNotContain("CONDITIONS EVALUATION REPORT"); + withDebugLogging(() -> processor.processAheadOfTime(beanFactory)); + assertThat(output).contains("CONDITIONS EVALUATION REPORT"); + } + + private void withDebugLogging(Runnable runnable) { + Logger logger = ((LoggerContext) LoggerFactory.getILoggerFactory()) + .getLogger(ConditionEvaluationReportLogger.class); + Level currentLevel = logger.getLevel(); + logger.setLevel(Level.DEBUG); + try { + runnable.run(); + } + finally { + logger.setLevel(currentLevel); + } + } + +} diff --git a/src/checkstyle/checkstyle-suppressions.xml b/src/checkstyle/checkstyle-suppressions.xml index 219a950738..d4dfb63f3b 100644 --- a/src/checkstyle/checkstyle-suppressions.xml +++ b/src/checkstyle/checkstyle-suppressions.xml @@ -5,6 +5,7 @@ +