From 708f600afe9b5c6236b15c2dcb7c64bddecd0075 Mon Sep 17 00:00:00 2001 From: Brian Clozel Date: Thu, 2 Feb 2023 18:19:17 +0100 Subject: [PATCH] Document how to mark an observation as an error The `ServerHttpObservationFilter` implementations record observations for processed HTTP exchanges. The `Observation.Context` contains various metadata contributed by the observation convention. The instrumentation can also mark the observation as an error by setting any `Throwable` on the context. Because the instrumentation is done as filters, only exceptions reaching the filter can be considered. Any error handled at a lower level by the Framework can, or cannot be considered as an error for an observation. This commit documents how a web application should opt-in for considering a handled exception as an error for the current observation. Closes gh-29848 --- .../src/docs/asciidoc/attributes.adoc | 1 + .../asciidoc/integration/observability.adoc | 12 ++++++ .../httpserver/reactive/UserController.java | 42 ++++++++++++++++++ .../httpserver/servlet/UserController.java | 43 +++++++++++++++++++ 4 files changed, 98 insertions(+) create mode 100644 framework-docs/src/main/java/org/springframework/docs/integration/observability/httpserver/reactive/UserController.java create mode 100644 framework-docs/src/main/java/org/springframework/docs/integration/observability/httpserver/servlet/UserController.java diff --git a/framework-docs/src/docs/asciidoc/attributes.adoc b/framework-docs/src/docs/asciidoc/attributes.adoc index 321eaf5cd3..1daab4dd8b 100644 --- a/framework-docs/src/docs/asciidoc/attributes.adoc +++ b/framework-docs/src/docs/asciidoc/attributes.adoc @@ -1,4 +1,5 @@ :chomp: default headers packages +:fold: all :docs-site: https://docs.spring.io // Spring Framework :docs-spring-framework: {docs-site}/spring-framework/docs/{spring-version} diff --git a/framework-docs/src/docs/asciidoc/integration/observability.adoc b/framework-docs/src/docs/asciidoc/integration/observability.adoc index 0fa1af5767..028bc1ee4e 100644 --- a/framework-docs/src/docs/asciidoc/integration/observability.adoc +++ b/framework-docs/src/docs/asciidoc/integration/observability.adoc @@ -67,6 +67,12 @@ HTTP server exchanges observations are created with the name `"http.server.reque Applications need to configure the `org.springframework.web.filter.ServerHttpObservationFilter` Servlet filter in their application. It is using the `org.springframework.http.server.observation.DefaultServerRequestObservationConvention` by default, backed by the `ServerRequestObservationContext`. +This will only record an observation as an error if the `Exception` has not been handled by the web Framework and has bubbled up to the Servlet filter. +Typically, all exceptions handled by Spring MVC's `@ExceptionHandler` and <> will not be recorded with the observation. +You can, at any point during request processing, set the error field on the `ObservationContext` yourself: + +include::code:UserController[] + By default, the following `KeyValues` are created: .Low cardinality Keys @@ -94,6 +100,12 @@ By default, the following `KeyValues` are created: Applications need to configure the `org.springframework.web.filter.reactive.ServerHttpObservationFilter` reactive `WebFilter` in their application. It is using the `org.springframework.http.server.reactive.observation.DefaultServerRequestObservationConvention` by default, backed by the `ServerRequestObservationContext`. +This will only record an observation as an error if the `Exception` has not been handled by the web Framework and has bubbled up to the `WebFilter`. +Typically, all exceptions handled by Spring WebFlux's `@ExceptionHandler` and <> will not be recorded with the observation. +You can, at any point during request processing, set the error field on the `ObservationContext` yourself: + +include::code:UserController[] + By default, the following `KeyValues` are created: .Low cardinality Keys diff --git a/framework-docs/src/main/java/org/springframework/docs/integration/observability/httpserver/reactive/UserController.java b/framework-docs/src/main/java/org/springframework/docs/integration/observability/httpserver/reactive/UserController.java new file mode 100644 index 0000000000..fc6221558c --- /dev/null +++ b/framework-docs/src/main/java/org/springframework/docs/integration/observability/httpserver/reactive/UserController.java @@ -0,0 +1,42 @@ +/* + * Copyright 2002-2023 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.docs.integration.observability.httpserver.reactive; + +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.filter.reactive.ServerHttpObservationFilter; +import org.springframework.web.server.ServerWebExchange; + +@Controller +public class UserController { + + @ExceptionHandler(MissingUserException.class) + ResponseEntity handleMissingUser(ServerWebExchange exchange, MissingUserException exception) { + // We want to record this exception with the observation + ServerHttpObservationFilter.findObservationContext(exchange) + .ifPresent(context -> context.setError(exception)); + return ResponseEntity.notFound().build(); + } + + // @fold:on + static class MissingUserException extends RuntimeException { + + } + // @fold:off + +} diff --git a/framework-docs/src/main/java/org/springframework/docs/integration/observability/httpserver/servlet/UserController.java b/framework-docs/src/main/java/org/springframework/docs/integration/observability/httpserver/servlet/UserController.java new file mode 100644 index 0000000000..5f44127961 --- /dev/null +++ b/framework-docs/src/main/java/org/springframework/docs/integration/observability/httpserver/servlet/UserController.java @@ -0,0 +1,43 @@ +/* + * Copyright 2002-2023 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.docs.integration.observability.httpserver.servlet; + +import jakarta.servlet.http.HttpServletRequest; + +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.filter.ServerHttpObservationFilter; + +@Controller +public class UserController { + + @ExceptionHandler(MissingUserException.class) + ResponseEntity handleMissingUser(HttpServletRequest request, MissingUserException exception) { + // We want to record this exception with the observation + ServerHttpObservationFilter.findObservationContext(request) + .ifPresent(context -> context.setError(exception)); + return ResponseEntity.notFound().build(); + } + + // @fold:on + static class MissingUserException extends RuntimeException { + + } + // @fold:off + +}