Add HttpOutcome for HTTP observations
Prior to this commit, the HTTP Observations would use `HttpStatus.Series` as a value source for the "outcome" key value in recorded observations. This would work for most cases, but would not align in the 2xx HTTP status cases: the series would provide a "SUCESSFUL" value whereas the heritage metrics support in Spring Boot would give "SUCESS". This commit introduces a dedicated `HttpOutcome` concept for this and applies it to all HTTP observations. Fixes gh-29232
This commit is contained in:
@@ -21,8 +21,8 @@ import java.io.IOException;
|
||||
import io.micrometer.common.KeyValue;
|
||||
import io.micrometer.common.KeyValues;
|
||||
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.client.ClientHttpResponse;
|
||||
import org.springframework.http.observation.HttpOutcome;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
@@ -43,8 +43,6 @@ public class DefaultClientHttpObservationConvention implements ClientHttpObserva
|
||||
|
||||
private static final KeyValue EXCEPTION_NONE = KeyValue.of(ClientHttpObservation.LowCardinalityKeyNames.EXCEPTION, "none");
|
||||
|
||||
private static final KeyValue OUTCOME_UNKNOWN = KeyValue.of(ClientHttpObservation.LowCardinalityKeyNames.OUTCOME, "UNKNOWN");
|
||||
|
||||
private static final KeyValue URI_EXPANDED_NONE = KeyValue.of(ClientHttpObservation.HighCardinalityKeyNames.URI_EXPANDED, "none");
|
||||
|
||||
private final String name;
|
||||
@@ -122,16 +120,14 @@ public class DefaultClientHttpObservationConvention implements ClientHttpObserva
|
||||
protected static KeyValue outcome(ClientHttpObservationContext context) {
|
||||
try {
|
||||
if (context.getResponse() != null) {
|
||||
HttpStatus status = HttpStatus.resolve(context.getResponse().getStatusCode().value());
|
||||
if (status != null) {
|
||||
return KeyValue.of(ClientHttpObservation.LowCardinalityKeyNames.OUTCOME, status.series().name());
|
||||
}
|
||||
HttpOutcome httpOutcome = HttpOutcome.forStatus(context.getResponse().getStatusCode());
|
||||
return httpOutcome.asKeyValue();
|
||||
}
|
||||
}
|
||||
catch (IOException ex) {
|
||||
// Continue
|
||||
}
|
||||
return OUTCOME_UNKNOWN;
|
||||
return HttpOutcome.UNKNOWN.asKeyValue();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -0,0 +1,101 @@
|
||||
/*
|
||||
* Copyright 2002-2022 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
|
||||
*
|
||||
* https://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.http.observation;
|
||||
|
||||
import io.micrometer.common.KeyValue;
|
||||
|
||||
import org.springframework.http.HttpStatusCode;
|
||||
|
||||
/**
|
||||
* The outcome of an HTTP request.
|
||||
* <p>Used as the {@code "outcome} {@link io.micrometer.common.KeyValue}
|
||||
* for HTTP {@link io.micrometer.observation.Observation observations}.
|
||||
*
|
||||
* @author Brian Clozel
|
||||
* @author Andy Wilkinson
|
||||
* @since 6.0.0
|
||||
*/
|
||||
public enum HttpOutcome {
|
||||
|
||||
/**
|
||||
* Outcome of the request was informational.
|
||||
*/
|
||||
INFORMATIONAL,
|
||||
|
||||
/**
|
||||
* Outcome of the request was success.
|
||||
*/
|
||||
SUCCESS,
|
||||
|
||||
/**
|
||||
* Outcome of the request was redirection.
|
||||
*/
|
||||
REDIRECTION,
|
||||
|
||||
/**
|
||||
* Outcome of the request was client error.
|
||||
*/
|
||||
CLIENT_ERROR,
|
||||
|
||||
/**
|
||||
* Outcome of the request was server error.
|
||||
*/
|
||||
SERVER_ERROR,
|
||||
|
||||
/**
|
||||
* Outcome of the request was unknown.
|
||||
*/
|
||||
UNKNOWN;
|
||||
|
||||
private final KeyValue keyValue;
|
||||
|
||||
HttpOutcome() {
|
||||
this.keyValue = KeyValue.of("outcome", name());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@code Outcome} as a {@link KeyValue} named {@code outcome}.
|
||||
* @return the {@code outcome} {@code KeyValue}
|
||||
*/
|
||||
public KeyValue asKeyValue() {
|
||||
return this.keyValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the {@code HttpOutcome} for the given HTTP {@code status} code.
|
||||
* @param status the HTTP status code
|
||||
* @return the matching HttpOutcome
|
||||
*/
|
||||
public static HttpOutcome forStatus(HttpStatusCode status) {
|
||||
if (status.is1xxInformational()) {
|
||||
return INFORMATIONAL;
|
||||
}
|
||||
else if (status.is2xxSuccessful()) {
|
||||
return SUCCESS;
|
||||
}
|
||||
else if (status.is3xxRedirection()) {
|
||||
return REDIRECTION;
|
||||
}
|
||||
else if (status.is4xxClientError()) {
|
||||
return CLIENT_ERROR;
|
||||
}
|
||||
else if (status.is5xxServerError()) {
|
||||
return SERVER_ERROR;
|
||||
}
|
||||
return UNKNOWN;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
/**
|
||||
* Base support for HTTP {@link io.micrometer.observation.Observation}.
|
||||
*/
|
||||
@NonNullApi
|
||||
@NonNullFields
|
||||
package org.springframework.http.observation;
|
||||
|
||||
import org.springframework.lang.NonNullApi;
|
||||
import org.springframework.lang.NonNullFields;
|
||||
@@ -20,6 +20,8 @@ import io.micrometer.common.KeyValue;
|
||||
import io.micrometer.common.KeyValues;
|
||||
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.HttpStatusCode;
|
||||
import org.springframework.http.observation.HttpOutcome;
|
||||
|
||||
/**
|
||||
* Default {@link HttpRequestsObservationConvention}.
|
||||
@@ -44,8 +46,6 @@ public class DefaultHttpRequestsObservationConvention implements HttpRequestsObs
|
||||
|
||||
private static final KeyValue EXCEPTION_NONE = KeyValue.of(HttpRequestsObservation.LowCardinalityKeyNames.EXCEPTION, "none");
|
||||
|
||||
private static final KeyValue OUTCOME_UNKNOWN = KeyValue.of(HttpRequestsObservation.LowCardinalityKeyNames.OUTCOME, "UNKNOWN");
|
||||
|
||||
private static final KeyValue URI_EXPANDED_UNKNOWN = KeyValue.of(HttpRequestsObservation.HighCardinalityKeyNames.URI_EXPANDED, "UNKNOWN");
|
||||
|
||||
private final String name;
|
||||
@@ -125,12 +125,11 @@ public class DefaultHttpRequestsObservationConvention implements HttpRequestsObs
|
||||
|
||||
protected KeyValue outcome(HttpRequestsObservationContext context) {
|
||||
if (context.getResponse() != null) {
|
||||
HttpStatus status = HttpStatus.resolve(context.getResponse().getStatus());
|
||||
if (status != null) {
|
||||
return KeyValue.of(HttpRequestsObservation.LowCardinalityKeyNames.OUTCOME, status.series().name());
|
||||
}
|
||||
HttpStatusCode statusCode = HttpStatusCode.valueOf(context.getResponse().getStatus());
|
||||
HttpOutcome httpOutcome = HttpOutcome.forStatus(statusCode);
|
||||
return httpOutcome.asKeyValue();
|
||||
}
|
||||
return OUTCOME_UNKNOWN;
|
||||
return HttpOutcome.UNKNOWN.asKeyValue();
|
||||
}
|
||||
|
||||
protected KeyValue uriExpanded(HttpRequestsObservationContext context) {
|
||||
|
||||
@@ -20,6 +20,7 @@ import io.micrometer.common.KeyValue;
|
||||
import io.micrometer.common.KeyValues;
|
||||
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.observation.HttpOutcome;
|
||||
import org.springframework.web.util.pattern.PathPattern;
|
||||
|
||||
/**
|
||||
@@ -46,8 +47,6 @@ public class DefaultHttpRequestsObservationConvention implements HttpRequestsObs
|
||||
|
||||
private static final KeyValue EXCEPTION_NONE = KeyValue.of(HttpRequestsObservation.LowCardinalityKeyNames.EXCEPTION, "none");
|
||||
|
||||
private static final KeyValue OUTCOME_UNKNOWN = KeyValue.of(HttpRequestsObservation.LowCardinalityKeyNames.OUTCOME, "UNKNOWN");
|
||||
|
||||
private static final KeyValue URI_EXPANDED_UNKNOWN = KeyValue.of(HttpRequestsObservation.HighCardinalityKeyNames.URI_EXPANDED, "UNKNOWN");
|
||||
|
||||
private final String name;
|
||||
@@ -128,15 +127,13 @@ public class DefaultHttpRequestsObservationConvention implements HttpRequestsObs
|
||||
|
||||
protected KeyValue outcome(HttpRequestsObservationContext context) {
|
||||
if (context.isConnectionAborted()) {
|
||||
return OUTCOME_UNKNOWN;
|
||||
return HttpOutcome.UNKNOWN.asKeyValue();
|
||||
}
|
||||
else if (context.getResponse() != null) {
|
||||
HttpStatus status = HttpStatus.resolve(context.getResponse().getStatusCode().value());
|
||||
if (status != null) {
|
||||
return KeyValue.of(HttpRequestsObservation.LowCardinalityKeyNames.OUTCOME, status.series().name());
|
||||
}
|
||||
HttpOutcome httpOutcome = HttpOutcome.forStatus(context.getResponse().getStatusCode());
|
||||
return httpOutcome.asKeyValue();
|
||||
}
|
||||
return OUTCOME_UNKNOWN;
|
||||
return HttpOutcome.UNKNOWN.asKeyValue();
|
||||
}
|
||||
|
||||
protected KeyValue uriExpanded(HttpRequestsObservationContext context) {
|
||||
|
||||
Reference in New Issue
Block a user