Skip to content

Commit ce95e09

Browse files
committed
Merge pull request #27878 from bono007
* pr/27878: Polish "Add startup time metrics" Add startup time metrics Closes gh-27878
2 parents 32cfde0 + c62a681 commit ce95e09

File tree

23 files changed

+657
-70
lines changed

23 files changed

+657
-70
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
/*
2+
* Copyright 2012-2021 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.actuate.autoconfigure.metrics.startup;
18+
19+
import io.micrometer.core.instrument.MeterRegistry;
20+
21+
import org.springframework.boot.actuate.autoconfigure.metrics.CompositeMeterRegistryAutoConfiguration;
22+
import org.springframework.boot.actuate.autoconfigure.metrics.MetricsAutoConfiguration;
23+
import org.springframework.boot.actuate.metrics.startup.StartupTimeMetrics;
24+
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
25+
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
26+
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
27+
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
28+
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
29+
import org.springframework.context.annotation.Bean;
30+
import org.springframework.context.annotation.Configuration;
31+
32+
/**
33+
* {@link EnableAutoConfiguration Auto-configuration} for startup time metrics.
34+
*
35+
* @author Chris Bono
36+
* @since 2.6.0
37+
*/
38+
@Configuration(proxyBeanMethods = false)
39+
@AutoConfigureAfter({ MetricsAutoConfiguration.class, CompositeMeterRegistryAutoConfiguration.class })
40+
@ConditionalOnClass(MeterRegistry.class)
41+
@ConditionalOnBean(MeterRegistry.class)
42+
public class StartupTimeMetricsAutoConfiguration {
43+
44+
@Bean
45+
@ConditionalOnMissingBean
46+
public StartupTimeMetrics startupTimeMetrics(MeterRegistry meterRegistry) {
47+
return new StartupTimeMetrics(meterRegistry);
48+
}
49+
50+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
/*
2+
* Copyright 2012-2021 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+
/**
18+
* Auto-configuration for actuator startup time metrics.
19+
*/
20+
package org.springframework.boot.actuate.autoconfigure.metrics.startup;

spring-boot-project/spring-boot-actuator-autoconfigure/src/main/resources/META-INF/spring.factories

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ org.springframework.boot.actuate.autoconfigure.metrics.mongo.MongoMetricsAutoCon
7575
org.springframework.boot.actuate.autoconfigure.metrics.orm.jpa.HibernateMetricsAutoConfiguration,\
7676
org.springframework.boot.actuate.autoconfigure.metrics.r2dbc.ConnectionPoolMetricsAutoConfiguration,\
7777
org.springframework.boot.actuate.autoconfigure.metrics.redis.LettuceMetricsAutoConfiguration,\
78+
org.springframework.boot.actuate.autoconfigure.metrics.startup.StartupTimeMetricsAutoConfiguration,\
7879
org.springframework.boot.actuate.autoconfigure.metrics.task.TaskExecutorMetricsAutoConfiguration,\
7980
org.springframework.boot.actuate.autoconfigure.metrics.web.client.HttpClientMetricsAutoConfiguration,\
8081
org.springframework.boot.actuate.autoconfigure.metrics.web.jetty.JettyMetricsAutoConfiguration,\
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
/*
2+
* Copyright 2012-2021 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.actuate.autoconfigure.metrics.startup;
18+
19+
import java.time.Duration;
20+
import java.util.concurrent.TimeUnit;
21+
22+
import io.micrometer.core.instrument.TimeGauge;
23+
import io.micrometer.core.instrument.simple.SimpleMeterRegistry;
24+
import org.junit.jupiter.api.Test;
25+
26+
import org.springframework.boot.SpringApplication;
27+
import org.springframework.boot.actuate.autoconfigure.metrics.test.MetricsRun;
28+
import org.springframework.boot.actuate.metrics.startup.StartupTimeMetrics;
29+
import org.springframework.boot.autoconfigure.AutoConfigurations;
30+
import org.springframework.boot.context.event.ApplicationReadyEvent;
31+
import org.springframework.boot.context.event.ApplicationStartedEvent;
32+
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
33+
34+
import static org.assertj.core.api.Assertions.assertThat;
35+
import static org.mockito.Mockito.mock;
36+
37+
/**
38+
* Tests for {@link StartupTimeMetricsAutoConfiguration}.
39+
*
40+
* @author Chris Bono
41+
* @author Stephane Nicoll
42+
*/
43+
class StartupTimeMetricsAutoConfigurationTests {
44+
45+
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner().with(MetricsRun.simple())
46+
.withConfiguration(AutoConfigurations.of(StartupTimeMetricsAutoConfiguration.class));
47+
48+
@Test
49+
void startupTimeMetricsAreRecorded() {
50+
this.contextRunner.run((context) -> {
51+
assertThat(context).hasSingleBean(StartupTimeMetrics.class);
52+
SimpleMeterRegistry registry = context.getBean(SimpleMeterRegistry.class);
53+
context.publishEvent(new ApplicationStartedEvent(new SpringApplication(), null,
54+
context.getSourceApplicationContext(), Duration.ofMillis(1500)));
55+
TimeGauge startedTimeGage = registry.find("application.started.time").timeGauge();
56+
assertThat(startedTimeGage).isNotNull();
57+
assertThat(startedTimeGage.value(TimeUnit.MILLISECONDS)).isEqualTo(1500L);
58+
context.publishEvent(new ApplicationReadyEvent(new SpringApplication(), null,
59+
context.getSourceApplicationContext(), Duration.ofMillis(2000)));
60+
TimeGauge readyTimeGage = registry.find("application.ready.time").timeGauge();
61+
assertThat(readyTimeGage).isNotNull();
62+
assertThat(readyTimeGage.value(TimeUnit.MILLISECONDS)).isEqualTo(2000L);
63+
});
64+
}
65+
66+
@Test
67+
void startupTimeMetricsCanBeDisabled() {
68+
this.contextRunner.withPropertyValues("management.metrics.enable.application.started.time:false",
69+
"management.metrics.enable.application.ready.time:false").run((context) -> {
70+
context.publishEvent(new ApplicationStartedEvent(new SpringApplication(), null,
71+
context.getSourceApplicationContext(), Duration.ofMillis(2500)));
72+
context.publishEvent(new ApplicationReadyEvent(new SpringApplication(), null,
73+
context.getSourceApplicationContext(), Duration.ofMillis(3000)));
74+
SimpleMeterRegistry registry = context.getBean(SimpleMeterRegistry.class);
75+
assertThat(registry.find("application.started.time").timeGauge()).isNull();
76+
assertThat(registry.find("application.ready.time").timeGauge()).isNull();
77+
});
78+
}
79+
80+
@Test
81+
void customStartupTimeMetricsAreRespected() {
82+
this.contextRunner
83+
.withBean("customStartupTimeMetrics", StartupTimeMetrics.class, () -> mock(StartupTimeMetrics.class))
84+
.run((context) -> assertThat(context).hasSingleBean(StartupTimeMetrics.class)
85+
.hasBean("customStartupTimeMetrics"));
86+
}
87+
88+
}

spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/web/jetty/JettyMetricsAutoConfigurationTests.java

Lines changed: 13 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
import org.springframework.boot.web.embedded.jetty.JettyServletWebServerFactory;
3636
import org.springframework.boot.web.reactive.context.AnnotationConfigReactiveWebServerApplicationContext;
3737
import org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext;
38+
import org.springframework.context.ConfigurableApplicationContext;
3839
import org.springframework.context.annotation.Bean;
3940
import org.springframework.context.annotation.Configuration;
4041
import org.springframework.http.server.reactive.HttpHandler;
@@ -57,8 +58,7 @@ void autoConfiguresThreadPoolMetricsWithEmbeddedServletJetty() {
5758
ServletWebServerFactoryAutoConfiguration.class))
5859
.withUserConfiguration(ServletWebServerConfiguration.class, MeterRegistryConfiguration.class)
5960
.run((context) -> {
60-
context.publishEvent(new ApplicationStartedEvent(new SpringApplication(), null,
61-
context.getSourceApplicationContext()));
61+
context.publishEvent(createApplicationStartedEvent(context.getSourceApplicationContext()));
6262
assertThat(context).hasSingleBean(JettyServerThreadPoolMetricsBinder.class);
6363
SimpleMeterRegistry registry = context.getBean(SimpleMeterRegistry.class);
6464
assertThat(registry.find("jetty.threads.config.min").meter()).isNotNull();
@@ -72,8 +72,7 @@ void autoConfiguresThreadPoolMetricsWithEmbeddedReactiveJetty() {
7272
ReactiveWebServerFactoryAutoConfiguration.class))
7373
.withUserConfiguration(ReactiveWebServerConfiguration.class, MeterRegistryConfiguration.class)
7474
.run((context) -> {
75-
context.publishEvent(new ApplicationStartedEvent(new SpringApplication(), null,
76-
context.getSourceApplicationContext()));
75+
context.publishEvent(createApplicationStartedEvent(context.getSourceApplicationContext()));
7776
SimpleMeterRegistry registry = context.getBean(SimpleMeterRegistry.class);
7877
assertThat(registry.find("jetty.threads.config.min").meter()).isNotNull();
7978
});
@@ -94,8 +93,7 @@ void autoConfiguresConnectionMetricsWithEmbeddedServletJetty() {
9493
ServletWebServerFactoryAutoConfiguration.class))
9594
.withUserConfiguration(ServletWebServerConfiguration.class, MeterRegistryConfiguration.class)
9695
.run((context) -> {
97-
context.publishEvent(new ApplicationStartedEvent(new SpringApplication(), null,
98-
context.getSourceApplicationContext()));
96+
context.publishEvent(createApplicationStartedEvent(context.getSourceApplicationContext()));
9997
assertThat(context).hasSingleBean(JettyConnectionMetricsBinder.class);
10098
SimpleMeterRegistry registry = context.getBean(SimpleMeterRegistry.class);
10199
assertThat(registry.find("jetty.connections.messages.in").meter()).isNotNull();
@@ -109,8 +107,7 @@ void autoConfiguresConnectionMetricsWithEmbeddedReactiveJetty() {
109107
ReactiveWebServerFactoryAutoConfiguration.class))
110108
.withUserConfiguration(ReactiveWebServerConfiguration.class, MeterRegistryConfiguration.class)
111109
.run((context) -> {
112-
context.publishEvent(new ApplicationStartedEvent(new SpringApplication(), null,
113-
context.getSourceApplicationContext()));
110+
context.publishEvent(createApplicationStartedEvent(context.getSourceApplicationContext()));
114111
SimpleMeterRegistry registry = context.getBean(SimpleMeterRegistry.class);
115112
assertThat(registry.find("jetty.connections.messages.in").meter()).isNotNull();
116113
});
@@ -124,8 +121,7 @@ void allowsCustomJettyConnectionMetricsBinderToBeUsed() {
124121
.withUserConfiguration(ServletWebServerConfiguration.class, CustomJettyConnectionMetricsBinder.class,
125122
MeterRegistryConfiguration.class)
126123
.run((context) -> {
127-
context.publishEvent(new ApplicationStartedEvent(new SpringApplication(), null,
128-
context.getSourceApplicationContext()));
124+
context.publishEvent(createApplicationStartedEvent(context.getSourceApplicationContext()));
129125
assertThat(context).hasSingleBean(JettyConnectionMetricsBinder.class)
130126
.hasBean("customJettyConnectionMetricsBinder");
131127
SimpleMeterRegistry registry = context.getBean(SimpleMeterRegistry.class);
@@ -143,8 +139,7 @@ void autoConfiguresSslHandshakeMetricsWithEmbeddedServletJetty() {
143139
.withPropertyValues("server.ssl.enabled: true", "server.ssl.key-store: src/test/resources/test.jks",
144140
"server.ssl.key-store-password: secret", "server.ssl.key-password: password")
145141
.run((context) -> {
146-
context.publishEvent(new ApplicationStartedEvent(new SpringApplication(), null,
147-
context.getSourceApplicationContext()));
142+
context.publishEvent(createApplicationStartedEvent(context.getSourceApplicationContext()));
148143
assertThat(context).hasSingleBean(JettySslHandshakeMetricsBinder.class);
149144
SimpleMeterRegistry registry = context.getBean(SimpleMeterRegistry.class);
150145
assertThat(registry.find("jetty.ssl.handshakes").meter()).isNotNull();
@@ -160,8 +155,7 @@ void autoConfiguresSslHandshakeMetricsWithEmbeddedReactiveJetty() {
160155
.withPropertyValues("server.ssl.enabled: true", "server.ssl.key-store: src/test/resources/test.jks",
161156
"server.ssl.key-store-password: secret", "server.ssl.key-password: password")
162157
.run((context) -> {
163-
context.publishEvent(new ApplicationStartedEvent(new SpringApplication(), null,
164-
context.getSourceApplicationContext()));
158+
context.publishEvent(createApplicationStartedEvent(context.getSourceApplicationContext()));
165159
SimpleMeterRegistry registry = context.getBean(SimpleMeterRegistry.class);
166160
assertThat(registry.find("jetty.ssl.handshakes").meter()).isNotNull();
167161
});
@@ -177,8 +171,7 @@ void allowsCustomJettySslHandshakeMetricsBinderToBeUsed() {
177171
.withPropertyValues("server.ssl.enabled: true", "server.ssl.key-store: src/test/resources/test.jks",
178172
"server.ssl.key-store-password: secret", "server.ssl.key-password: password")
179173
.run((context) -> {
180-
context.publishEvent(new ApplicationStartedEvent(new SpringApplication(), null,
181-
context.getSourceApplicationContext()));
174+
context.publishEvent(createApplicationStartedEvent(context.getSourceApplicationContext()));
182175
assertThat(context).hasSingleBean(JettySslHandshakeMetricsBinder.class)
183176
.hasBean("customJettySslHandshakeMetricsBinder");
184177
SimpleMeterRegistry registry = context.getBean(SimpleMeterRegistry.class);
@@ -213,6 +206,10 @@ void doesNotAutoConfigureSslHandshakeMetricsWhenSslEnabledPropertySetToFalse() {
213206
.run((context) -> assertThat(context).doesNotHaveBean(JettySslHandshakeMetricsBinder.class));
214207
}
215208

209+
private ApplicationStartedEvent createApplicationStartedEvent(ConfigurableApplicationContext context) {
210+
return new ApplicationStartedEvent(new SpringApplication(), null, context, null);
211+
}
212+
216213
@Configuration(proxyBeanMethods = false)
217214
static class MeterRegistryConfiguration {
218215

spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/web/tomcat/TomcatMetricsAutoConfigurationTests.java

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
import org.springframework.boot.web.embedded.tomcat.TomcatWebServer;
3939
import org.springframework.boot.web.reactive.context.AnnotationConfigReactiveWebServerApplicationContext;
4040
import org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext;
41+
import org.springframework.context.ConfigurableApplicationContext;
4142
import org.springframework.context.annotation.Bean;
4243
import org.springframework.context.annotation.Configuration;
4344
import org.springframework.http.server.reactive.HttpHandler;
@@ -61,8 +62,7 @@ void autoConfiguresTomcatMetricsWithEmbeddedServletTomcat() {
6162
ServletWebServerFactoryAutoConfiguration.class))
6263
.withUserConfiguration(ServletWebServerConfiguration.class, MeterRegistryConfiguration.class)
6364
.withPropertyValues("server.tomcat.mbeanregistry.enabled=true").run((context) -> {
64-
context.publishEvent(new ApplicationStartedEvent(new SpringApplication(), null,
65-
context.getSourceApplicationContext()));
65+
context.publishEvent(createApplicationStartedEvent(context.getSourceApplicationContext()));
6666
assertThat(context).hasSingleBean(TomcatMetricsBinder.class);
6767
SimpleMeterRegistry registry = context.getBean(SimpleMeterRegistry.class);
6868
assertThat(registry.find("tomcat.sessions.active.max").meter()).isNotNull();
@@ -78,8 +78,7 @@ void autoConfiguresTomcatMetricsWithEmbeddedReactiveTomcat() {
7878
ReactiveWebServerFactoryAutoConfiguration.class))
7979
.withUserConfiguration(ReactiveWebServerConfiguration.class, MeterRegistryConfiguration.class)
8080
.withPropertyValues("server.tomcat.mbeanregistry.enabled=true").run((context) -> {
81-
context.publishEvent(new ApplicationStartedEvent(new SpringApplication(), null,
82-
context.getSourceApplicationContext()));
81+
context.publishEvent(createApplicationStartedEvent(context.getSourceApplicationContext()));
8382
SimpleMeterRegistry registry = context.getBean(SimpleMeterRegistry.class);
8483
assertThat(registry.find("tomcat.sessions.active.max").meter()).isNotNull();
8584
assertThat(registry.find("tomcat.threads.current").meter()).isNotNull();
@@ -109,6 +108,10 @@ void allowsCustomTomcatMetricsToBeUsed() {
109108
.hasBean("customTomcatMetrics"));
110109
}
111110

111+
private ApplicationStartedEvent createApplicationStartedEvent(ConfigurableApplicationContext context) {
112+
return new ApplicationStartedEvent(new SpringApplication(), null, context, null);
113+
}
114+
112115
private void resetTomcatState() {
113116
ReflectionTestUtils.setField(Registry.class, "registry", null);
114117
AtomicInteger containerCounter = (AtomicInteger) ReflectionTestUtils.getField(TomcatWebServer.class,

0 commit comments

Comments
 (0)