diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/startup/StartupTimeMetricsAutoConfiguration.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/startup/StartupTimeMetricsListenerAutoConfiguration.java similarity index 89% rename from spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/startup/StartupTimeMetricsAutoConfiguration.java rename to spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/startup/StartupTimeMetricsListenerAutoConfiguration.java index e30fe37790..9e678e191b 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/startup/StartupTimeMetricsAutoConfiguration.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/startup/StartupTimeMetricsListenerAutoConfiguration.java @@ -20,7 +20,7 @@ import io.micrometer.core.instrument.MeterRegistry; import org.springframework.boot.actuate.autoconfigure.metrics.CompositeMeterRegistryAutoConfiguration; import org.springframework.boot.actuate.autoconfigure.metrics.MetricsAutoConfiguration; -import org.springframework.boot.actuate.metrics.startup.StartupTimeMetrics; +import org.springframework.boot.actuate.metrics.startup.StartupTimeMetricsListener; import org.springframework.boot.autoconfigure.AutoConfigureAfter; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; @@ -39,12 +39,12 @@ import org.springframework.context.annotation.Configuration; @AutoConfigureAfter({ MetricsAutoConfiguration.class, CompositeMeterRegistryAutoConfiguration.class }) @ConditionalOnClass(MeterRegistry.class) @ConditionalOnBean(MeterRegistry.class) -public class StartupTimeMetricsAutoConfiguration { +public class StartupTimeMetricsListenerAutoConfiguration { @Bean @ConditionalOnMissingBean - public StartupTimeMetrics startupTimeMetrics(MeterRegistry meterRegistry) { - return new StartupTimeMetrics(meterRegistry); + public StartupTimeMetricsListener startupTimeMetrics(MeterRegistry meterRegistry) { + return new StartupTimeMetricsListener(meterRegistry); } } diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/resources/META-INF/spring.factories b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/resources/META-INF/spring.factories index 3cec6d499b..dd6eeb9497 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/resources/META-INF/spring.factories +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/resources/META-INF/spring.factories @@ -75,7 +75,7 @@ org.springframework.boot.actuate.autoconfigure.metrics.mongo.MongoMetricsAutoCon org.springframework.boot.actuate.autoconfigure.metrics.orm.jpa.HibernateMetricsAutoConfiguration,\ org.springframework.boot.actuate.autoconfigure.metrics.r2dbc.ConnectionPoolMetricsAutoConfiguration,\ org.springframework.boot.actuate.autoconfigure.metrics.redis.LettuceMetricsAutoConfiguration,\ -org.springframework.boot.actuate.autoconfigure.metrics.startup.StartupTimeMetricsAutoConfiguration,\ +org.springframework.boot.actuate.autoconfigure.metrics.startup.StartupTimeMetricsListenerAutoConfiguration,\ org.springframework.boot.actuate.autoconfigure.metrics.task.TaskExecutorMetricsAutoConfiguration,\ org.springframework.boot.actuate.autoconfigure.metrics.web.client.HttpClientMetricsAutoConfiguration,\ org.springframework.boot.actuate.autoconfigure.metrics.web.jetty.JettyMetricsAutoConfiguration,\ diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/startup/StartupTimeMetricsAutoConfigurationTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/startup/StartupTimeMetricsListenerAutoConfigurationTests.java similarity index 88% rename from spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/startup/StartupTimeMetricsAutoConfigurationTests.java rename to spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/startup/StartupTimeMetricsListenerAutoConfigurationTests.java index 2dec9ec603..dae7409542 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/startup/StartupTimeMetricsAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/startup/StartupTimeMetricsListenerAutoConfigurationTests.java @@ -25,7 +25,7 @@ import org.junit.jupiter.api.Test; import org.springframework.boot.SpringApplication; import org.springframework.boot.actuate.autoconfigure.metrics.test.MetricsRun; -import org.springframework.boot.actuate.metrics.startup.StartupTimeMetrics; +import org.springframework.boot.actuate.metrics.startup.StartupTimeMetricsListener; import org.springframework.boot.autoconfigure.AutoConfigurations; import org.springframework.boot.context.event.ApplicationReadyEvent; import org.springframework.boot.context.event.ApplicationStartedEvent; @@ -35,20 +35,20 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.mock; /** - * Tests for {@link StartupTimeMetricsAutoConfiguration}. + * Tests for {@link StartupTimeMetricsListenerAutoConfiguration}. * * @author Chris Bono * @author Stephane Nicoll */ -class StartupTimeMetricsAutoConfigurationTests { +class StartupTimeMetricsListenerAutoConfigurationTests { private final ApplicationContextRunner contextRunner = new ApplicationContextRunner().with(MetricsRun.simple()) - .withConfiguration(AutoConfigurations.of(StartupTimeMetricsAutoConfiguration.class)); + .withConfiguration(AutoConfigurations.of(StartupTimeMetricsListenerAutoConfiguration.class)); @Test void startupTimeMetricsAreRecorded() { this.contextRunner.run((context) -> { - assertThat(context).hasSingleBean(StartupTimeMetrics.class); + assertThat(context).hasSingleBean(StartupTimeMetricsListener.class); SimpleMeterRegistry registry = context.getBean(SimpleMeterRegistry.class); context.publishEvent(new ApplicationStartedEvent(new SpringApplication(), null, context.getSourceApplicationContext(), Duration.ofMillis(1500))); @@ -80,8 +80,9 @@ class StartupTimeMetricsAutoConfigurationTests { @Test void customStartupTimeMetricsAreRespected() { this.contextRunner - .withBean("customStartupTimeMetrics", StartupTimeMetrics.class, () -> mock(StartupTimeMetrics.class)) - .run((context) -> assertThat(context).hasSingleBean(StartupTimeMetrics.class) + .withBean("customStartupTimeMetrics", StartupTimeMetricsListener.class, + () -> mock(StartupTimeMetricsListener.class)) + .run((context) -> assertThat(context).hasSingleBean(StartupTimeMetricsListener.class) .hasBean("customStartupTimeMetrics")); } diff --git a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/startup/StartupTimeMetrics.java b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/startup/StartupTimeMetricsListener.java similarity index 63% rename from spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/startup/StartupTimeMetrics.java rename to spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/startup/StartupTimeMetricsListener.java index d4e8f78032..90d682b34a 100644 --- a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/startup/StartupTimeMetrics.java +++ b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/startup/StartupTimeMetricsListener.java @@ -36,9 +36,10 @@ import org.springframework.context.event.SmartApplicationListener; * {@link ApplicationReadyEvent}. * * @author Chris Bono + * @author Phillip Webb * @since 2.6.0 */ -public class StartupTimeMetrics implements SmartApplicationListener { +public class StartupTimeMetricsListener implements SmartApplicationListener { /** * The default name to use for the application started time metric. @@ -52,11 +53,11 @@ public class StartupTimeMetrics implements SmartApplicationListener { private final MeterRegistry meterRegistry; - private final String applicationStartedTimeMetricName; + private final String startedTimeMetricName; - private final String applicationReadyTimeMetricName; + private final String readyTimeMetricName; - private final Iterable tags; + private final Tags tags; /** * Create a new instance using default metric names. @@ -64,26 +65,25 @@ public class StartupTimeMetrics implements SmartApplicationListener { * @see #APPLICATION_STARTED_TIME_METRIC_NAME * @see #APPLICATION_READY_TIME_METRIC_NAME */ - public StartupTimeMetrics(MeterRegistry meterRegistry) { - this(meterRegistry, Collections.emptyList(), APPLICATION_STARTED_TIME_METRIC_NAME, - APPLICATION_READY_TIME_METRIC_NAME); + public StartupTimeMetricsListener(MeterRegistry meterRegistry) { + this(meterRegistry, APPLICATION_STARTED_TIME_METRIC_NAME, APPLICATION_READY_TIME_METRIC_NAME, + Collections.emptyList()); } /** * Create a new instance using the specified options. * @param meterRegistry the registry to use + * @param startedTimeMetricName the name to use for the application started time + * metric + * @param readyTimeMetricName the name to use for the application ready time metric * @param tags the tags to associate to application startup metrics - * @param applicationStartedTimeMetricName the name to use for the application started - * time metric - * @param applicationReadyTimeMetricName the name to use for the application ready - * time metric */ - public StartupTimeMetrics(MeterRegistry meterRegistry, Iterable tags, String applicationStartedTimeMetricName, - String applicationReadyTimeMetricName) { + public StartupTimeMetricsListener(MeterRegistry meterRegistry, String startedTimeMetricName, + String readyTimeMetricName, Iterable tags) { this.meterRegistry = meterRegistry; - this.tags = (tags != null) ? tags : Collections.emptyList(); - this.applicationStartedTimeMetricName = applicationStartedTimeMetricName; - this.applicationReadyTimeMetricName = applicationReadyTimeMetricName; + this.startedTimeMetricName = startedTimeMetricName; + this.readyTimeMetricName = readyTimeMetricName; + this.tags = Tags.of(tags); } @Override @@ -103,33 +103,27 @@ public class StartupTimeMetrics implements SmartApplicationListener { } private void onApplicationStarted(ApplicationStartedEvent event) { - if (event.getStartedTime() == null) { - return; - } - registerGauge(this.applicationStartedTimeMetricName, "Time taken (ms) to start the application", - event.getStartedTime(), createTagsFrom(event.getSpringApplication())); + registerGauge(this.startedTimeMetricName, "Time taken (ms) to start the application", event.getTimeTaken(), + event.getSpringApplication()); } private void onApplicationReady(ApplicationReadyEvent event) { - if (event.getReadyTime() == null) { - return; - } - registerGauge(this.applicationReadyTimeMetricName, - "Time taken (ms) for the application to be ready to service requests", event.getReadyTime(), - createTagsFrom(event.getSpringApplication())); + registerGauge(this.readyTimeMetricName, "Time taken (ms) for the application to be ready to service requests", + event.getTimeTaken(), event.getSpringApplication()); } - private void registerGauge(String metricName, String description, Duration time, Iterable tags) { - TimeGauge.builder(metricName, time::toMillis, TimeUnit.MILLISECONDS).tags(tags).description(description) - .register(this.meterRegistry); + private void registerGauge(String name, String description, Duration timeTaken, + SpringApplication springApplication) { + if (timeTaken != null) { + Iterable tags = createTagsFrom(springApplication); + TimeGauge.builder(name, timeTaken::toMillis, TimeUnit.MILLISECONDS).tags(tags).description(description) + .register(this.meterRegistry); + } } private Iterable createTagsFrom(SpringApplication springApplication) { Class mainClass = springApplication.getMainApplicationClass(); - if (mainClass == null) { - return this.tags; - } - return Tags.concat(this.tags, "main-application-class", mainClass.getName()); + return (mainClass != null) ? this.tags.and("main-application-class", mainClass.getName()) : this.tags; } } diff --git a/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/startup/StartupTimeMetricsTests.java b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/startup/StartupTimeMetricsListenerTests.java similarity index 81% rename from spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/startup/StartupTimeMetricsTests.java rename to spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/startup/StartupTimeMetricsListenerTests.java index e17eae1fc3..c713930302 100644 --- a/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/startup/StartupTimeMetricsTests.java +++ b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/startup/StartupTimeMetricsListenerTests.java @@ -35,26 +35,26 @@ import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; /** - * Tests for {@link StartupTimeMetrics}. + * Tests for {@link StartupTimeMetricsListener}. * * @author Chris Bono */ -class StartupTimeMetricsTests { +class StartupTimeMetricsListenerTests { private MeterRegistry registry; - private StartupTimeMetrics metrics; + private StartupTimeMetricsListener listener; @BeforeEach void setup() { this.registry = new SimpleMeterRegistry(); - this.metrics = new StartupTimeMetrics(this.registry); + this.listener = new StartupTimeMetricsListener(this.registry); } @Test void metricsRecordedWithoutCustomTags() { - this.metrics.onApplicationEvent(applicationStartedEvent(2000L)); - this.metrics.onApplicationEvent(applicationReadyEvent(2200L)); + this.listener.onApplicationEvent(applicationStartedEvent(2000L)); + this.listener.onApplicationEvent(applicationReadyEvent(2200L)); assertMetricExistsWithValue("application.started.time", 2000L); assertMetricExistsWithValue("application.ready.time", 2200L); } @@ -62,9 +62,9 @@ class StartupTimeMetricsTests { @Test void metricsRecordedWithCustomTagsAndMetricNames() { Tags tags = Tags.of("foo", "bar"); - this.metrics = new StartupTimeMetrics(this.registry, tags, "m1", "m2"); - this.metrics.onApplicationEvent(applicationStartedEvent(1000L)); - this.metrics.onApplicationEvent(applicationReadyEvent(1050L)); + this.listener = new StartupTimeMetricsListener(this.registry, "m1", "m2", tags); + this.listener.onApplicationEvent(applicationStartedEvent(1000L)); + this.listener.onApplicationEvent(applicationReadyEvent(1050L)); assertMetricExistsWithCustomTagsAndValue("m1", tags, 1000L); assertMetricExistsWithCustomTagsAndValue("m2", tags, 1050L); } @@ -72,7 +72,7 @@ class StartupTimeMetricsTests { @Test void metricRecordedWithoutMainAppClassTag() { SpringApplication application = mock(SpringApplication.class); - this.metrics.onApplicationEvent(new ApplicationStartedEvent(application, null, null, Duration.ofSeconds(2))); + this.listener.onApplicationEvent(new ApplicationStartedEvent(application, null, null, Duration.ofSeconds(2))); TimeGauge applicationStartedGague = this.registry.find("application.started.time").timeGauge(); assertThat(applicationStartedGague).isNotNull(); assertThat(applicationStartedGague.getId().getTags()).isEmpty(); @@ -82,8 +82,8 @@ class StartupTimeMetricsTests { void metricRecordedWithoutMainAppClassTagAndAdditionalTags() { SpringApplication application = mock(SpringApplication.class); Tags tags = Tags.of("foo", "bar"); - this.metrics = new StartupTimeMetrics(this.registry, tags, "started", "ready"); - this.metrics.onApplicationEvent(new ApplicationReadyEvent(application, null, null, Duration.ofSeconds(2))); + this.listener = new StartupTimeMetricsListener(this.registry, "started", "ready", tags); + this.listener.onApplicationEvent(new ApplicationReadyEvent(application, null, null, Duration.ofSeconds(2))); TimeGauge applicationReadyGague = this.registry.find("ready").timeGauge(); assertThat(applicationReadyGague).isNotNull(); assertThat(applicationReadyGague.getId().getTags()).containsExactlyElementsOf(tags); @@ -91,8 +91,8 @@ class StartupTimeMetricsTests { @Test void metricsNotRecordedWhenStartupTimeNotAvailable() { - this.metrics.onApplicationEvent(applicationStartedEvent(null)); - this.metrics.onApplicationEvent(applicationReadyEvent(null)); + this.listener.onApplicationEvent(applicationStartedEvent(null)); + this.listener.onApplicationEvent(applicationReadyEvent(null)); assertThat(this.registry.find("application.started.time").timeGauge()).isNull(); assertThat(this.registry.find("application.ready.time").timeGauge()).isNull(); } diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/SpringApplication.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/SpringApplication.java index 63be21dac1..d71e17678a 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/SpringApplication.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/SpringApplication.java @@ -78,7 +78,6 @@ import org.springframework.util.ClassUtils; import org.springframework.util.CollectionUtils; import org.springframework.util.ObjectUtils; import org.springframework.util.ReflectionUtils; -import org.springframework.util.StopWatch; import org.springframework.util.StringUtils; /** @@ -286,8 +285,7 @@ public class SpringApplication { * @return a running {@link ApplicationContext} */ public ConfigurableApplicationContext run(String... args) { - StopWatch stopWatch = new StopWatch(); - stopWatch.start(); + long startTime = System.nanoTime(); DefaultBootstrapContext bootstrapContext = createBootstrapContext(); ConfigurableApplicationContext context = null; configureHeadlessProperty(); @@ -303,23 +301,20 @@ public class SpringApplication { prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner); refreshContext(context); afterRefresh(context, applicationArguments); - stopWatch.stop(); - Duration startedTime = Duration.ofMillis(stopWatch.getTotalTimeMillis()); - stopWatch.start(); + Duration timeTakeToStartup = Duration.ofNanos(System.nanoTime() - startTime); if (this.logStartupInfo) { - new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), startedTime); + new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), timeTakeToStartup); } - listeners.started(context, startedTime); + listeners.started(context, timeTakeToStartup); callRunners(context, applicationArguments); } catch (Throwable ex) { handleRunFailure(context, ex, listeners); throw new IllegalStateException(ex); } - try { - stopWatch.stop(); - listeners.running(context, Duration.ofMillis(stopWatch.getTotalTimeMillis())); + Duration timeTakenToReady = Duration.ofNanos(System.nanoTime() - startTime); + listeners.ready(context, timeTakenToReady); } catch (Throwable ex) { handleRunFailure(context, ex, null); diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/SpringApplicationRunListener.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/SpringApplicationRunListener.java index c37659d256..d12ff10172 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/SpringApplicationRunListener.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/SpringApplicationRunListener.java @@ -77,13 +77,11 @@ public interface SpringApplicationRunListener { * {@link CommandLineRunner CommandLineRunners} and {@link ApplicationRunner * ApplicationRunners} have not been called. * @param context the application context. - * @since 2.0.0 - * @deprecated since 2.6.0 for removal in 2.8.0 in favour of - * {@link #started(ConfigurableApplicationContext, Duration)} + * @param timeTaken the time taken to start the application or {@code null} if unknown + * @since 2.6.0 */ - @Deprecated - default void started(ConfigurableApplicationContext context) { - started(context, null); + default void started(ConfigurableApplicationContext context, Duration timeTaken) { + started(context); } /** @@ -91,12 +89,12 @@ public interface SpringApplicationRunListener { * {@link CommandLineRunner CommandLineRunners} and {@link ApplicationRunner * ApplicationRunners} have not been called. * @param context the application context. - * @param startedTime the time taken to start the application or {@code null} if - * unknown - * @since 2.6.0 + * @since 2.0.0 + * @deprecated since 2.6.0 for removal in 2.8.0 in favor of + * {@link #started(ConfigurableApplicationContext, Duration)} */ - default void started(ConfigurableApplicationContext context, Duration startedTime) { - started(context); + @Deprecated + default void started(ConfigurableApplicationContext context) { } /** @@ -104,26 +102,25 @@ public interface SpringApplicationRunListener { * been refreshed and all {@link CommandLineRunner CommandLineRunners} and * {@link ApplicationRunner ApplicationRunners} have been called. * @param context the application context. - * @deprecated since 2.6.0 for removal in 2.8.0 in favour of - * {@link #running(ConfigurableApplicationContext, Duration)} + * @param timeTaken the time taken for the application to be ready or {@code null} if + * unknown + * @since 2.6.0 + */ + default void ready(ConfigurableApplicationContext context, Duration timeTaken) { + running(context); + } + + /** + * Called immediately before the run method finishes, when the application context has + * been refreshed and all {@link CommandLineRunner CommandLineRunners} and + * {@link ApplicationRunner ApplicationRunners} have been called. + * @param context the application context. + * @deprecated since 2.6.0 for removal in 2.8.0 in favor of + * {@link #ready(ConfigurableApplicationContext, Duration)} * @since 2.0.0 */ @Deprecated default void running(ConfigurableApplicationContext context) { - running(context, null); - } - - /** - * Called immediately before the run method finishes, when the application context has - * been refreshed and all {@link CommandLineRunner CommandLineRunners} and - * {@link ApplicationRunner ApplicationRunners} have been called. - * @param context the application context. - * @param readyTime the time taken for the application to be ready to service requests - * or {@code null} if unknown - * @since 2.6.0 - */ - default void running(ConfigurableApplicationContext context, Duration readyTime) { - running(context); } /** diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/SpringApplicationRunListeners.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/SpringApplicationRunListeners.java index 982cc0f24c..7033411890 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/SpringApplicationRunListeners.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/SpringApplicationRunListeners.java @@ -74,12 +74,12 @@ class SpringApplicationRunListeners { doWithListeners("spring.boot.application.context-loaded", (listener) -> listener.contextLoaded(context)); } - void started(ConfigurableApplicationContext context, Duration startupTime) { - doWithListeners("spring.boot.application.started", (listener) -> listener.started(context, startupTime)); + void started(ConfigurableApplicationContext context, Duration timeTaken) { + doWithListeners("spring.boot.application.started", (listener) -> listener.started(context, timeTaken)); } - void running(ConfigurableApplicationContext context, Duration startupTime) { - doWithListeners("spring.boot.application.running", (listener) -> listener.running(context, startupTime)); + void ready(ConfigurableApplicationContext context, Duration timeTaken) { + doWithListeners("spring.boot.application.running", (listener) -> listener.ready(context, timeTaken)); } void failed(ConfigurableApplicationContext context, Throwable exception) { diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/StartupInfoLogger.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/StartupInfoLogger.java index 962f72e4b8..35a8629c27 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/StartupInfoLogger.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/StartupInfoLogger.java @@ -56,9 +56,9 @@ class StartupInfoLogger { applicationLog.debug(LogMessage.of(this::getRunningMessage)); } - void logStarted(Log applicationLog, Duration startupTime) { + void logStarted(Log applicationLog, Duration timeTakeToStartup) { if (applicationLog.isInfoEnabled()) { - applicationLog.info(getStartedMessage(startupTime)); + applicationLog.info(getStartedMessage(timeTakeToStartup)); } } @@ -83,12 +83,12 @@ class StartupInfoLogger { return message; } - private CharSequence getStartedMessage(Duration startupTime) { + private CharSequence getStartedMessage(Duration timeTakeToStartup) { StringBuilder message = new StringBuilder(); message.append("Started "); appendApplicationName(message); message.append(" in "); - message.append(startupTime.toMillis() / 1000.0); + message.append(timeTakeToStartup.toMillis() / 1000.0); message.append(" seconds"); try { double uptime = ManagementFactory.getRuntimeMXBean().getUptime() / 1000.0; diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/event/ApplicationReadyEvent.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/event/ApplicationReadyEvent.java index 754d167ca4..9e7b89ee44 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/event/ApplicationReadyEvent.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/event/ApplicationReadyEvent.java @@ -37,7 +37,7 @@ public class ApplicationReadyEvent extends SpringApplicationEvent { private final ConfigurableApplicationContext context; - private final Duration readyTime; + private final Duration timeTaken; /** * Create a new {@link ApplicationReadyEvent} instance. @@ -57,14 +57,14 @@ public class ApplicationReadyEvent extends SpringApplicationEvent { * @param application the current application * @param args the arguments the application is running with * @param context the context that was being created - * @param readyTime the time taken to get the application ready to service requests + * @param timeTaken the time taken to get the application ready to service requests * @since 2.6.0 */ public ApplicationReadyEvent(SpringApplication application, String[] args, ConfigurableApplicationContext context, - Duration readyTime) { + Duration timeTaken) { super(application, args); this.context = context; - this.readyTime = readyTime; + this.timeTaken = timeTaken; } /** @@ -79,9 +79,10 @@ public class ApplicationReadyEvent extends SpringApplicationEvent { * Return the time taken for the application to be ready to service requests, or * {@code null} if unknown. * @return the time taken to be ready to service requests + * @since 2.6.0 */ - public Duration getReadyTime() { - return this.readyTime; + public Duration getTimeTaken() { + return this.timeTaken; } } diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/event/ApplicationStartedEvent.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/event/ApplicationStartedEvent.java index b9a00c8e34..0ff27b2c6a 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/event/ApplicationStartedEvent.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/event/ApplicationStartedEvent.java @@ -36,7 +36,7 @@ public class ApplicationStartedEvent extends SpringApplicationEvent { private final ConfigurableApplicationContext context; - private final Duration startedTime; + private final Duration timeTaken; /** * Create a new {@link ApplicationStartedEvent} instance. @@ -57,14 +57,14 @@ public class ApplicationStartedEvent extends SpringApplicationEvent { * @param application the current application * @param args the arguments the application is running with * @param context the context that was being created - * @param startedTime the time taken to start the application + * @param timeTaken the time taken to start the application * @since 2.6.0 */ public ApplicationStartedEvent(SpringApplication application, String[] args, ConfigurableApplicationContext context, - Duration startedTime) { + Duration timeTaken) { super(application, args); this.context = context; - this.startedTime = startedTime; + this.timeTaken = timeTaken; } /** @@ -78,9 +78,10 @@ public class ApplicationStartedEvent extends SpringApplicationEvent { /** * Return the time taken to start the application, or {@code null} if unknown. * @return the startup time + * @since 2.6.0 */ - public Duration getStartedTime() { - return this.startedTime; + public Duration getTimeTaken() { + return this.timeTaken; } } diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/event/EventPublishingRunListener.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/event/EventPublishingRunListener.java index 3fc39b628a..79d1ec5d95 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/event/EventPublishingRunListener.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/event/EventPublishingRunListener.java @@ -104,14 +104,14 @@ public class EventPublishingRunListener implements SpringApplicationRunListener, } @Override - public void started(ConfigurableApplicationContext context, Duration startedTime) { - context.publishEvent(new ApplicationStartedEvent(this.application, this.args, context, startedTime)); + public void started(ConfigurableApplicationContext context, Duration timeTaken) { + context.publishEvent(new ApplicationStartedEvent(this.application, this.args, context, timeTaken)); AvailabilityChangeEvent.publish(context, LivenessState.CORRECT); } @Override - public void running(ConfigurableApplicationContext context, Duration readyTime) { - context.publishEvent(new ApplicationReadyEvent(this.application, this.args, context, readyTime)); + public void ready(ConfigurableApplicationContext context, Duration timeTaken) { + context.publishEvent(new ApplicationReadyEvent(this.application, this.args, context, timeTaken)); AvailabilityChangeEvent.publish(context, ReadinessState.ACCEPTING_TRAFFIC); } diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/SpringApplicationTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/SpringApplicationTests.java index a497b70a49..048867ea70 100644 --- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/SpringApplicationTests.java +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/SpringApplicationTests.java @@ -363,7 +363,7 @@ class SpringApplicationTests { void applicationRunningEventListener() { SpringApplication application = new SpringApplication(ExampleConfig.class); application.setWebApplicationType(WebApplicationType.NONE); - AtomicReference reference = setupListener(application, ApplicationReadyEvent.class); + AtomicReference reference = addListener(application, ApplicationReadyEvent.class); this.context = application.run("--foo=bar"); assertThat(application).isSameAs(reference.get().getSpringApplication()); } @@ -372,7 +372,7 @@ class SpringApplicationTests { void contextRefreshedEventListener() { SpringApplication application = new SpringApplication(ExampleConfig.class); application.setWebApplicationType(WebApplicationType.NONE); - AtomicReference reference = setupListener(application, ContextRefreshedEvent.class); + AtomicReference reference = addListener(application, ContextRefreshedEvent.class); this.context = application.run("--foo=bar"); assertThat(this.context).isSameAs(reference.get().getApplicationContext()); // Custom initializers do not switch off the defaults @@ -405,18 +405,18 @@ class SpringApplicationTests { void applicationStartedEventHasStartedTime() { SpringApplication application = new SpringApplication(ExampleConfig.class); application.setWebApplicationType(WebApplicationType.NONE); - AtomicReference reference = setupListener(application, ApplicationStartedEvent.class); + AtomicReference reference = addListener(application, ApplicationStartedEvent.class); this.context = application.run(); - assertThat(reference.get()).isNotNull().extracting(ApplicationStartedEvent::getStartedTime).isNotNull(); + assertThat(reference.get()).isNotNull().extracting(ApplicationStartedEvent::getTimeTaken).isNotNull(); } @Test void applicationReadyEventHasReadyTime() { SpringApplication application = new SpringApplication(ExampleConfig.class); application.setWebApplicationType(WebApplicationType.NONE); - AtomicReference reference = setupListener(application, ApplicationReadyEvent.class); + AtomicReference reference = addListener(application, ApplicationReadyEvent.class); this.context = application.run(); - assertThat(reference.get()).isNotNull().extracting(ApplicationReadyEvent::getReadyTime).isNotNull(); + assertThat(reference.get()).isNotNull().extracting(ApplicationReadyEvent::getTimeTaken).isNotNull(); } @Test @@ -1252,24 +1252,10 @@ class SpringApplicationTests { && ((AvailabilityChangeEvent) argument).getState().equals(state); } - private AtomicReference setupListener(SpringApplication application, - Class targetEventType) { - final AtomicReference reference = new AtomicReference<>(); - class TestEventListener implements SmartApplicationListener { - - @Override - @SuppressWarnings("unchecked") - public void onApplicationEvent(ApplicationEvent event) { - reference.set((T) event); - } - - @Override - public boolean supportsEventType(Class eventType) { - return targetEventType.isAssignableFrom(eventType); - } - - } - application.addListeners(new TestEventListener()); + private AtomicReference addListener(SpringApplication application, + Class eventType) { + AtomicReference reference = new AtomicReference<>(); + application.addListeners(new TestEventListener<>(eventType, reference)); return reference; } @@ -1302,6 +1288,30 @@ class SpringApplicationTests { }; } + static class TestEventListener implements SmartApplicationListener { + + private final Class eventType; + + private final AtomicReference reference; + + TestEventListener(Class eventType, AtomicReference reference) { + this.eventType = eventType; + this.reference = reference; + } + + @Override + public boolean supportsEventType(Class eventType) { + return this.eventType.isAssignableFrom(eventType); + } + + @Override + @SuppressWarnings("unchecked") + public void onApplicationEvent(ApplicationEvent event) { + this.reference.set((E) event); + } + + } + @Configuration static class InaccessibleConfiguration { diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/StartupInfoLoggerTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/StartupInfoLoggerTests.java index 813a24858a..4f41da83a6 100644 --- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/StartupInfoLoggerTests.java +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/StartupInfoLoggerTests.java @@ -25,7 +25,6 @@ import org.junit.jupiter.api.Test; import org.mockito.ArgumentCaptor; import org.springframework.boot.system.ApplicationPid; -import org.springframework.util.StopWatch; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.BDDMockito.given; @@ -56,11 +55,9 @@ class StartupInfoLoggerTests { @Test void startedFormat() { - StopWatch stopWatch = new StopWatch(); - stopWatch.start(); given(this.log.isInfoEnabled()).willReturn(true); - stopWatch.stop(); - new StartupInfoLogger(getClass()).logStarted(this.log, Duration.ofMillis(stopWatch.getTotalTimeMillis())); + Duration timeTakeToStartup = Duration.ofMillis(10); + new StartupInfoLogger(getClass()).logStarted(this.log, timeTakeToStartup); ArgumentCaptor captor = ArgumentCaptor.forClass(Object.class); verify(this.log).info(captor.capture()); assertThat(captor.getValue().toString()).matches("Started " + getClass().getSimpleName() diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/event/EventPublishingRunListenerTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/event/EventPublishingRunListenerTests.java index 263e17f7e9..fb3e714a30 100644 --- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/event/EventPublishingRunListenerTests.java +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/event/EventPublishingRunListenerTests.java @@ -74,7 +74,7 @@ class EventPublishingRunListenerTests { context.refresh(); this.runListener.started(context, null); checkApplicationEvents(ApplicationStartedEvent.class, AvailabilityChangeEvent.class); - this.runListener.running(context, null); + this.runListener.ready(context, null); checkApplicationEvents(ApplicationReadyEvent.class, AvailabilityChangeEvent.class); }