From 9e9dd4095986e28f2b93ec244864c2b66d827b89 Mon Sep 17 00:00:00 2001 From: Michael McFadyen Date: Sun, 16 Sep 2018 17:18:14 +0100 Subject: [PATCH] Add outcome tag to MVC and WebFlux HTTP request metrics See gh-14486 --- .../server/DefaultWebFluxTagsProvider.java | 3 +- .../web/reactive/server/WebFluxTags.java | 34 ++++++++++++++++++ .../servlet/DefaultWebMvcTagsProvider.java | 3 +- .../metrics/web/servlet/WebMvcTags.java | 32 +++++++++++++++++ .../endpoint/web/servlet/WebMvcTagsTests.java | 28 +++++++++++++++ .../web/reactive/server/WebFluxTagsTests.java | 35 +++++++++++++++++++ 6 files changed, 133 insertions(+), 2 deletions(-) diff --git a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/web/reactive/server/DefaultWebFluxTagsProvider.java b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/web/reactive/server/DefaultWebFluxTagsProvider.java index 94b02b938a..21e640cab1 100644 --- a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/web/reactive/server/DefaultWebFluxTagsProvider.java +++ b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/web/reactive/server/DefaultWebFluxTagsProvider.java @@ -35,7 +35,8 @@ public class DefaultWebFluxTagsProvider implements WebFluxTagsProvider { public Iterable httpRequestTags(ServerWebExchange exchange, Throwable exception) { return Arrays.asList(WebFluxTags.method(exchange), WebFluxTags.uri(exchange), - WebFluxTags.exception(exception), WebFluxTags.status(exchange)); + WebFluxTags.exception(exception), WebFluxTags.status(exchange), + WebFluxTags.outcome(exchange)); } } diff --git a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/web/reactive/server/WebFluxTags.java b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/web/reactive/server/WebFluxTags.java index db95cad6f7..782579279a 100644 --- a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/web/reactive/server/WebFluxTags.java +++ b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/web/reactive/server/WebFluxTags.java @@ -30,6 +30,7 @@ import org.springframework.web.util.pattern.PathPattern; * * @author Jon Schneider * @author Andy Wilkinson + * @author Michael McFadyen * @since 2.0.0 */ public final class WebFluxTags { @@ -42,6 +43,14 @@ public final class WebFluxTags { private static final Tag EXCEPTION_NONE = Tag.of("exception", "None"); + private static final Tag OUTCOME_UNKNOWN = Tag.of("outcome", "UNKNOWN"); + + private static final Tag OUTCOME_SUCCESS = Tag.of("outcome", "SUCCESS"); + + private static final Tag OUTCOME_CLIENT_ERROR = Tag.of("outcome", "CLIENT_ERROR"); + + private static final Tag OUTCOME_SERVER_ERROR = Tag.of("outcome", "SERVER_ERROR"); + private WebFluxTags() { } @@ -112,4 +121,29 @@ public final class WebFluxTags { return EXCEPTION_NONE; } + /** + * Creates a {@code outcome} tag based on the response status of the given + * {@code exchange}. + * @param exchange the exchange + * @return the "outcome" tag derived from the response status + */ + public static Tag outcome(ServerWebExchange exchange) { + if (exchange != null && exchange.getResponse().getStatusCode() != null) { + HttpStatus status = exchange.getResponse().getStatusCode(); + if (status.is1xxInformational() || status.is2xxSuccessful() + || status.is3xxRedirection()) { + return OUTCOME_SUCCESS; + } + else if (status.is4xxClientError()) { + return OUTCOME_CLIENT_ERROR; + } + else { + return OUTCOME_SERVER_ERROR; + } + } + else { + return OUTCOME_UNKNOWN; + } + } + } diff --git a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/web/servlet/DefaultWebMvcTagsProvider.java b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/web/servlet/DefaultWebMvcTagsProvider.java index 3f78a203bb..5c10b58486 100644 --- a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/web/servlet/DefaultWebMvcTagsProvider.java +++ b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/web/servlet/DefaultWebMvcTagsProvider.java @@ -34,7 +34,8 @@ public class DefaultWebMvcTagsProvider implements WebMvcTagsProvider { public Iterable getTags(HttpServletRequest request, HttpServletResponse response, Object handler, Throwable exception) { return Tags.of(WebMvcTags.method(request), WebMvcTags.uri(request, response), - WebMvcTags.exception(exception), WebMvcTags.status(response)); + WebMvcTags.exception(exception), WebMvcTags.status(response), + WebMvcTags.outcome(response)); } @Override diff --git a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/web/servlet/WebMvcTags.java b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/web/servlet/WebMvcTags.java index d800636593..a7ba72a427 100644 --- a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/web/servlet/WebMvcTags.java +++ b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/web/servlet/WebMvcTags.java @@ -34,6 +34,7 @@ import org.springframework.web.servlet.HandlerMapping; * @author Jon Schneider * @author Andy Wilkinson * @author Brian Clozel + * @author Michael McFadyen * @since 2.0.0 */ public final class WebMvcTags { @@ -50,6 +51,14 @@ public final class WebMvcTags { private static final Tag STATUS_UNKNOWN = Tag.of("status", "UNKNOWN"); + private static final Tag OUTCOME_UNKNOWN = Tag.of("outcome", "UNKNOWN"); + + private static final Tag OUTCOME_SUCCESS = Tag.of("outcome", "SUCCESS"); + + private static final Tag OUTCOME_CLIENT_ERROR = Tag.of("outcome", "CLIENT_ERROR"); + + private static final Tag OUTCOME_SERVER_ERROR = Tag.of("outcome", "SERVER_ERROR"); + private static final Tag METHOD_UNKNOWN = Tag.of("method", "UNKNOWN"); private static final Pattern TRAILING_SLASH_PATTERN = Pattern.compile("/$"); @@ -149,4 +158,27 @@ public final class WebMvcTags { return EXCEPTION_NONE; } + /** + * Creates a {@code outcome} tag based on the status of the given {@code response}. + * @param response the HTTP response + * @return the outcome tag derived from the status of the response + */ + public static Tag outcome(HttpServletResponse response) { + if (response != null) { + int status = response.getStatus(); + if (status < 400) { + return OUTCOME_SUCCESS; + } + else if (status < 500) { + return OUTCOME_CLIENT_ERROR; + } + else { + return OUTCOME_SERVER_ERROR; + } + } + else { + return OUTCOME_UNKNOWN; + } + } + } diff --git a/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/web/servlet/WebMvcTagsTests.java b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/web/servlet/WebMvcTagsTests.java index f871b918a0..df3e15d5ab 100644 --- a/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/web/servlet/WebMvcTagsTests.java +++ b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/web/servlet/WebMvcTagsTests.java @@ -31,6 +31,7 @@ import static org.assertj.core.api.Assertions.assertThat; * * @author Andy Wilkinson * @author Brian Clozel + * @author Michael McFadyen */ public class WebMvcTagsTests { @@ -91,4 +92,31 @@ public class WebMvcTagsTests { assertThat(tag.getValue()).isEqualTo("UNKNOWN"); } + @Test + public void outcomeTagIsUnknownWhenResponseIsNull() { + Tag tag = WebMvcTags.outcome(null); + assertThat(tag.getValue()).isEqualTo("UNKNOWN"); + } + + @Test + public void outcomeTagIsSuccessWhenResponseIs2XX() { + this.response.setStatus(200); + Tag tag = WebMvcTags.outcome(this.response); + assertThat(tag.getValue()).isEqualTo("SUCCESS"); + } + + @Test + public void outcomeTagIsClientErrorWhenResponseIs4XX() { + this.response.setStatus(400); + Tag tag = WebMvcTags.outcome(this.response); + assertThat(tag.getValue()).isEqualTo("CLIENT_ERROR"); + } + + @Test + public void outcomeTagIsServerErrorWhenResponseIs5XX() { + this.response.setStatus(500); + Tag tag = WebMvcTags.outcome(this.response); + assertThat(tag.getValue()).isEqualTo("SERVER_ERROR"); + } + } diff --git a/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/web/reactive/server/WebFluxTagsTests.java b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/web/reactive/server/WebFluxTagsTests.java index a25a30da1d..b342ffaf55 100644 --- a/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/web/reactive/server/WebFluxTagsTests.java +++ b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/web/reactive/server/WebFluxTagsTests.java @@ -36,6 +36,7 @@ import static org.mockito.Mockito.mock; * Tests for {@link WebFluxTags}. * * @author Brian Clozel + * @author Michael McFadyen */ public class WebFluxTagsTests { @@ -88,4 +89,38 @@ public class WebFluxTagsTests { assertThat(tag.getValue()).isEqualTo("CUSTOM"); } + @Test + public void outcomeTagIsUnknownWhenResponseIsNull() { + Tag tag = WebFluxTags.outcome(null); + assertThat(tag.getValue()).isEqualTo("UNKNOWN"); + } + + @Test + public void outcomeTagIsUnknownWhenResponseStatusIsNull() { + this.exchange.getResponse().setStatusCode(null); + Tag tag = WebFluxTags.outcome(this.exchange); + assertThat(tag.getValue()).isEqualTo("UNKNOWN"); + } + + @Test + public void outcomeTagIsSuccessWhenResponseIs2XX() { + this.exchange.getResponse().setStatusCode(HttpStatus.OK); + Tag tag = WebFluxTags.outcome(this.exchange); + assertThat(tag.getValue()).isEqualTo("SUCCESS"); + } + + @Test + public void outcomeTagIsClientErrorWhenResponseIs4XX() { + this.exchange.getResponse().setStatusCode(HttpStatus.BAD_REQUEST); + Tag tag = WebFluxTags.outcome(this.exchange); + assertThat(tag.getValue()).isEqualTo("CLIENT_ERROR"); + } + + @Test + public void outcomeTagIsServerErrorWhenResponseIs5XX() { + this.exchange.getResponse().setStatusCode(HttpStatus.BAD_GATEWAY); + Tag tag = WebFluxTags.outcome(this.exchange); + assertThat(tag.getValue()).isEqualTo("SERVER_ERROR"); + } + }