Skip to content

Commit 632c123

Browse files
committed
Polish "Add support for GET requests for /actuator/startup"
See gh-24717
1 parent 4b8d6ef commit 632c123

File tree

4 files changed

+65
-38
lines changed

4 files changed

+65
-38
lines changed

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

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,26 +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`).
11+
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[]
18+
19+
The resulting response is similar to the following:
20+
21+
include::{snippets}/startup-snapshot/http-response.adoc[]
22+
23+
24+
[[startup-retrieving-drain]]
25+
== Draining the Application Startup steps
26+
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:
1228

1329
include::{snippets}/startup/curl-request.adoc[]
1430

1531
The resulting response is similar to the following:
1632

1733
include::{snippets}/startup/http-response.adoc[]
1834

19-
NOTE: The above call resets the application startup steps buffer - subsequent calls to the endpoint will
20-
not include the returned steps. To retrieve a snapshot of the steps recorded so far without removing them
21-
from the startup buffer, make a `GET` request to `/actuator/startup`.
22-
2335
[[startup-retrieving-response-structure]]
2436
=== Response Structure
2537

26-
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.
2739
The following table describes the structure of the response:
2840

2941
[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: 7 additions & 7 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.
@@ -46,18 +46,18 @@ public StartupEndpoint(BufferingApplicationStartup applicationStartup) {
4646
this.applicationStartup = applicationStartup;
4747
}
4848

49-
@WriteOperation
50-
public StartupResponse startup() {
51-
StartupTimeline startupTimeline = this.applicationStartup.drainBufferedTimeline();
52-
return new StartupResponse(startupTimeline);
53-
}
54-
5549
@ReadOperation
5650
public StartupResponse startupSnapshot() {
5751
StartupTimeline startupTimeline = this.applicationStartup.getBufferedTimeline();
5852
return new StartupResponse(startupTimeline);
5953
}
6054

55+
@WriteOperation
56+
public StartupResponse startup() {
57+
StartupTimeline startupTimeline = this.applicationStartup.drainBufferedTimeline();
58+
return new StartupResponse(startupTimeline);
59+
}
60+
6161
/**
6262
* A description of an application startup, primarily intended for serialization to
6363
* JSON.

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

Lines changed: 22 additions & 17 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,13 +16,17 @@
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

@@ -37,40 +41,41 @@ class StartupEndpointTests {
3741
@Test
3842
void startupEventsAreFound() {
3943
BufferingApplicationStartup applicationStartup = new BufferingApplicationStartup(256);
40-
ApplicationContextRunner contextRunner = new ApplicationContextRunner()
41-
.withInitializer((context) -> context.setApplicationStartup(applicationStartup))
42-
.withUserConfiguration(EndpointConfiguration.class);
43-
contextRunner.run((context) -> {
44-
StartupEndpoint.StartupResponse startup = context.getBean(StartupEndpoint.class).startup();
44+
testStartupEndpoint(applicationStartup, (startupEndpoint) -> {
45+
StartupResponse startup = startupEndpoint.startup();
4546
assertThat(startup.getSpringBootVersion()).isEqualTo(SpringBootVersion.getVersion());
4647
assertThat(startup.getTimeline().getStartTime())
4748
.isEqualTo(applicationStartup.getBufferedTimeline().getStartTime());
4849
});
4950
}
5051

5152
@Test
52-
void bufferIsDrained() {
53+
void bufferWithGetIsNotDrained() {
5354
BufferingApplicationStartup applicationStartup = new BufferingApplicationStartup(256);
54-
ApplicationContextRunner contextRunner = new ApplicationContextRunner()
55-
.withInitializer((context) -> context.setApplicationStartup(applicationStartup))
56-
.withUserConfiguration(EndpointConfiguration.class);
57-
contextRunner.run((context) -> {
58-
StartupEndpoint.StartupResponse startup = context.getBean(StartupEndpoint.class).startup();
55+
testStartupEndpoint(applicationStartup, (startupEndpoint) -> {
56+
StartupResponse startup = startupEndpoint.startupSnapshot();
5957
assertThat(startup.getTimeline().getEvents()).isNotEmpty();
60-
assertThat(applicationStartup.getBufferedTimeline().getEvents()).isEmpty();
58+
assertThat(applicationStartup.getBufferedTimeline().getEvents()).isNotEmpty();
6159
});
6260
}
6361

6462
@Test
65-
void bufferIsNotDrained() {
63+
void bufferWithPostIsDrained() {
6664
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) {
6773
ApplicationContextRunner contextRunner = new ApplicationContextRunner()
6874
.withInitializer((context) -> context.setApplicationStartup(applicationStartup))
6975
.withUserConfiguration(EndpointConfiguration.class);
7076
contextRunner.run((context) -> {
71-
StartupEndpoint.StartupResponse startup = context.getBean(StartupEndpoint.class).startupSnapshot();
72-
assertThat(startup.getTimeline().getEvents()).isNotEmpty();
73-
assertThat(applicationStartup.getBufferedTimeline().getEvents()).isNotEmpty();
77+
assertThat(context).hasSingleBean(StartupEndpoint.class);
78+
startupEndpoint.accept(context.getBean(StartupEndpoint.class));
7479
});
7580
}
7681

0 commit comments

Comments
 (0)