Skip to content

Commit 82c4245

Browse files
committed
Add an outcome tag to RestTemplate metrics similar to that provided for WebMVC and WebFlux #15420
1 parent 2153a75 commit 82c4245

File tree

7 files changed

+229
-4
lines changed

7 files changed

+229
-4
lines changed

spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/web/client/DefaultRestTemplateExchangeTagsProvider.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
* Default implementation of {@link RestTemplateExchangeTagsProvider}.
2929
*
3030
* @author Jon Schneider
31+
* @author Nishant Raut
3132
* @since 2.0.0
3233
*/
3334
public class DefaultRestTemplateExchangeTagsProvider
@@ -41,7 +42,8 @@ public Iterable<Tag> getTags(String urlTemplate, HttpRequest request,
4142
: RestTemplateExchangeTags.uri(request));
4243
return Arrays.asList(RestTemplateExchangeTags.method(request), uriTag,
4344
RestTemplateExchangeTags.status(response),
44-
RestTemplateExchangeTags.clientName(request));
45+
RestTemplateExchangeTags.clientName(request),
46+
RestTemplateExchangeTags.outcome(response));
4547
}
4648

4749
}

spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/web/client/RestTemplateExchangeTags.java

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import io.micrometer.core.instrument.Tag;
2424

2525
import org.springframework.http.HttpRequest;
26+
import org.springframework.http.HttpStatus;
2627
import org.springframework.http.client.ClientHttpResponse;
2728
import org.springframework.util.StringUtils;
2829
import org.springframework.web.client.RestTemplate;
@@ -33,12 +34,25 @@
3334
*
3435
* @author Andy Wilkinson
3536
* @author Jon Schneider
37+
* @author Nishant Raut
3638
* @since 2.0.0
3739
*/
3840
public final class RestTemplateExchangeTags {
3941

4042
private static final Pattern STRIP_URI_PATTERN = Pattern.compile("^https?://[^/]+/");
4143

44+
private static final Tag OUTCOME_UNKNOWN = Tag.of("outcome", "UNKNOWN");
45+
46+
private static final Tag OUTCOME_INFORMATIONAL = Tag.of("outcome", "INFORMATIONAL");
47+
48+
private static final Tag OUTCOME_SUCCESS = Tag.of("outcome", "SUCCESS");
49+
50+
private static final Tag OUTCOME_REDIRECTION = Tag.of("outcome", "REDIRECTION");
51+
52+
private static final Tag OUTCOME_CLIENT_ERROR = Tag.of("outcome", "CLIENT_ERROR");
53+
54+
private static final Tag OUTCOME_SERVER_ERROR = Tag.of("outcome", "SERVER_ERROR");
55+
4256
private RestTemplateExchangeTags() {
4357
}
4458

@@ -115,4 +129,44 @@ public static Tag clientName(HttpRequest request) {
115129
return Tag.of("clientName", host);
116130
}
117131

132+
/**
133+
* Creates a {@code outcome} {@code Tag} derived from the
134+
* {@link ClientHttpResponse#getStatusCode() status} of the given {@code response}.
135+
* @param response the response
136+
* @return the outcome tag
137+
* @since 2.2.0
138+
*/
139+
public static Tag outcome(ClientHttpResponse response) {
140+
if (response != null) {
141+
HttpStatus status = extractStatus(response);
142+
if (status != null) {
143+
if (status.is1xxInformational()) {
144+
return OUTCOME_INFORMATIONAL;
145+
}
146+
if (status.is2xxSuccessful()) {
147+
return OUTCOME_SUCCESS;
148+
}
149+
if (status.is3xxRedirection()) {
150+
return OUTCOME_REDIRECTION;
151+
}
152+
if (status.is4xxClientError()) {
153+
return OUTCOME_CLIENT_ERROR;
154+
}
155+
}
156+
return OUTCOME_SERVER_ERROR;
157+
}
158+
return OUTCOME_UNKNOWN;
159+
}
160+
161+
private static HttpStatus extractStatus(ClientHttpResponse response) {
162+
163+
try {
164+
return response.getStatusCode();
165+
}
166+
catch (IOException ex) {
167+
return null;
168+
}
169+
170+
}
171+
118172
}

spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/web/reactive/client/DefaultWebClientExchangeTagsProvider.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
* Default implementation of {@link WebClientExchangeTagsProvider}.
2828
*
2929
* @author Brian Clozel
30+
* @author Nishant Raut
3031
* @since 2.1.0
3132
*/
3233
public class DefaultWebClientExchangeTagsProvider
@@ -40,7 +41,8 @@ public Iterable<Tag> tags(ClientRequest request, ClientResponse response,
4041
Tag clientName = WebClientExchangeTags.clientName(request);
4142
if (response != null) {
4243
return Arrays.asList(method, uri, clientName,
43-
WebClientExchangeTags.status(response));
44+
WebClientExchangeTags.status(response),
45+
WebClientExchangeTags.outcome(response));
4446
}
4547
else {
4648
return Arrays.asList(method, uri, clientName,

spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/web/reactive/client/WebClientExchangeTags.java

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121

2222
import io.micrometer.core.instrument.Tag;
2323

24+
import org.springframework.http.HttpStatus;
2425
import org.springframework.http.client.reactive.ClientHttpRequest;
2526
import org.springframework.web.reactive.function.client.ClientRequest;
2627
import org.springframework.web.reactive.function.client.ClientResponse;
@@ -31,6 +32,7 @@
3132
* performed by a {@link WebClient}.
3233
*
3334
* @author Brian Clozel
35+
* @author Nishant Raut
3436
* @since 2.1.0
3537
*/
3638
public final class WebClientExchangeTags {
@@ -47,6 +49,18 @@ public final class WebClientExchangeTags {
4749

4850
private static final Tag CLIENT_NAME_NONE = Tag.of("clientName", "none");
4951

52+
private static final Tag OUTCOME_UNKNOWN = Tag.of("outcome", "UNKNOWN");
53+
54+
private static final Tag OUTCOME_INFORMATIONAL = Tag.of("outcome", "INFORMATIONAL");
55+
56+
private static final Tag OUTCOME_SUCCESS = Tag.of("outcome", "SUCCESS");
57+
58+
private static final Tag OUTCOME_REDIRECTION = Tag.of("outcome", "REDIRECTION");
59+
60+
private static final Tag OUTCOME_CLIENT_ERROR = Tag.of("outcome", "CLIENT_ERROR");
61+
62+
private static final Tag OUTCOME_SERVER_ERROR = Tag.of("outcome", "SERVER_ERROR");
63+
5064
private WebClientExchangeTags() {
5165
}
5266

@@ -111,4 +125,33 @@ public static Tag clientName(ClientRequest request) {
111125
return Tag.of("clientName", host);
112126
}
113127

128+
/**
129+
* Creates a {@code outcome} {@code Tag} derived from the
130+
* {@link ClientResponse#statusCode() status} of the given {@code response}.
131+
* @param response the response
132+
* @return the outcome tag
133+
* @since 2.2.0
134+
*/
135+
public static Tag outcome(ClientResponse response) {
136+
if (response != null) {
137+
HttpStatus status = response.statusCode();
138+
if (status != null) {
139+
if (status.is1xxInformational()) {
140+
return OUTCOME_INFORMATIONAL;
141+
}
142+
if (status.is2xxSuccessful()) {
143+
return OUTCOME_SUCCESS;
144+
}
145+
if (status.is3xxRedirection()) {
146+
return OUTCOME_REDIRECTION;
147+
}
148+
if (status.is4xxClientError()) {
149+
return OUTCOME_CLIENT_ERROR;
150+
}
151+
}
152+
return OUTCOME_SERVER_ERROR;
153+
}
154+
return OUTCOME_UNKNOWN;
155+
}
156+
114157
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
/*
2+
* Copyright 2012-2018 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.boot.actuate.metrics.web.client;
18+
19+
import io.micrometer.core.instrument.Tag;
20+
import org.junit.Test;
21+
22+
import org.springframework.http.HttpStatus;
23+
import org.springframework.mock.http.client.MockClientHttpResponse;
24+
25+
import static org.assertj.core.api.Assertions.assertThat;
26+
27+
/**
28+
* Tests for {@link RestTemplateExchangeTags}.
29+
*
30+
* @author Nishant Raut
31+
*/
32+
public class RestTemplateExchangeTagsTests {
33+
34+
private MockClientHttpResponse response;
35+
36+
@Test
37+
public void outcomeTagIsUnknownWhenResponseStatusIsNull() {
38+
Tag tag = RestTemplateExchangeTags.outcome(null);
39+
assertThat(tag.getValue()).isEqualTo("UNKNOWN");
40+
}
41+
42+
@Test
43+
public void outcomeTagIsInformationalWhenResponseIs1xx() {
44+
this.response = new MockClientHttpResponse("foo".getBytes(), HttpStatus.CONTINUE);
45+
Tag tag = RestTemplateExchangeTags.outcome(this.response);
46+
assertThat(tag.getValue()).isEqualTo("INFORMATIONAL");
47+
}
48+
49+
@Test
50+
public void outcomeTagIsSuccessWhenResponseIs2xx() {
51+
this.response = new MockClientHttpResponse("foo".getBytes(), HttpStatus.OK);
52+
Tag tag = RestTemplateExchangeTags.outcome(this.response);
53+
assertThat(tag.getValue()).isEqualTo("SUCCESS");
54+
}
55+
56+
@Test
57+
public void outcomeTagIsRedirectionWhenResponseIs3xx() {
58+
this.response = new MockClientHttpResponse("foo".getBytes(),
59+
HttpStatus.MOVED_PERMANENTLY);
60+
Tag tag = RestTemplateExchangeTags.outcome(this.response);
61+
assertThat(tag.getValue()).isEqualTo("REDIRECTION");
62+
}
63+
64+
@Test
65+
public void outcomeTagIsClientErrorWhenResponseIs4xx() {
66+
this.response = new MockClientHttpResponse("foo".getBytes(),
67+
HttpStatus.BAD_REQUEST);
68+
Tag tag = RestTemplateExchangeTags.outcome(this.response);
69+
assertThat(tag.getValue()).isEqualTo("CLIENT_ERROR");
70+
}
71+
72+
@Test
73+
public void outcomeTagIsServerErrorWhenResponseIs5xx() {
74+
this.response = new MockClientHttpResponse("foo".getBytes(),
75+
HttpStatus.BAD_GATEWAY);
76+
Tag tag = RestTemplateExchangeTags.outcome(this.response);
77+
assertThat(tag.getValue()).isEqualTo("SERVER_ERROR");
78+
}
79+
80+
}

spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/web/reactive/client/DefaultWebClientExchangeTagsProviderTests.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
* Tests for {@link DefaultWebClientExchangeTagsProvider}
3838
*
3939
* @author Brian Clozel
40+
* @author Nishant Raut
4041
*/
4142
public class DefaultWebClientExchangeTagsProviderTests {
4243

@@ -66,7 +67,7 @@ public void tagsShouldBePopulated() {
6667
Iterable<Tag> tags = this.tagsProvider.tags(this.request, this.response, null);
6768
assertThat(tags).containsExactlyInAnyOrder(Tag.of("method", "GET"),
6869
Tag.of("uri", "/projects/{project}"), Tag.of("clientName", "example.org"),
69-
Tag.of("status", "200"));
70+
Tag.of("status", "200"), Tag.of("outcome", "SUCCESS"));
7071
}
7172

7273
@Test
@@ -76,7 +77,8 @@ public void tagsWhenNoUriTemplateShouldProvideUriPath() {
7677
Iterable<Tag> tags = this.tagsProvider.tags(request, this.response, null);
7778
assertThat(tags).containsExactlyInAnyOrder(Tag.of("method", "GET"),
7879
Tag.of("uri", "/projects/spring-boot"),
79-
Tag.of("clientName", "example.org"), Tag.of("status", "200"));
80+
Tag.of("clientName", "example.org"), Tag.of("status", "200"),
81+
Tag.of("outcome", "SUCCESS"));
8082
}
8183

8284
@Test

spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/web/reactive/client/WebClientExchangeTagsTests.java

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
* Tests for {@link WebClientExchangeTags}
3838
*
3939
* @author Brian Clozel
40+
* @author Nishant Raut
4041
*/
4142
public class WebClientExchangeTagsTests {
4243

@@ -113,4 +114,45 @@ public void statusWhenClientException() {
113114
.isEqualTo(Tag.of("status", "CLIENT_ERROR"));
114115
}
115116

117+
@Test
118+
public void outcomeTagIsUnknownWhenResponseStatusIsNull() {
119+
Tag tag = WebClientExchangeTags.outcome(null);
120+
assertThat(tag.getValue()).isEqualTo("UNKNOWN");
121+
}
122+
123+
@Test
124+
public void outcomeTagIsInformationalWhenResponseIs1xx() {
125+
given(this.response.statusCode()).willReturn(HttpStatus.CONTINUE);
126+
Tag tag = WebClientExchangeTags.outcome(this.response);
127+
assertThat(tag.getValue()).isEqualTo("INFORMATIONAL");
128+
}
129+
130+
@Test
131+
public void outcomeTagIsSuccessWhenResponseIs2xx() {
132+
given(this.response.statusCode()).willReturn(HttpStatus.OK);
133+
Tag tag = WebClientExchangeTags.outcome(this.response);
134+
assertThat(tag.getValue()).isEqualTo("SUCCESS");
135+
}
136+
137+
@Test
138+
public void outcomeTagIsRedirectionWhenResponseIs3xx() {
139+
given(this.response.statusCode()).willReturn(HttpStatus.MOVED_PERMANENTLY);
140+
Tag tag = WebClientExchangeTags.outcome(this.response);
141+
assertThat(tag.getValue()).isEqualTo("REDIRECTION");
142+
}
143+
144+
@Test
145+
public void outcomeTagIsClientErrorWhenResponseIs4xx() {
146+
given(this.response.statusCode()).willReturn(HttpStatus.BAD_REQUEST);
147+
Tag tag = WebClientExchangeTags.outcome(this.response);
148+
assertThat(tag.getValue()).isEqualTo("CLIENT_ERROR");
149+
}
150+
151+
@Test
152+
public void outcomeTagIsServerErrorWhenResponseIs5xx() {
153+
given(this.response.statusCode()).willReturn(HttpStatus.BAD_GATEWAY);
154+
Tag tag = WebClientExchangeTags.outcome(this.response);
155+
assertThat(tag.getValue()).isEqualTo("SERVER_ERROR");
156+
}
157+
116158
}

0 commit comments

Comments
 (0)