Skip to content

Commit

Permalink
Add an outcome tag to RestTemplate metrics similar to that provided f…
Browse files Browse the repository at this point in the history
…or WebMVC and WebFlux #15420
  • Loading branch information
nishantraut committed Jan 6, 2019
1 parent 2153a75 commit 82c4245
Show file tree
Hide file tree
Showing 7 changed files with 229 additions and 4 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
* Default implementation of {@link RestTemplateExchangeTagsProvider}.
*
* @author Jon Schneider
* @author Nishant Raut
* @since 2.0.0
*/
public class DefaultRestTemplateExchangeTagsProvider
Expand All @@ -41,7 +42,8 @@ public Iterable<Tag> getTags(String urlTemplate, HttpRequest request,
: RestTemplateExchangeTags.uri(request));
return Arrays.asList(RestTemplateExchangeTags.method(request), uriTag,
RestTemplateExchangeTags.status(response),
RestTemplateExchangeTags.clientName(request));
RestTemplateExchangeTags.clientName(request),
RestTemplateExchangeTags.outcome(response));
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
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;
Expand All @@ -33,12 +34,25 @@
*
* @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() {
}

Expand Down Expand Up @@ -115,4 +129,44 @@ public static Tag clientName(HttpRequest request) {
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;
}

}

}
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
* Default implementation of {@link WebClientExchangeTagsProvider}.
*
* @author Brian Clozel
* @author Nishant Raut
* @since 2.1.0
*/
public class DefaultWebClientExchangeTagsProvider
Expand All @@ -40,7 +41,8 @@ public Iterable<Tag> tags(ClientRequest request, ClientResponse response,
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,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@

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;
Expand All @@ -31,6 +32,7 @@
* performed by a {@link WebClient}.
*
* @author Brian Clozel
* @author Nishant Raut
* @since 2.1.0
*/
public final class WebClientExchangeTags {
Expand All @@ -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() {
}

Expand Down Expand Up @@ -111,4 +125,33 @@ public static Tag clientName(ClientRequest request) {
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;
}

}
Original file line number Diff line number Diff line change
@@ -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");
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
* Tests for {@link DefaultWebClientExchangeTagsProvider}
*
* @author Brian Clozel
* @author Nishant Raut
*/
public class DefaultWebClientExchangeTagsProviderTests {

Expand Down Expand Up @@ -66,7 +67,7 @@ public void tagsShouldBePopulated() {
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
Expand All @@ -76,7 +77,8 @@ public void tagsWhenNoUriTemplateShouldProvideUriPath() {
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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
* Tests for {@link WebClientExchangeTags}
*
* @author Brian Clozel
* @author Nishant Raut
*/
public class WebClientExchangeTagsTests {

Expand Down Expand Up @@ -113,4 +114,45 @@ public void statusWhenClientException() {
.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");
}

}

0 comments on commit 82c4245

Please sign in to comment.