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
This commit is contained in:
commit
04a02f0a2d
|
@ -4,11 +4,27 @@
|
|||
The `startup` endpoint provides information about the application's startup sequence.
|
||||
|
||||
|
||||
|
||||
[[startup-retrieving]]
|
||||
== Retrieving the Application Startup steps
|
||||
|
||||
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:
|
||||
The application startup steps can either be retrieved as a snapshot (`GET`) or drained from the buffer (`POST`).
|
||||
|
||||
[[startup-retrieving-snapshot]]
|
||||
=== Retrieving a snapshot of the Application Startup steps
|
||||
|
||||
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:
|
||||
|
||||
include::{snippets}/startup-snapshot/curl-request.adoc[]
|
||||
|
||||
The resulting response is similar to the following:
|
||||
|
||||
include::{snippets}/startup-snapshot/http-response.adoc[]
|
||||
|
||||
|
||||
[[startup-retrieving-drain]]
|
||||
== Draining the Application Startup steps
|
||||
|
||||
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:
|
||||
|
||||
include::{snippets}/startup/curl-request.adoc[]
|
||||
|
||||
|
@ -16,12 +32,10 @@ The resulting response is similar to the following:
|
|||
|
||||
include::{snippets}/startup/http-response.adoc[]
|
||||
|
||||
|
||||
|
||||
[[startup-retrieving-response-structure]]
|
||||
=== Response Structure
|
||||
|
||||
The response contains details of the application startup steps recorded so far by the application.
|
||||
The response contains details of the application startup steps.
|
||||
The following table describes the structure of the response:
|
||||
|
||||
[cols="2,1,3"]
|
||||
|
|
|
@ -26,12 +26,13 @@ import org.springframework.context.annotation.Bean;
|
|||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.core.metrics.StartupStep;
|
||||
import org.springframework.restdocs.payload.FieldDescriptor;
|
||||
import org.springframework.restdocs.payload.JsonFieldType;
|
||||
import org.springframework.restdocs.payload.ResponseFieldsSnippet;
|
||||
import org.springframework.restdocs.payload.PayloadDocumentation;
|
||||
|
||||
import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document;
|
||||
import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath;
|
||||
import static org.springframework.restdocs.payload.PayloadDocumentation.responseFields;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||
|
||||
|
@ -39,6 +40,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
|
|||
* Tests for generating documentation describing {@link StartupEndpoint}.
|
||||
*
|
||||
* @author Brian Clozel
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
class StartupEndpointDocumentationTests extends MockMvcEndpointDocumentationTests {
|
||||
|
||||
|
@ -53,9 +55,20 @@ class StartupEndpointDocumentationTests extends MockMvcEndpointDocumentationTest
|
|||
instantiate.end();
|
||||
}
|
||||
|
||||
@Test
|
||||
void startupSnapshot() throws Exception {
|
||||
this.mockMvc.perform(get("/actuator/startup")).andExpect(status().isOk())
|
||||
.andDo(document("startup-snapshot", PayloadDocumentation.responseFields(responseFields())));
|
||||
}
|
||||
|
||||
@Test
|
||||
void startup() throws Exception {
|
||||
ResponseFieldsSnippet responseFields = responseFields(
|
||||
this.mockMvc.perform(post("/actuator/startup")).andExpect(status().isOk())
|
||||
.andDo(document("startup", PayloadDocumentation.responseFields(responseFields())));
|
||||
}
|
||||
|
||||
private FieldDescriptor[] responseFields() {
|
||||
return new FieldDescriptor[] {
|
||||
fieldWithPath("springBootVersion").type(JsonFieldType.STRING)
|
||||
.description("Spring Boot version for this application.").optional(),
|
||||
fieldWithPath("timeline.startTime").description("Start time of the application."),
|
||||
|
@ -73,10 +86,7 @@ class StartupEndpointDocumentationTests extends MockMvcEndpointDocumentationTest
|
|||
fieldWithPath("timeline.events.[].startupStep.tags[].key")
|
||||
.description("The key of the StartupStep Tag."),
|
||||
fieldWithPath("timeline.events.[].startupStep.tags[].value")
|
||||
.description("The value of the StartupStep Tag."));
|
||||
|
||||
this.mockMvc.perform(post("/actuator/startup")).andExpect(status().isOk())
|
||||
.andDo(document("startup", responseFields));
|
||||
.description("The value of the StartupStep Tag.") };
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2012-2020 the original author or authors.
|
||||
* Copyright 2012-2021 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.
|
||||
|
@ -18,6 +18,7 @@ package org.springframework.boot.actuate.startup;
|
|||
|
||||
import org.springframework.boot.SpringBootVersion;
|
||||
import org.springframework.boot.actuate.endpoint.annotation.Endpoint;
|
||||
import org.springframework.boot.actuate.endpoint.annotation.ReadOperation;
|
||||
import org.springframework.boot.actuate.endpoint.annotation.WriteOperation;
|
||||
import org.springframework.boot.context.metrics.buffering.BufferingApplicationStartup;
|
||||
import org.springframework.boot.context.metrics.buffering.StartupTimeline;
|
||||
|
@ -28,6 +29,7 @@ import org.springframework.boot.context.metrics.buffering.StartupTimeline;
|
|||
* application startup}.
|
||||
*
|
||||
* @author Brian Clozel
|
||||
* @author Chris Bono
|
||||
* @since 2.4.0
|
||||
*/
|
||||
@Endpoint(id = "startup")
|
||||
|
@ -44,6 +46,12 @@ public class StartupEndpoint {
|
|||
this.applicationStartup = applicationStartup;
|
||||
}
|
||||
|
||||
@ReadOperation
|
||||
public StartupResponse startupSnapshot() {
|
||||
StartupTimeline startupTimeline = this.applicationStartup.getBufferedTimeline();
|
||||
return new StartupResponse(startupTimeline);
|
||||
}
|
||||
|
||||
@WriteOperation
|
||||
public StartupResponse startup() {
|
||||
StartupTimeline startupTimeline = this.applicationStartup.drainBufferedTimeline();
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2012-2020 the original author or authors.
|
||||
* Copyright 2012-2021 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.
|
||||
|
@ -16,13 +16,17 @@
|
|||
|
||||
package org.springframework.boot.actuate.startup;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.boot.SpringBootVersion;
|
||||
import org.springframework.boot.actuate.startup.StartupEndpoint.StartupResponse;
|
||||
import org.springframework.boot.context.metrics.buffering.BufferingApplicationStartup;
|
||||
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.metrics.ApplicationStartup;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
|
@ -30,17 +34,15 @@ import static org.assertj.core.api.Assertions.assertThat;
|
|||
* Tests for {@link StartupEndpoint}.
|
||||
*
|
||||
* @author Brian Clozel
|
||||
* @author Chris Bono
|
||||
*/
|
||||
class StartupEndpointTests {
|
||||
|
||||
@Test
|
||||
void startupEventsAreFound() {
|
||||
BufferingApplicationStartup applicationStartup = new BufferingApplicationStartup(256);
|
||||
ApplicationContextRunner contextRunner = new ApplicationContextRunner()
|
||||
.withInitializer((context) -> context.setApplicationStartup(applicationStartup))
|
||||
.withUserConfiguration(EndpointConfiguration.class);
|
||||
contextRunner.run((context) -> {
|
||||
StartupEndpoint.StartupResponse startup = context.getBean(StartupEndpoint.class).startup();
|
||||
testStartupEndpoint(applicationStartup, (startupEndpoint) -> {
|
||||
StartupResponse startup = startupEndpoint.startup();
|
||||
assertThat(startup.getSpringBootVersion()).isEqualTo(SpringBootVersion.getVersion());
|
||||
assertThat(startup.getTimeline().getStartTime())
|
||||
.isEqualTo(applicationStartup.getBufferedTimeline().getStartTime());
|
||||
|
@ -48,15 +50,32 @@ class StartupEndpointTests {
|
|||
}
|
||||
|
||||
@Test
|
||||
void bufferIsDrained() {
|
||||
void bufferWithGetIsNotDrained() {
|
||||
BufferingApplicationStartup applicationStartup = new BufferingApplicationStartup(256);
|
||||
testStartupEndpoint(applicationStartup, (startupEndpoint) -> {
|
||||
StartupResponse startup = startupEndpoint.startupSnapshot();
|
||||
assertThat(startup.getTimeline().getEvents()).isNotEmpty();
|
||||
assertThat(applicationStartup.getBufferedTimeline().getEvents()).isNotEmpty();
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void bufferWithPostIsDrained() {
|
||||
BufferingApplicationStartup applicationStartup = new BufferingApplicationStartup(256);
|
||||
testStartupEndpoint(applicationStartup, (startupEndpoint) -> {
|
||||
StartupResponse startup = startupEndpoint.startup();
|
||||
assertThat(startup.getTimeline().getEvents()).isNotEmpty();
|
||||
assertThat(applicationStartup.getBufferedTimeline().getEvents()).isEmpty();
|
||||
});
|
||||
}
|
||||
|
||||
private void testStartupEndpoint(ApplicationStartup applicationStartup, Consumer<StartupEndpoint> startupEndpoint) {
|
||||
ApplicationContextRunner contextRunner = new ApplicationContextRunner()
|
||||
.withInitializer((context) -> context.setApplicationStartup(applicationStartup))
|
||||
.withUserConfiguration(EndpointConfiguration.class);
|
||||
contextRunner.run((context) -> {
|
||||
StartupEndpoint.StartupResponse startup = context.getBean(StartupEndpoint.class).startup();
|
||||
assertThat(startup.getTimeline().getEvents()).isNotEmpty();
|
||||
assertThat(applicationStartup.getBufferedTimeline().getEvents()).isEmpty();
|
||||
assertThat(context).hasSingleBean(StartupEndpoint.class);
|
||||
startupEndpoint.accept(context.getBean(StartupEndpoint.class));
|
||||
});
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue