Skip to content

Commit 0f88ea6

Browse files
committed
Merge pull request #14486 from Michael McFadyen
* gh-14486: Polish "Add outcome tag to MVC and WebFlux HTTP request metrics" Add outcome tag to MVC and WebFlux HTTP request metrics
2 parents 6da483f + c974192 commit 0f88ea6

File tree

7 files changed

+210
-14
lines changed

7 files changed

+210
-14
lines changed

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,8 @@ public class DefaultWebFluxTagsProvider implements WebFluxTagsProvider {
3535
public Iterable<Tag> httpRequestTags(ServerWebExchange exchange,
3636
Throwable exception) {
3737
return Arrays.asList(WebFluxTags.method(exchange), WebFluxTags.uri(exchange),
38-
WebFluxTags.exception(exception), WebFluxTags.status(exchange));
38+
WebFluxTags.exception(exception), WebFluxTags.status(exchange),
39+
WebFluxTags.outcome(exchange));
3940
}
4041

4142
}

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

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
*
3131
* @author Jon Schneider
3232
* @author Andy Wilkinson
33+
* @author Michael McFadyen
3334
* @since 2.0.0
3435
*/
3536
public final class WebFluxTags {
@@ -42,6 +43,18 @@ public final class WebFluxTags {
4243

4344
private static final Tag EXCEPTION_NONE = Tag.of("exception", "None");
4445

46+
private static final Tag OUTCOME_UNKNOWN = Tag.of("outcome", "UNKNOWN");
47+
48+
private static final Tag OUTCOME_INFORMATIONAL = Tag.of("outcome", "INFORMATIONAL");
49+
50+
private static final Tag OUTCOME_SUCCESS = Tag.of("outcome", "SUCCESS");
51+
52+
private static final Tag OUTCOME_REDIRECTION = Tag.of("outcome", "REDIRECTION");
53+
54+
private static final Tag OUTCOME_CLIENT_ERROR = Tag.of("outcome", "CLIENT_ERROR");
55+
56+
private static final Tag OUTCOME_SERVER_ERROR = Tag.of("outcome", "SERVER_ERROR");
57+
4558
private WebFluxTags() {
4659
}
4760

@@ -112,4 +125,30 @@ public static Tag exception(Throwable exception) {
112125
return EXCEPTION_NONE;
113126
}
114127

128+
/**
129+
* Creates a {@code outcome} tag based on the response status of the given
130+
* {@code exchange}.
131+
* @param exchange the exchange
132+
* @return the outcome tag derived from the response status
133+
*/
134+
public static Tag outcome(ServerWebExchange exchange) {
135+
HttpStatus status = exchange.getResponse().getStatusCode();
136+
if (status != null) {
137+
if (status.is1xxInformational()) {
138+
return OUTCOME_INFORMATIONAL;
139+
}
140+
if (status.is2xxSuccessful()) {
141+
return OUTCOME_SUCCESS;
142+
}
143+
if (status.is3xxRedirection()) {
144+
return OUTCOME_REDIRECTION;
145+
}
146+
if (status.is4xxClientError()) {
147+
return OUTCOME_CLIENT_ERROR;
148+
}
149+
return OUTCOME_SERVER_ERROR;
150+
}
151+
return OUTCOME_UNKNOWN;
152+
}
153+
115154
}

spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/web/servlet/DefaultWebMvcTagsProvider.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,8 @@ public class DefaultWebMvcTagsProvider implements WebMvcTagsProvider {
3434
public Iterable<Tag> getTags(HttpServletRequest request, HttpServletResponse response,
3535
Object handler, Throwable exception) {
3636
return Tags.of(WebMvcTags.method(request), WebMvcTags.uri(request, response),
37-
WebMvcTags.exception(exception), WebMvcTags.status(response));
37+
WebMvcTags.exception(exception), WebMvcTags.status(response),
38+
WebMvcTags.outcome(response));
3839
}
3940

4041
@Override

spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/web/servlet/WebMvcTags.java

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
* @author Jon Schneider
3535
* @author Andy Wilkinson
3636
* @author Brian Clozel
37+
* @author Michael McFadyen
3738
* @since 2.0.0
3839
*/
3940
public final class WebMvcTags {
@@ -50,6 +51,18 @@ public final class WebMvcTags {
5051

5152
private static final Tag STATUS_UNKNOWN = Tag.of("status", "UNKNOWN");
5253

54+
private static final Tag OUTCOME_UNKNOWN = Tag.of("outcome", "UNKNOWN");
55+
56+
private static final Tag OUTCOME_INFORMATIONAL = Tag.of("outcome", "INFORMATIONAL");
57+
58+
private static final Tag OUTCOME_SUCCESS = Tag.of("outcome", "SUCCESS");
59+
60+
private static final Tag OUTCOME_REDIRECTION = Tag.of("outcome", "REDIRECTION");
61+
62+
private static final Tag OUTCOME_CLIENT_ERROR = Tag.of("outcome", "CLIENT_ERROR");
63+
64+
private static final Tag OUTCOME_SERVER_ERROR = Tag.of("outcome", "SERVER_ERROR");
65+
5366
private static final Tag METHOD_UNKNOWN = Tag.of("method", "UNKNOWN");
5467

5568
private static final Pattern TRAILING_SLASH_PATTERN = Pattern.compile("/$");
@@ -149,4 +162,29 @@ public static Tag exception(Throwable exception) {
149162
return EXCEPTION_NONE;
150163
}
151164

165+
/**
166+
* Creates a {@code outcome} tag based on the status of the given {@code response}.
167+
* @param response the HTTP response
168+
* @return the outcome tag derived from the status of the response
169+
*/
170+
public static Tag outcome(HttpServletResponse response) {
171+
if (response != null) {
172+
int status = response.getStatus();
173+
if (status < 200) {
174+
return OUTCOME_INFORMATIONAL;
175+
}
176+
if (status < 300) {
177+
return OUTCOME_SUCCESS;
178+
}
179+
if (status < 400) {
180+
return OUTCOME_REDIRECTION;
181+
}
182+
else if (status < 500) {
183+
return OUTCOME_CLIENT_ERROR;
184+
}
185+
return OUTCOME_SERVER_ERROR;
186+
}
187+
return OUTCOME_UNKNOWN;
188+
}
189+
152190
}

spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/web/servlet/WebMvcTagsTests.java

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
*
3232
* @author Andy Wilkinson
3333
* @author Brian Clozel
34+
* @author Michael McFadyen
3435
*/
3536
public class WebMvcTagsTests {
3637

@@ -91,4 +92,45 @@ public void uriTagIsUnknownWhenRequestIsNull() {
9192
assertThat(tag.getValue()).isEqualTo("UNKNOWN");
9293
}
9394

95+
@Test
96+
public void outcomeTagIsUnknownWhenResponseIsNull() {
97+
Tag tag = WebMvcTags.outcome(null);
98+
assertThat(tag.getValue()).isEqualTo("UNKNOWN");
99+
}
100+
101+
@Test
102+
public void outcomeTagIsInformationalWhenResponseIs1xx() {
103+
this.response.setStatus(100);
104+
Tag tag = WebMvcTags.outcome(this.response);
105+
assertThat(tag.getValue()).isEqualTo("INFORMATIONAL");
106+
}
107+
108+
@Test
109+
public void outcomeTagIsSuccessWhenResponseIs2xx() {
110+
this.response.setStatus(200);
111+
Tag tag = WebMvcTags.outcome(this.response);
112+
assertThat(tag.getValue()).isEqualTo("SUCCESS");
113+
}
114+
115+
@Test
116+
public void outcomeTagIsRedirectionWhenResponseIs3xx() {
117+
this.response.setStatus(301);
118+
Tag tag = WebMvcTags.outcome(this.response);
119+
assertThat(tag.getValue()).isEqualTo("REDIRECTION");
120+
}
121+
122+
@Test
123+
public void outcomeTagIsClientErrorWhenResponseIs4xx() {
124+
this.response.setStatus(400);
125+
Tag tag = WebMvcTags.outcome(this.response);
126+
assertThat(tag.getValue()).isEqualTo("CLIENT_ERROR");
127+
}
128+
129+
@Test
130+
public void outcomeTagIsServerErrorWhenResponseIs5xx() {
131+
this.response.setStatus(500);
132+
Tag tag = WebMvcTags.outcome(this.response);
133+
assertThat(tag.getValue()).isEqualTo("SERVER_ERROR");
134+
}
135+
94136
}

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

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
* Tests for {@link WebFluxTags}.
3737
*
3838
* @author Brian Clozel
39+
* @author Michael McFadyen
3940
*/
4041
public class WebFluxTagsTests {
4142

@@ -88,4 +89,46 @@ public void methodTagToleratesNonStandardHttpMethods() {
8889
assertThat(tag.getValue()).isEqualTo("CUSTOM");
8990
}
9091

92+
@Test
93+
public void outcomeTagIsUnknownWhenResponseStatusIsNull() {
94+
this.exchange.getResponse().setStatusCode(null);
95+
Tag tag = WebFluxTags.outcome(this.exchange);
96+
assertThat(tag.getValue()).isEqualTo("UNKNOWN");
97+
}
98+
99+
@Test
100+
public void outcomeTagIsInformationalWhenResponseIs1xx() {
101+
this.exchange.getResponse().setStatusCode(HttpStatus.CONTINUE);
102+
Tag tag = WebFluxTags.outcome(this.exchange);
103+
assertThat(tag.getValue()).isEqualTo("INFORMATIONAL");
104+
}
105+
106+
@Test
107+
public void outcomeTagIsSuccessWhenResponseIs2xx() {
108+
this.exchange.getResponse().setStatusCode(HttpStatus.OK);
109+
Tag tag = WebFluxTags.outcome(this.exchange);
110+
assertThat(tag.getValue()).isEqualTo("SUCCESS");
111+
}
112+
113+
@Test
114+
public void outcomeTagIsRedirectionWhenResponseIs3xx() {
115+
this.exchange.getResponse().setStatusCode(HttpStatus.MOVED_PERMANENTLY);
116+
Tag tag = WebFluxTags.outcome(this.exchange);
117+
assertThat(tag.getValue()).isEqualTo("REDIRECTION");
118+
}
119+
120+
@Test
121+
public void outcomeTagIsClientErrorWhenResponseIs4xx() {
122+
this.exchange.getResponse().setStatusCode(HttpStatus.BAD_REQUEST);
123+
Tag tag = WebFluxTags.outcome(this.exchange);
124+
assertThat(tag.getValue()).isEqualTo("CLIENT_ERROR");
125+
}
126+
127+
@Test
128+
public void outcomeTagIsServerErrorWhenResponseIs5xx() {
129+
this.exchange.getResponse().setStatusCode(HttpStatus.BAD_GATEWAY);
130+
Tag tag = WebFluxTags.outcome(this.exchange);
131+
assertThat(tag.getValue()).isEqualTo("SERVER_ERROR");
132+
}
133+
91134
}

spring-boot-project/spring-boot-docs/src/main/asciidoc/production-ready-features.adoc

Lines changed: 44 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1723,12 +1723,28 @@ customized by setting the `management.metrics.web.server.requests-metric-name` p
17231723

17241724
By default, Spring MVC-related metrics are tagged with the following information:
17251725

1726-
* `method`, the request's method (for example, `GET` or `POST`).
1727-
* `uri`, the request's URI template prior to variable substitution, if possible (for
1728-
example, `/api/person/{id}`).
1729-
* `status`, the response's HTTP status code (for example, `200` or `500`).
1730-
* `exception`, the simple class name of any exception that was thrown while handling the
1731-
request.
1726+
|===
1727+
|Tag |Description
1728+
1729+
|`exception`
1730+
|Simple class name of any exception that was thrown while handling the request.
1731+
1732+
|`method`
1733+
|Request's method (for example, `GET` or `POST`)
1734+
1735+
|`outcome`
1736+
|Request's outcome based on the status code of the response. 1xx is
1737+
`INFORMATIONAL`, 2xx is `SUCCESS`, 3xx is `REDIRECTION`, 4xx `CLIENT_ERROR`, and 5xx is
1738+
`SERVER_ERROR`
1739+
1740+
|`status`
1741+
|Response's HTTP status code (for example, `200` or `500`)
1742+
1743+
|`uri`
1744+
|Request's URI template prior to variable substitution, if possible (for example,
1745+
`/api/person/{id}`)
1746+
1747+
|===
17321748

17331749
To customize the tags, provide a `@Bean` that implements `WebMvcTagsProvider`.
17341750

@@ -1744,12 +1760,28 @@ the name by setting the `management.metrics.web.server.requests-metric-name` pro
17441760

17451761
By default, WebFlux-related metrics are tagged with the following information:
17461762

1747-
* `method`, the request's method (for example, `GET` or `POST`).
1748-
* `uri`, the request's URI template prior to variable substitution, if possible (for
1749-
example, `/api/person/{id}`).
1750-
* `status`, the response's HTTP status code (for example, `200` or `500`).
1751-
* `exception`, the simple class name of any exception that was thrown while handling the
1752-
request.
1763+
|===
1764+
|Tag |Description
1765+
1766+
|`exception`
1767+
|Simple class name of any exception that was thrown while handling the request.
1768+
1769+
|`method`
1770+
|Request's method (for example, `GET` or `POST`)
1771+
1772+
|`outcome`
1773+
|Request's outcome based on the status code of the response. 1xx is
1774+
`INFORMATIONAL`, 2xx is `SUCCESS`, 3xx is `REDIRECTION`, 4xx `CLIENT_ERROR`, and 5xx is
1775+
`SERVER_ERROR`
1776+
1777+
|`status`
1778+
|Response's HTTP status code (for example, `200` or `500`)
1779+
1780+
|`uri`
1781+
|Request's URI template prior to variable substitution, if possible (for example,
1782+
`/api/person/{id}`)
1783+
1784+
|===
17531785

17541786
To customize the tags, provide a `@Bean` that implements `WebFluxTagsProvider`.
17551787

0 commit comments

Comments
 (0)