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
This commit is contained in:
Andy Wilkinson
2018-09-18 12:34:09 +01:00
7 changed files with 210 additions and 14 deletions

View File

@@ -35,7 +35,8 @@ public class DefaultWebFluxTagsProvider implements WebFluxTagsProvider {
public Iterable<Tag> httpRequestTags(ServerWebExchange exchange,
Throwable exception) {
return Arrays.asList(WebFluxTags.method(exchange), WebFluxTags.uri(exchange),
WebFluxTags.exception(exception), WebFluxTags.status(exchange));
WebFluxTags.exception(exception), WebFluxTags.status(exchange),
WebFluxTags.outcome(exchange));
}
}

View File

@@ -30,6 +30,7 @@ import org.springframework.web.util.pattern.PathPattern;
*
* @author Jon Schneider
* @author Andy Wilkinson
* @author Michael McFadyen
* @since 2.0.0
*/
public final class WebFluxTags {
@@ -42,6 +43,18 @@ public final class WebFluxTags {
private static final Tag EXCEPTION_NONE = Tag.of("exception", "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 WebFluxTags() {
}
@@ -112,4 +125,30 @@ public final class WebFluxTags {
return EXCEPTION_NONE;
}
/**
* Creates a {@code outcome} tag based on the response status of the given
* {@code exchange}.
* @param exchange the exchange
* @return the outcome tag derived from the response status
*/
public static Tag outcome(ServerWebExchange exchange) {
HttpStatus status = exchange.getResponse().getStatusCode();
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;
}
}

View File

@@ -34,7 +34,8 @@ public class DefaultWebMvcTagsProvider implements WebMvcTagsProvider {
public Iterable<Tag> getTags(HttpServletRequest request, HttpServletResponse response,
Object handler, Throwable exception) {
return Tags.of(WebMvcTags.method(request), WebMvcTags.uri(request, response),
WebMvcTags.exception(exception), WebMvcTags.status(response));
WebMvcTags.exception(exception), WebMvcTags.status(response),
WebMvcTags.outcome(response));
}
@Override

View File

@@ -34,6 +34,7 @@ import org.springframework.web.servlet.HandlerMapping;
* @author Jon Schneider
* @author Andy Wilkinson
* @author Brian Clozel
* @author Michael McFadyen
* @since 2.0.0
*/
public final class WebMvcTags {
@@ -50,6 +51,18 @@ public final class WebMvcTags {
private static final Tag STATUS_UNKNOWN = Tag.of("status", "UNKNOWN");
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 static final Tag METHOD_UNKNOWN = Tag.of("method", "UNKNOWN");
private static final Pattern TRAILING_SLASH_PATTERN = Pattern.compile("/$");
@@ -149,4 +162,29 @@ public final class WebMvcTags {
return EXCEPTION_NONE;
}
/**
* Creates a {@code outcome} tag based on the status of the given {@code response}.
* @param response the HTTP response
* @return the outcome tag derived from the status of the response
*/
public static Tag outcome(HttpServletResponse response) {
if (response != null) {
int status = response.getStatus();
if (status < 200) {
return OUTCOME_INFORMATIONAL;
}
if (status < 300) {
return OUTCOME_SUCCESS;
}
if (status < 400) {
return OUTCOME_REDIRECTION;
}
else if (status < 500) {
return OUTCOME_CLIENT_ERROR;
}
return OUTCOME_SERVER_ERROR;
}
return OUTCOME_UNKNOWN;
}
}

View File

@@ -31,6 +31,7 @@ import static org.assertj.core.api.Assertions.assertThat;
*
* @author Andy Wilkinson
* @author Brian Clozel
* @author Michael McFadyen
*/
public class WebMvcTagsTests {
@@ -91,4 +92,45 @@ public class WebMvcTagsTests {
assertThat(tag.getValue()).isEqualTo("UNKNOWN");
}
@Test
public void outcomeTagIsUnknownWhenResponseIsNull() {
Tag tag = WebMvcTags.outcome(null);
assertThat(tag.getValue()).isEqualTo("UNKNOWN");
}
@Test
public void outcomeTagIsInformationalWhenResponseIs1xx() {
this.response.setStatus(100);
Tag tag = WebMvcTags.outcome(this.response);
assertThat(tag.getValue()).isEqualTo("INFORMATIONAL");
}
@Test
public void outcomeTagIsSuccessWhenResponseIs2xx() {
this.response.setStatus(200);
Tag tag = WebMvcTags.outcome(this.response);
assertThat(tag.getValue()).isEqualTo("SUCCESS");
}
@Test
public void outcomeTagIsRedirectionWhenResponseIs3xx() {
this.response.setStatus(301);
Tag tag = WebMvcTags.outcome(this.response);
assertThat(tag.getValue()).isEqualTo("REDIRECTION");
}
@Test
public void outcomeTagIsClientErrorWhenResponseIs4xx() {
this.response.setStatus(400);
Tag tag = WebMvcTags.outcome(this.response);
assertThat(tag.getValue()).isEqualTo("CLIENT_ERROR");
}
@Test
public void outcomeTagIsServerErrorWhenResponseIs5xx() {
this.response.setStatus(500);
Tag tag = WebMvcTags.outcome(this.response);
assertThat(tag.getValue()).isEqualTo("SERVER_ERROR");
}
}

View File

@@ -36,6 +36,7 @@ import static org.mockito.Mockito.mock;
* Tests for {@link WebFluxTags}.
*
* @author Brian Clozel
* @author Michael McFadyen
*/
public class WebFluxTagsTests {
@@ -88,4 +89,46 @@ public class WebFluxTagsTests {
assertThat(tag.getValue()).isEqualTo("CUSTOM");
}
@Test
public void outcomeTagIsUnknownWhenResponseStatusIsNull() {
this.exchange.getResponse().setStatusCode(null);
Tag tag = WebFluxTags.outcome(this.exchange);
assertThat(tag.getValue()).isEqualTo("UNKNOWN");
}
@Test
public void outcomeTagIsInformationalWhenResponseIs1xx() {
this.exchange.getResponse().setStatusCode(HttpStatus.CONTINUE);
Tag tag = WebFluxTags.outcome(this.exchange);
assertThat(tag.getValue()).isEqualTo("INFORMATIONAL");
}
@Test
public void outcomeTagIsSuccessWhenResponseIs2xx() {
this.exchange.getResponse().setStatusCode(HttpStatus.OK);
Tag tag = WebFluxTags.outcome(this.exchange);
assertThat(tag.getValue()).isEqualTo("SUCCESS");
}
@Test
public void outcomeTagIsRedirectionWhenResponseIs3xx() {
this.exchange.getResponse().setStatusCode(HttpStatus.MOVED_PERMANENTLY);
Tag tag = WebFluxTags.outcome(this.exchange);
assertThat(tag.getValue()).isEqualTo("REDIRECTION");
}
@Test
public void outcomeTagIsClientErrorWhenResponseIs4xx() {
this.exchange.getResponse().setStatusCode(HttpStatus.BAD_REQUEST);
Tag tag = WebFluxTags.outcome(this.exchange);
assertThat(tag.getValue()).isEqualTo("CLIENT_ERROR");
}
@Test
public void outcomeTagIsServerErrorWhenResponseIs5xx() {
this.exchange.getResponse().setStatusCode(HttpStatus.BAD_GATEWAY);
Tag tag = WebFluxTags.outcome(this.exchange);
assertThat(tag.getValue()).isEqualTo("SERVER_ERROR");
}
}

View File

@@ -1723,12 +1723,28 @@ customized by setting the `management.metrics.web.server.requests-metric-name` p
By default, Spring MVC-related metrics are tagged with the following information:
* `method`, the request's method (for example, `GET` or `POST`).
* `uri`, the request's URI template prior to variable substitution, if possible (for
example, `/api/person/{id}`).
* `status`, the response's HTTP status code (for example, `200` or `500`).
* `exception`, the simple class name of any exception that was thrown while handling the
request.
|===
|Tag |Description
|`exception`
|Simple class name of any exception that was thrown while handling the request.
|`method`
|Request's method (for example, `GET` or `POST`)
|`outcome`
|Request's outcome based on the status code of the response. 1xx is
`INFORMATIONAL`, 2xx is `SUCCESS`, 3xx is `REDIRECTION`, 4xx `CLIENT_ERROR`, and 5xx is
`SERVER_ERROR`
|`status`
|Response's HTTP status code (for example, `200` or `500`)
|`uri`
|Request's URI template prior to variable substitution, if possible (for example,
`/api/person/{id}`)
|===
To customize the tags, provide a `@Bean` that implements `WebMvcTagsProvider`.
@@ -1744,12 +1760,28 @@ the name by setting the `management.metrics.web.server.requests-metric-name` pro
By default, WebFlux-related metrics are tagged with the following information:
* `method`, the request's method (for example, `GET` or `POST`).
* `uri`, the request's URI template prior to variable substitution, if possible (for
example, `/api/person/{id}`).
* `status`, the response's HTTP status code (for example, `200` or `500`).
* `exception`, the simple class name of any exception that was thrown while handling the
request.
|===
|Tag |Description
|`exception`
|Simple class name of any exception that was thrown while handling the request.
|`method`
|Request's method (for example, `GET` or `POST`)
|`outcome`
|Request's outcome based on the status code of the response. 1xx is
`INFORMATIONAL`, 2xx is `SUCCESS`, 3xx is `REDIRECTION`, 4xx `CLIENT_ERROR`, and 5xx is
`SERVER_ERROR`
|`status`
|Response's HTTP status code (for example, `200` or `500`)
|`uri`
|Request's URI template prior to variable substitution, if possible (for example,
`/api/person/{id}`)
|===
To customize the tags, provide a `@Bean` that implements `WebFluxTagsProvider`.