diff --git a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/web/client/RestTemplateExchangeTags.java b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/web/client/RestTemplateExchangeTags.java index 9b6f6fd50c..e729c1c398 100644 --- a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/web/client/RestTemplateExchangeTags.java +++ b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/web/client/RestTemplateExchangeTags.java @@ -18,6 +18,9 @@ package org.springframework.boot.actuate.metrics.web.client; import java.io.IOException; import java.net.URI; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; import java.util.regex.Pattern; import io.micrometer.core.instrument.Tag; @@ -54,6 +57,18 @@ public final class RestTemplateExchangeTags { private static final Tag OUTCOME_SERVER_ERROR = Tag.of("outcome", "SERVER_ERROR"); + private static final Map SERIES_OUTCOMES; + + static { + Map seriesOutcomes = new HashMap<>(); + seriesOutcomes.put(HttpStatus.Series.INFORMATIONAL, OUTCOME_INFORMATIONAL); + seriesOutcomes.put(HttpStatus.Series.SUCCESSFUL, OUTCOME_SUCCESS); + seriesOutcomes.put(HttpStatus.Series.REDIRECTION, OUTCOME_REDIRECTION); + seriesOutcomes.put(HttpStatus.Series.CLIENT_ERROR, OUTCOME_CLIENT_ERROR); + seriesOutcomes.put(HttpStatus.Series.SERVER_ERROR, OUTCOME_SERVER_ERROR); + SERIES_OUTCOMES = Collections.unmodifiableMap(seriesOutcomes); + } + private RestTemplateExchangeTags() { } @@ -132,7 +147,7 @@ public final class RestTemplateExchangeTags { /** * Creates an {@code outcome} {@code Tag} derived from the - * {@link ClientHttpResponse#getStatusCode() status} of the given {@code response}. + * {@link ClientHttpResponse#getRawStatusCode() status} of the given {@code response}. * @param response the response * @return the outcome tag * @since 2.2.0 @@ -140,28 +155,16 @@ public final class RestTemplateExchangeTags { public static Tag outcome(ClientHttpResponse response) { try { if (response != null) { - HttpStatus statusCode = response.getStatusCode(); - if (statusCode.is1xxInformational()) { - return OUTCOME_INFORMATIONAL; - } - if (statusCode.is2xxSuccessful()) { - return OUTCOME_SUCCESS; - } - if (statusCode.is3xxRedirection()) { - return OUTCOME_REDIRECTION; - } - if (statusCode.is4xxClientError()) { - return OUTCOME_CLIENT_ERROR; - } - if (statusCode.is5xxServerError()) { - return OUTCOME_SERVER_ERROR; + HttpStatus.Series series = HttpStatus.Series.resolve(response.getRawStatusCode()); + if (series != null) { + return SERIES_OUTCOMES.getOrDefault(series, OUTCOME_UNKNOWN); } } - return OUTCOME_UNKNOWN; } catch (IOException | IllegalArgumentException ex) { - return OUTCOME_UNKNOWN; + // Continue } + return OUTCOME_UNKNOWN; } } diff --git a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/web/reactive/client/WebClientExchangeTags.java b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/web/reactive/client/WebClientExchangeTags.java index ff7036886d..13ee23871e 100644 --- a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/web/reactive/client/WebClientExchangeTags.java +++ b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/web/reactive/client/WebClientExchangeTags.java @@ -140,7 +140,7 @@ public final class WebClientExchangeTags { /** * Creates an {@code outcome} {@code Tag} derived from the - * {@link ClientResponse#statusCode() status} of the given {@code response}. + * {@link ClientResponse#rawStatusCode() status} of the given {@code response}. * @param response the response * @return the outcome tag * @since 2.2.0 @@ -154,7 +154,7 @@ public final class WebClientExchangeTags { } } } - catch (IllegalArgumentException exc) { + catch (IllegalArgumentException ex) { // Continue } return OUTCOME_UNKNOWN; diff --git a/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/web/client/RestTemplateExchangeTagsTests.java b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/web/client/RestTemplateExchangeTagsTests.java index 0249339083..ebaa05c2a5 100644 --- a/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/web/client/RestTemplateExchangeTagsTests.java +++ b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/web/client/RestTemplateExchangeTagsTests.java @@ -81,7 +81,7 @@ class RestTemplateExchangeTagsTests { @Test void outcomeTagIsUnknownWhenResponseThrowsIOException() throws Exception { ClientHttpResponse response = mock(ClientHttpResponse.class); - given(response.getStatusCode()).willThrow(IOException.class); + given(response.getRawStatusCode()).willThrow(IOException.class); Tag tag = RestTemplateExchangeTags.outcome(response); assertThat(tag.getValue()).isEqualTo("UNKNOWN"); } @@ -89,7 +89,23 @@ class RestTemplateExchangeTagsTests { @Test void outcomeTagIsUnknownForCustomResponseStatus() throws Exception { ClientHttpResponse response = mock(ClientHttpResponse.class); - given(response.getStatusCode()).willThrow(IllegalArgumentException.class); + given(response.getRawStatusCode()).willThrow(IllegalArgumentException.class); + Tag tag = RestTemplateExchangeTags.outcome(response); + assertThat(tag.getValue()).isEqualTo("UNKNOWN"); + } + + @Test + void outcomeTagIsClientErrorWhenResponseIsNonStandardInClientSeries() throws IOException { + ClientHttpResponse response = mock(ClientHttpResponse.class); + given(response.getRawStatusCode()).willReturn(490); + Tag tag = RestTemplateExchangeTags.outcome(response); + assertThat(tag.getValue()).isEqualTo("CLIENT_ERROR"); + } + + @Test + void outcomeTagIsUnknownWhenResponseStatusIsInUnknownSeries() throws IOException { + ClientHttpResponse response = mock(ClientHttpResponse.class); + given(response.getRawStatusCode()).willReturn(701); Tag tag = RestTemplateExchangeTags.outcome(response); assertThat(tag.getValue()).isEqualTo("UNKNOWN"); } diff --git a/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/web/reactive/client/WebClientExchangeTagsTests.java b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/web/reactive/client/WebClientExchangeTagsTests.java index b296d27f30..94fa8dc8c7 100644 --- a/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/web/reactive/client/WebClientExchangeTagsTests.java +++ b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/web/reactive/client/WebClientExchangeTagsTests.java @@ -148,7 +148,7 @@ class WebClientExchangeTagsTests { } @Test - void outcomeTagIsServerErrorWhenResponseIsNonStandardInKnownSeries() { + void outcomeTagIsClientErrorWhenResponseIsNonStandardInClientSeries() { given(this.response.rawStatusCode()).willReturn(490); Tag tag = WebClientExchangeTags.outcome(this.response); assertThat(tag.getValue()).isEqualTo("CLIENT_ERROR");