Skip to content

Commit 04a02f0

Browse files
committed
Merge pull request #24717 from bono007
* pr/24717: Polish "Add support for GET requests for /actuator/startup" Add support for GET requests for /actuator/startup Closes gh-24717
2 parents 8a6e79d + 632c123 commit 04a02f0

File tree

4 files changed

+74
-23
lines changed

4 files changed

+74
-23
lines changed

spring-boot-project/spring-boot-actuator-autoconfigure/src/docs/asciidoc/endpoints/startup.adoc

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,24 +4,38 @@
44
The `startup` endpoint provides information about the application's startup sequence.
55

66

7-
87
[[startup-retrieving]]
98
== Retrieving the Application Startup steps
109

11-
To retrieve the steps recorded so far during the application startup phase , make a `POST` request to `/actuator/startup`, as shown in the following curl-based example:
10+
The application startup steps can either be retrieved as a snapshot (`GET`) or drained from the buffer (`POST`).
1211

13-
include::{snippets}/startup/curl-request.adoc[]
12+
[[startup-retrieving-snapshot]]
13+
=== Retrieving a snapshot of the Application Startup steps
14+
15+
To retrieve the steps recorded so far during the application startup phase , make a `GET` request to `/actuator/startup`, as shown in the following curl-based example:
16+
17+
include::{snippets}/startup-snapshot/curl-request.adoc[]
1418

1519
The resulting response is similar to the following:
1620

17-
include::{snippets}/startup/http-response.adoc[]
21+
include::{snippets}/startup-snapshot/http-response.adoc[]
22+
1823

24+
[[startup-retrieving-drain]]
25+
== Draining the Application Startup steps
1926

27+
To drain and return the steps recorded so far during the application startup phase , make a `POST` request to `/actuator/startup`, as shown in the following curl-based example:
28+
29+
include::{snippets}/startup/curl-request.adoc[]
30+
31+
The resulting response is similar to the following:
32+
33+
include::{snippets}/startup/http-response.adoc[]
2034

2135
[[startup-retrieving-response-structure]]
2236
=== Response Structure
2337

24-
The response contains details of the application startup steps recorded so far by the application.
38+
The response contains details of the application startup steps.
2539
The following table describes the structure of the response:
2640

2741
[cols="2,1,3"]

spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/documentation/StartupEndpointDocumentationTests.java

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -26,19 +26,21 @@
2626
import org.springframework.context.annotation.Configuration;
2727
import org.springframework.context.annotation.Import;
2828
import org.springframework.core.metrics.StartupStep;
29+
import org.springframework.restdocs.payload.FieldDescriptor;
2930
import org.springframework.restdocs.payload.JsonFieldType;
30-
import org.springframework.restdocs.payload.ResponseFieldsSnippet;
31+
import org.springframework.restdocs.payload.PayloadDocumentation;
3132

3233
import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document;
3334
import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath;
34-
import static org.springframework.restdocs.payload.PayloadDocumentation.responseFields;
35+
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
3536
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
3637
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
3738

