Add an outcome tag to web client metrics
Similar to what's ben done in gh-15420 for Spring MVC and Spring WebFlux, this commit adds an outcome tag for the client side on both `RestTemplate` and `WebClient`. See gh-15594
This commit is contained in:
parent
a83d9635af
commit
21df40b6c7
|
@ -28,6 +28,7 @@ import org.springframework.util.StringUtils;
|
|||
* Default implementation of {@link RestTemplateExchangeTagsProvider}.
|
||||
*
|
||||
* @author Jon Schneider
|
||||
* @author Nishant Raut
|
||||
* @since 2.0.0
|
||||
*/
|
||||
public class DefaultRestTemplateExchangeTagsProvider
|
||||
|
@ -41,7 +42,8 @@ public class DefaultRestTemplateExchangeTagsProvider
|
|||
: RestTemplateExchangeTags.uri(request));
|
||||
return Arrays.asList(RestTemplateExchangeTags.method(request), uriTag,
|
||||
RestTemplateExchangeTags.status(response),
|
||||
RestTemplateExchangeTags.clientName(request));
|
||||
RestTemplateExchangeTags.clientName(request),
|
||||
RestTemplateExchangeTags.outcome(response));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -23,6 +23,7 @@ import java.util.regex.Pattern;
|
|||
import io.micrometer.core.instrument.Tag;
|
||||
|
||||
import org.springframework.http.HttpRequest;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.client.ClientHttpResponse;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
@ -33,12 +34,25 @@ import org.springframework.web.client.RestTemplate;
|
|||
*
|
||||
* @author Andy Wilkinson
|
||||
* @author Jon Schneider
|
||||
* @author Nishant Raut
|
||||
* @since 2.0.0
|
||||
*/
|
||||
public final class RestTemplateExchangeTags {
|
||||
|
||||
private static final Pattern STRIP_URI_PATTERN = Pattern.compile("^https?://[^/]+/");
|
||||
|
||||
private static final Tag OUTCOME_UNKNOWN = Tag.of("outcome", "UNKNOWN");
|
||||
|
||||
private static final Tag OUTCOME_INFORMATIONAL = Tag.of("outcome", "INFORMATIONAL");
|
||||
|
||||
private static final Tag OUTCOME_SUCCESS = Tag.of("outcome", "SUCCESS");
|
||||
|
||||
private static final Tag OUTCOME_REDIRECTION = Tag.of("outcome", "REDIRECTION");
|
||||
|
||||
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 RestTemplateExchangeTags() {
|
||||
}
|
||||
|
||||
|
@ -115,4 +129,44 @@ public final class RestTemplateExchangeTags {
|
|||
return Tag.of("clientName", host);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@code outcome} {@code Tag} derived from the
|
||||
* {@link ClientHttpResponse#getStatusCode() status} of the given {@code response}.
|
||||
* @param response the response
|
||||
* @return the outcome tag
|
||||
* @since 2.2.0
|
||||
*/
|
||||
public static Tag outcome(ClientHttpResponse response) {
|
||||
if (response != null) {
|
||||
HttpStatus status = extractStatus(response);
|
||||
if (status != null) {
|
||||
if (status.is1xxInformational()) {
|
||||
return OUTCOME_INFORMATIONAL;
|
||||
}
|
||||
if (status.is2xxSuccessful()) {
|
||||
return OUTCOME_SUCCESS;
|
||||
}
|
||||
if (status.is3xxRedirection()) {
|
||||
return OUTCOME_REDIRECTION;
|
||||
}
|
||||
if (status.is4xxClientError()) {
|
||||
return OUTCOME_CLIENT_ERROR;
|
||||
}
|
||||
}
|
||||
return OUTCOME_SERVER_ERROR;
|
||||
}
|
||||
return OUTCOME_UNKNOWN;
|
||||
}
|
||||
|
||||
private static HttpStatus extractStatus(ClientHttpResponse response) {
|
||||
|
||||
try {
|
||||
return response.getStatusCode();
|
||||
}
|
||||
catch (IOException ex) {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -27,6 +27,7 @@ import org.springframework.web.reactive.function.client.ClientResponse;
|
|||
* Default implementation of {@link WebClientExchangeTagsProvider}.
|
||||
*
|
||||
* @author Brian Clozel
|
||||
* @author Nishant Raut
|
||||
* @since 2.1.0
|
||||
*/
|
||||
public class DefaultWebClientExchangeTagsProvider
|
||||
|
@ -40,7 +41,8 @@ public class DefaultWebClientExchangeTagsProvider
|
|||
Tag clientName = WebClientExchangeTags.clientName(request);
|
||||
if (response != null) {
|
||||
return Arrays.asList(method, uri, clientName,
|
||||
WebClientExchangeTags.status(response));
|
||||
WebClientExchangeTags.status(response),
|
||||
WebClientExchangeTags.outcome(response));
|
||||
}
|
||||
else {
|
||||
return Arrays.asList(method, uri, clientName,
|
||||
|
|
|
@ -21,6 +21,7 @@ import java.util.regex.Pattern;
|
|||
|
||||
import io.micrometer.core.instrument.Tag;
|
||||
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.client.reactive.ClientHttpRequest;
|
||||
import org.springframework.web.reactive.function.client.ClientRequest;
|
||||
import org.springframework.web.reactive.function.client.ClientResponse;
|
||||
|
@ -31,6 +32,7 @@ import org.springframework.web.reactive.function.client.WebClient;
|
|||
* performed by a {@link WebClient}.
|
||||
*
|
||||
* @author Brian Clozel
|
||||
* @author Nishant Raut
|
||||
* @since 2.1.0
|
||||
*/
|
||||
public final class WebClientExchangeTags {
|
||||
|
@ -47,6 +49,18 @@ public final class WebClientExchangeTags {
|
|||
|
||||
private static final Tag CLIENT_NAME_NONE = Tag.of("clientName", "none");
|
||||
|
||||
private static final Tag OUTCOME_UNKNOWN = Tag.of("outcome", "UNKNOWN");
|
||||
|
||||
private static final Tag OUTCOME_INFORMATIONAL = Tag.of("outcome", "INFORMATIONAL");
|
||||
|
||||
private static final Tag OUTCOME_SUCCESS = Tag.of("outcome", "SUCCESS");
|
||||
|
||||
private static final Tag OUTCOME_REDIRECTION = Tag.of("outcome", "REDIRECTION");
|
||||
|
||||
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 WebClientExchangeTags() {
|
||||
}
|
||||
|
||||
|
@ -111,4 +125,33 @@ public final class WebClientExchangeTags {
|
|||
return Tag.of("clientName", host);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@code outcome} {@code Tag} derived from the
|
||||
* {@link ClientResponse#statusCode() status} of the given {@code response}.
|
||||
* @param response the response
|
||||
* @return the outcome tag
|
||||
* @since 2.2.0
|
||||
*/
|
||||
public static Tag outcome(ClientResponse response) {
|
||||
if (response != null) {
|
||||
HttpStatus status = response.statusCode();
|
||||
if (status != null) {
|
||||
if (status.is1xxInformational()) {
|
||||
return OUTCOME_INFORMATIONAL;
|
||||
}
|
||||
if (status.is2xxSuccessful()) {
|
||||
return OUTCOME_SUCCESS;
|
||||
}
|
||||
if (status.is3xxRedirection()) {
|
||||
return OUTCOME_REDIRECTION;
|
||||
}
|
||||
if (status.is4xxClientError()) {
|
||||
return OUTCOME_CLIENT_ERROR;
|
||||
}
|
||||
}
|
||||
return OUTCOME_SERVER_ERROR;
|
||||
}
|
||||
return OUTCOME_UNKNOWN;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,80 @@
|
|||
/*
|
||||
* Copyright 2012-2018 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.actuate.metrics.web.client;
|
||||
|
||||
import io.micrometer.core.instrument.Tag;
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.mock.http.client.MockClientHttpResponse;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Tests for {@link RestTemplateExchangeTags}.
|
||||
*
|
||||
* @author Nishant Raut
|
||||
*/
|
||||
public class RestTemplateExchangeTagsTests {
|
||||
|
||||
private MockClientHttpResponse response;
|
||||
|
||||
@Test
|
||||
public void outcomeTagIsUnknownWhenResponseStatusIsNull() {
|
||||
Tag tag = RestTemplateExchangeTags.outcome(null);
|
||||
assertThat(tag.getValue()).isEqualTo("UNKNOWN");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void outcomeTagIsInformationalWhenResponseIs1xx() {
|
||||
this.response = new MockClientHttpResponse("foo".getBytes(), HttpStatus.CONTINUE);
|
||||
Tag tag = RestTemplateExchangeTags.outcome(this.response);
|
||||
assertThat(tag.getValue()).isEqualTo("INFORMATIONAL");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void outcomeTagIsSuccessWhenResponseIs2xx() {
|
||||
this.response = new MockClientHttpResponse("foo".getBytes(), HttpStatus.OK);
|
||||
Tag tag = RestTemplateExchangeTags.outcome(this.response);
|
||||
assertThat(tag.getValue()).isEqualTo("SUCCESS");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void outcomeTagIsRedirectionWhenResponseIs3xx() {
|
||||
this.response = new MockClientHttpResponse("foo".getBytes(),
|
||||
HttpStatus.MOVED_PERMANENTLY);
|
||||
Tag tag = RestTemplateExchangeTags.outcome(this.response);
|
||||
assertThat(tag.getValue()).isEqualTo("REDIRECTION");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void outcomeTagIsClientErrorWhenResponseIs4xx() {
|
||||
this.response = new MockClientHttpResponse("foo".getBytes(),
|
||||
HttpStatus.BAD_REQUEST);
|
||||
Tag tag = RestTemplateExchangeTags.outcome(this.response);
|
||||
assertThat(tag.getValue()).isEqualTo("CLIENT_ERROR");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void outcomeTagIsServerErrorWhenResponseIs5xx() {
|
||||
this.response = new MockClientHttpResponse("foo".getBytes(),
|
||||
HttpStatus.BAD_GATEWAY);
|
||||
Tag tag = RestTemplateExchangeTags.outcome(this.response);
|
||||
assertThat(tag.getValue()).isEqualTo("SERVER_ERROR");
|
||||
}
|
||||
|
||||
}
|
|
@ -37,6 +37,7 @@ import static org.mockito.Mockito.mock;
|
|||
* Tests for {@link DefaultWebClientExchangeTagsProvider}
|
||||
*
|
||||
* @author Brian Clozel
|
||||
* @author Nishant Raut
|
||||
*/
|
||||
public class DefaultWebClientExchangeTagsProviderTests {
|
||||
|
||||
|
@ -66,7 +67,7 @@ public class DefaultWebClientExchangeTagsProviderTests {
|
|||
Iterable<Tag> tags = this.tagsProvider.tags(this.request, this.response, null);
|
||||
assertThat(tags).containsExactlyInAnyOrder(Tag.of("method", "GET"),
|
||||
Tag.of("uri", "/projects/{project}"), Tag.of("clientName", "example.org"),
|
||||
Tag.of("status", "200"));
|
||||
Tag.of("status", "200"), Tag.of("outcome", "SUCCESS"));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -76,7 +77,8 @@ public class DefaultWebClientExchangeTagsProviderTests {
|
|||
Iterable<Tag> tags = this.tagsProvider.tags(request, this.response, null);
|
||||
assertThat(tags).containsExactlyInAnyOrder(Tag.of("method", "GET"),
|
||||
Tag.of("uri", "/projects/spring-boot"),
|
||||
Tag.of("clientName", "example.org"), Tag.of("status", "200"));
|
||||
Tag.of("clientName", "example.org"), Tag.of("status", "200"),
|
||||
Tag.of("outcome", "SUCCESS"));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
@ -37,6 +37,7 @@ import static org.mockito.Mockito.mock;
|
|||
* Tests for {@link WebClientExchangeTags}
|
||||
*
|
||||
* @author Brian Clozel
|
||||
* @author Nishant Raut
|
||||
*/
|
||||
public class WebClientExchangeTagsTests {
|
||||
|
||||
|
@ -113,4 +114,45 @@ public class WebClientExchangeTagsTests {
|
|||
.isEqualTo(Tag.of("status", "CLIENT_ERROR"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void outcomeTagIsUnknownWhenResponseStatusIsNull() {
|
||||
Tag tag = WebClientExchangeTags.outcome(null);
|
||||
assertThat(tag.getValue()).isEqualTo("UNKNOWN");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void outcomeTagIsInformationalWhenResponseIs1xx() {
|
||||
given(this.response.statusCode()).willReturn(HttpStatus.CONTINUE);
|
||||
Tag tag = WebClientExchangeTags.outcome(this.response);
|
||||
assertThat(tag.getValue()).isEqualTo("INFORMATIONAL");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void outcomeTagIsSuccessWhenResponseIs2xx() {
|
||||
given(this.response.statusCode()).willReturn(HttpStatus.OK);
|
||||
Tag tag = WebClientExchangeTags.outcome(this.response);
|
||||
assertThat(tag.getValue()).isEqualTo("SUCCESS");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void outcomeTagIsRedirectionWhenResponseIs3xx() {
|
||||
given(this.response.statusCode()).willReturn(HttpStatus.MOVED_PERMANENTLY);
|
||||
Tag tag = WebClientExchangeTags.outcome(this.response);
|
||||
assertThat(tag.getValue()).isEqualTo("REDIRECTION");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void outcomeTagIsClientErrorWhenResponseIs4xx() {
|
||||
given(this.response.statusCode()).willReturn(HttpStatus.BAD_REQUEST);
|
||||
Tag tag = WebClientExchangeTags.outcome(this.response);
|
||||
assertThat(tag.getValue()).isEqualTo("CLIENT_ERROR");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void outcomeTagIsServerErrorWhenResponseIs5xx() {
|
||||
given(this.response.statusCode()).willReturn(HttpStatus.BAD_GATEWAY);
|
||||
Tag tag = WebClientExchangeTags.outcome(this.response);
|
||||
assertThat(tag.getValue()).isEqualTo("SERVER_ERROR");
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue