Browse Source
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-29232pull/29235/head
Brian Clozel
2 years ago
15 changed files with 224 additions and 42 deletions
@ -0,0 +1,101 @@
@@ -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 @@
@@ -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; |
@ -0,0 +1,83 @@
@@ -0,0 +1,83 @@
|
||||
/* |
||||
* 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.junit.jupiter.params.ParameterizedTest; |
||||
import org.junit.jupiter.params.provider.ValueSource; |
||||
|
||||
import org.springframework.http.HttpStatusCode; |
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat; |
||||
|
||||
/** |
||||
* Tests for {@link HttpOutcome}. |
||||
* |
||||
* @author Brian Clozel |
||||
*/ |
||||
class HttpOutcomeTests { |
||||
|
||||
@ParameterizedTest |
||||
@ValueSource(ints = {100, 101, 102}) |
||||
void shouldResolveInformational(int code) { |
||||
HttpOutcome httpOutcome = HttpOutcome.forStatus(HttpStatusCode.valueOf(code)); |
||||
assertThat(httpOutcome).isEqualTo(HttpOutcome.INFORMATIONAL); |
||||
assertThat(httpOutcome.asKeyValue()).isEqualTo(KeyValue.of("outcome", "INFORMATIONAL")); |
||||
} |
||||
|
||||
@ParameterizedTest |
||||
@ValueSource(ints = {200, 202, 226}) |
||||
void shouldResolveSuccess(int code) { |
||||
HttpOutcome httpOutcome = HttpOutcome.forStatus(HttpStatusCode.valueOf(code)); |
||||
assertThat(httpOutcome).isEqualTo(HttpOutcome.SUCCESS); |
||||
assertThat(httpOutcome.asKeyValue()).isEqualTo(KeyValue.of("outcome", "SUCCESS")); |
||||
} |
||||
|
||||
@ParameterizedTest |
||||
@ValueSource(ints = {300, 302, 303}) |
||||
void shouldResolveRedirection(int code) { |
||||
HttpOutcome httpOutcome = HttpOutcome.forStatus(HttpStatusCode.valueOf(code)); |
||||
assertThat(httpOutcome).isEqualTo(HttpOutcome.REDIRECTION); |
||||
assertThat(httpOutcome.asKeyValue()).isEqualTo(KeyValue.of("outcome", "REDIRECTION")); |
||||
} |
||||
|
||||
@ParameterizedTest |
||||
@ValueSource(ints = {404, 404, 405}) |
||||
void shouldResolveClientError(int code) { |
||||
HttpOutcome httpOutcome = HttpOutcome.forStatus(HttpStatusCode.valueOf(code)); |
||||
assertThat(httpOutcome).isEqualTo(HttpOutcome.CLIENT_ERROR); |
||||
assertThat(httpOutcome.asKeyValue()).isEqualTo(KeyValue.of("outcome", "CLIENT_ERROR")); |
||||
} |
||||
|
||||
@ParameterizedTest |
||||
@ValueSource(ints = {500, 502, 503}) |
||||
void shouldResolveServerError(int code) { |
||||
HttpOutcome httpOutcome = HttpOutcome.forStatus(HttpStatusCode.valueOf(code)); |
||||
assertThat(httpOutcome).isEqualTo(HttpOutcome.SERVER_ERROR); |
||||
assertThat(httpOutcome.asKeyValue()).isEqualTo(KeyValue.of("outcome", "SERVER_ERROR")); |
||||
} |
||||
|
||||
@ParameterizedTest |
||||
@ValueSource(ints = {600, 799, 855}) |
||||
void shouldResolveUnknown(int code) { |
||||
HttpOutcome httpOutcome = HttpOutcome.forStatus(HttpStatusCode.valueOf(code)); |
||||
assertThat(httpOutcome).isEqualTo(HttpOutcome.UNKNOWN); |
||||
assertThat(httpOutcome.asKeyValue()).isEqualTo(KeyValue.of("outcome", "UNKNOWN")); |
||||
} |
||||
|
||||
} |
Loading…
Reference in new issue