Skip to content

Commit 537fc5c

Browse files
izeyeshakuzen
authored andcommitted
Improve handling of non-standard status codes in RestTemplate metrics (#1564)
This commit backports spring-projects/spring-boot#17991.
1 parent 6fc2baa commit 537fc5c

File tree

2 files changed

+133
-18
lines changed

2 files changed

+133
-18
lines changed

micrometer-spring-legacy/src/main/java/io/micrometer/spring/web/client/RestTemplateExchangeTags.java

Lines changed: 22 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,9 @@
2525

2626
import java.io.IOException;
2727
import java.net.URI;
28+
import java.util.Collections;
29+
import java.util.HashMap;
30+
import java.util.Map;
2831

2932
/**
3033
* Factory methods for creating {@link Tag Tags} related to a request-response exchange
@@ -47,6 +50,18 @@ public final class RestTemplateExchangeTags {
4750

4851
private static final Tag OUTCOME_SERVER_ERROR = Tag.of("outcome", "SERVER_ERROR");
4952

53+
private static final Map<HttpStatus.Series, Tag> SERIES_OUTCOMES;
54+
55+
static {
56+
Map<HttpStatus.Series, Tag> seriesOutcomes = new HashMap<>();
57+
seriesOutcomes.put(HttpStatus.Series.INFORMATIONAL, OUTCOME_INFORMATIONAL);
58+
seriesOutcomes.put(HttpStatus.Series.SUCCESSFUL, OUTCOME_SUCCESS);
59+
seriesOutcomes.put(HttpStatus.Series.REDIRECTION, OUTCOME_REDIRECTION);
60+
seriesOutcomes.put(HttpStatus.Series.CLIENT_ERROR, OUTCOME_CLIENT_ERROR);
61+
seriesOutcomes.put(HttpStatus.Series.SERVER_ERROR, OUTCOME_SERVER_ERROR);
62+
SERIES_OUTCOMES = Collections.unmodifiableMap(seriesOutcomes);
63+
}
64+
5065
private RestTemplateExchangeTags() {
5166
}
5267

@@ -141,26 +156,15 @@ public static Tag clientName(HttpRequest request) {
141156
public static Tag outcome(ClientHttpResponse response) {
142157
try {
143158
if (response != null) {
144-
HttpStatus statusCode = response.getStatusCode();
145-
if (statusCode.is1xxInformational()) {
146-
return OUTCOME_INFORMATIONAL;
147-
}
148-
if (statusCode.is2xxSuccessful()) {
149-
return OUTCOME_SUCCESS;
150-
}
151-
if (statusCode.is3xxRedirection()) {
152-
return OUTCOME_REDIRECTION;
153-
}
154-
if (statusCode.is4xxClientError()) {
155-
return OUTCOME_CLIENT_ERROR;
156-
}
157-
if (statusCode.is5xxServerError()) {
158-
return OUTCOME_SERVER_ERROR;
159+
HttpStatus.Series series = HttpStatus.Series.valueOf(response.getRawStatusCode());
160+
if (series != null) {
161+
return SERIES_OUTCOMES.getOrDefault(series, OUTCOME_UNKNOWN);
159162
}
160163
}
161-
return OUTCOME_UNKNOWN;
162-
} catch (IOException | IllegalArgumentException e) {
163-
return OUTCOME_UNKNOWN;
164164
}
165+
catch (IOException | IllegalArgumentException ex) {
166+
// Continue
167+
}
168+
return OUTCOME_UNKNOWN;
165169
}
166170
}
Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
/**
2+
* Copyright 2019 Pivotal Software, Inc.
3+
* <p>
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+
* <p>
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
* <p>
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+
package io.micrometer.spring.web.client;
17+
18+
import io.micrometer.core.instrument.Tag;
19+
import org.junit.Test;
20+
import org.springframework.http.HttpStatus;
21+
import org.springframework.http.client.ClientHttpResponse;
22+
import org.springframework.mock.http.client.MockClientHttpResponse;
23+
24+
import java.io.IOException;
25+
26+
import static org.assertj.core.api.Assertions.assertThat;
27+
import static org.mockito.BDDMockito.given;
28+
import static org.mockito.Mockito.mock;
29+
30+
/**
31+
* Tests for {@link RestTemplateExchangeTags}.
32+
*
33+
* @author Nishant Raut
34+
* @author Brian Clozel
35+
*/
36+
public class RestTemplateExchangeTagsTest {
37+
38+
@Test
39+
public void outcomeTagIsUnknownWhenResponseIsNull() {
40+
Tag tag = RestTemplateExchangeTags.outcome(null);
41+
assertThat(tag.getValue()).isEqualTo("UNKNOWN");
42+
}
43+
44+
@Test
45+
public void outcomeTagIsInformationalWhenResponseIs1xx() {
46+
ClientHttpResponse response = new MockClientHttpResponse("foo".getBytes(), HttpStatus.CONTINUE);
47+
Tag tag = RestTemplateExchangeTags.outcome(response);
48+
assertThat(tag.getValue()).isEqualTo("INFORMATIONAL");
49+
}
50+
51+
@Test
52+
public void outcomeTagIsSuccessWhenResponseIs2xx() {
53+
ClientHttpResponse response = new MockClientHttpResponse("foo".getBytes(), HttpStatus.OK);
54+
Tag tag = RestTemplateExchangeTags.outcome(response);
55+
assertThat(tag.getValue()).isEqualTo("SUCCESS");
56+
}
57+
58+
@Test
59+
public void outcomeTagIsRedirectionWhenResponseIs3xx() {
60+
ClientHttpResponse response = new MockClientHttpResponse("foo".getBytes(), HttpStatus.MOVED_PERMANENTLY);
61+
Tag tag = RestTemplateExchangeTags.outcome(response);
62+
assertThat(tag.getValue()).isEqualTo("REDIRECTION");
63+
}
64+
65+
@Test
66+
public void outcomeTagIsClientErrorWhenResponseIs4xx() {
67+
ClientHttpResponse response = new MockClientHttpResponse("foo".getBytes(), HttpStatus.BAD_REQUEST);
68+
Tag tag = RestTemplateExchangeTags.outcome(response);
69+
assertThat(tag.getValue()).isEqualTo("CLIENT_ERROR");
70+
}
71+
72+
@Test
73+
public void outcomeTagIsServerErrorWhenResponseIs5xx() {
74+
ClientHttpResponse response = new MockClientHttpResponse("foo".getBytes(), HttpStatus.BAD_GATEWAY);
75+
Tag tag = RestTemplateExchangeTags.outcome(response);
76+
assertThat(tag.getValue()).isEqualTo("SERVER_ERROR");
77+
}
78+
79+
@Test
80+
public void outcomeTagIsUnknownWhenResponseThrowsIOException() throws Exception {
81+
ClientHttpResponse response = mock(ClientHttpResponse.class);
82+
given(response.getRawStatusCode()).willThrow(IOException.class);
83+
Tag tag = RestTemplateExchangeTags.outcome(response);
84+
assertThat(tag.getValue()).isEqualTo("UNKNOWN");
85+
}
86+
87+
@Test
88+
public void outcomeTagIsUnknownForCustomResponseStatus() throws Exception {
89+
ClientHttpResponse response = mock(ClientHttpResponse.class);
90+
given(response.getRawStatusCode()).willThrow(IllegalArgumentException.class);
91+
Tag tag = RestTemplateExchangeTags.outcome(response);
92+
assertThat(tag.getValue()).isEqualTo("UNKNOWN");
93+
}
94+
95+
@Test
96+
public void outcomeTagIsClientErrorWhenResponseIsNonStandardInClientSeries() throws IOException {
97+
ClientHttpResponse response = mock(ClientHttpResponse.class);
98+
given(response.getRawStatusCode()).willReturn(490);
99+
Tag tag = RestTemplateExchangeTags.outcome(response);
100+
assertThat(tag.getValue()).isEqualTo("CLIENT_ERROR");
101+
}
102+
103+
@Test
104+
public void outcomeTagIsUnknownWhenResponseStatusIsInUnknownSeries() throws IOException {
105+
ClientHttpResponse response = mock(ClientHttpResponse.class);
106+
given(response.getRawStatusCode()).willReturn(701);
107+
Tag tag = RestTemplateExchangeTags.outcome(response);
108+
assertThat(tag.getValue()).isEqualTo("UNKNOWN");
109+
}
110+
111+
}

0 commit comments

Comments
 (0)