3839
/**
3940
* Tests for generating documentation describing {@link StartupEndpoint}.
4041
*
4142
* @author Brian Clozel
43+
* @author Stephane Nicoll
4244
*/
4345
class StartupEndpointDocumentationTests extends MockMvcEndpointDocumentationTests {
4446

@@ -53,9 +55,20 @@ void appendSampleStartupSteps(@Autowired BufferingApplicationStartup application
5355
instantiate.end();
5456
}
5557

58+
@Test
59+
void startupSnapshot() throws Exception {
60+
this.mockMvc.perform(get("/actuator/startup")).andExpect(status().isOk())
61+
.andDo(document("startup-snapshot", PayloadDocumentation.responseFields(responseFields())));
62+
}
63+
5664
@Test
5765
void startup() throws Exception {
58-
ResponseFieldsSnippet responseFields = responseFields(
66+
this.mockMvc.perform(post("/actuator/startup")).andExpect(status().isOk())
67+
.andDo(document("startup", PayloadDocumentation.responseFields(responseFields())));
68+
}
69+
70+
private FieldDescriptor[] responseFields() {
71+
return new FieldDescriptor[] {
5972
fieldWithPath("springBootVersion").type(JsonFieldType.STRING)
6073
.description("Spring Boot version for this application.").optional(),
6174
fieldWithPath("timeline.startTime").description("Start time of the application."),
@@ -73,10 +86,7 @@ void startup() throws Exception {
7386
fieldWithPath("timeline.events.[].startupStep.tags[].key")
7487
.description("The key of the StartupStep Tag."),
7588
fieldWithPath("timeline.events.[].startupStep.tags[].value")
76-
.description("The value of the StartupStep Tag."));
77-
78-
this.mockMvc.perform(post("/actuator/startup")).andExpect(status().isOk())
79-
.andDo(document("startup", responseFields));
89+
.description("The value of the StartupStep Tag.") };
8090
}
8191

8292
@Configuration(proxyBeanMethods = false)

spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/startup/StartupEndpoint.java

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2020 the original author or authors.
2+
* Copyright 2012-2021 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -18,6 +18,7 @@
1818

1919
import org.springframework.boot.SpringBootVersion;
2020
import org.springframework.boot.actuate.endpoint.annotation.Endpoint;
21+
import org.springframework.boot.actuate.endpoint.annotation.ReadOperation;
2122
import org.springframework.boot.actuate.endpoint.annotation.WriteOperation;
2223
import org.springframework.boot.context.metrics.buffering.BufferingApplicationStartup;
2324
import org.springframework.boot.context.metrics.buffering.StartupTimeline;
@@ -28,6 +29,7 @@
2829
* application startup}.
2930
*
3031
* @author Brian Clozel
32+
* @author Chris Bono
3133
* @since 2.4.0
3234
*/
3335
@Endpoint(id = "startup")
@@ -44,6 +46,12 @@ public StartupEndpoint(BufferingApplicationStartup applicationStartup) {
4446
this.applicationStartup = applicationStartup;
4547
}
4648

49+
@ReadOperation
50+
public StartupResponse startupSnapshot() {
51+
StartupTimeline startupTimeline = this.applicationStartup.getBufferedTimeline();
52+
return new StartupResponse(startupTimeline);
53+
}
54+
4755
@WriteOperation
4856
public StartupResponse startup() {
4957
StartupTimeline startupTimeline = this.applicationStartup.drainBufferedTimeline();

spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/startup/StartupEndpointTests.java

Lines changed: 29 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2020 the original author or authors.
2+
* Copyright 2012-2021 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -16,47 +16,66 @@
1616

1717
package org.springframework.boot.actuate.startup;
1818

19+
import java.util.function.Consumer;
20+
1921
import org.junit.jupiter.api.Test;
2022

2123
import org.springframework.boot.SpringBootVersion;
24+
import org.springframework.boot.actuate.startup.StartupEndpoint.StartupResponse;
2225
import org.springframework.boot.context.metrics.buffering.BufferingApplicationStartup;
2326
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
2427
import org.springframework.context.annotation.Bean;
2528
import org.springframework.context.annotation.Configuration;
29+
import org.springframework.core.metrics.ApplicationStartup;
2630

2731
import static org.assertj.core.api.Assertions.assertThat;
2832

2933
/**
3034
* Tests for {@link StartupEndpoint}.
3135
*
3236
* @author Brian Clozel
37+
* @author Chris Bono
3338
*/
3439
class StartupEndpointTests {
3540

3641
@Test
3742
void startupEventsAreFound() {
3843
BufferingApplicationStartup applicationStartup = new BufferingApplicationStartup(256);
39-
ApplicationContextRunner contextRunner = new ApplicationContextRunner()
40-
.withInitializer((context) -> context.setApplicationStartup(applicationStartup))
41-
.withUserConfiguration(EndpointConfiguration.class);
42-
contextRunner.run((context) -> {
43-
StartupEndpoint.StartupResponse startup = context.getBean(StartupEndpoint.class).startup();
44+
testStartupEndpoint(applicationStartup, (startupEndpoint) -> {
45+
StartupResponse startup = startupEndpoint.startup();
4446
assertThat(startup.getSpringBootVersion()).isEqualTo(SpringBootVersion.getVersion());
4547
assertThat(startup.getTimeline().getStartTime())
4648
.isEqualTo(applicationStartup.getBufferedTimeline().getStartTime());
4749
});
4850
}
4951

5052
@Test
51-
void bufferIsDrained() {
53+
void bufferWithGetIsNotDrained() {
54+
BufferingApplicationStartup applicationStartup = new BufferingApplicationStartup(256);
55+
testStartupEndpoint(applicationStartup, (startupEndpoint) -> {
56+
StartupResponse startup = startupEndpoint.startupSnapshot();
57+
assertThat(startup.getTimeline().getEvents()).isNotEmpty();
58+
assertThat(applicationStartup.getBufferedTimeline().getEvents()).isNotEmpty();
59+
});
60+
}
61+
62+
@Test
63+
void bufferWithPostIsDrained() {
5264
BufferingApplicationStartup applicationStartup = new BufferingApplicationStartup(256);
65+
testStartupEndpoint(applicationStartup, (startupEndpoint) -> {
66+
StartupResponse startup = startupEndpoint.startup();
67+
assertThat(startup.getTimeline().getEvents()).isNotEmpty();
68+
assertThat(applicationStartup.getBufferedTimeline().getEvents()).isEmpty();
69+
});
70+
}
71+
72+
private void testStartupEndpoint(ApplicationStartup applicationStartup, Consumer<StartupEndpoint> startupEndpoint) {
5373
ApplicationContextRunner contextRunner = new ApplicationContextRunner()
5474
.withInitializer((context) -> context.setApplicationStartup(applicationStartup))
5575
.withUserConfiguration(EndpointConfiguration.class);
5676
contextRunner.run((context) -> {
57-
StartupEndpoint.StartupResponse startup = context.getBean(StartupEndpoint.class).startup();
58-
assertThat(startup.getTimeline().getEvents()).isNotEmpty();
59-
assertThat(applicationStartup.getBufferedTimeline().getEvents()).isEmpty();
77+
assertThat(context).hasSingleBean(StartupEndpoint.class);
78+
startupEndpoint.accept(context.getBean(StartupEndpoint.class));
6079
});
6180
}
6281

0 commit comments

Comments
 (0)