Skip to content

Commit

Permalink
Document how to mark an observation as an error
Browse files Browse the repository at this point in the history
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 spring-projectsgh-29848
  • Loading branch information
bclozel authored and mdeinum committed Jun 29, 2023
1 parent c13aae5 commit e169ec5
Show file tree
Hide file tree
Showing 4 changed files with 98 additions and 0 deletions.
1 change: 1 addition & 0 deletions framework-docs/src/docs/asciidoc/attributes.adoc
Original file line number Diff line number Diff line change
@@ -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}
Expand Down
12 changes: 12 additions & 0 deletions framework-docs/src/docs/asciidoc/integration/observability.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -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 <<web.adoc#mvc-ann-rest-exceptions,`ProblemDetail` support>> 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
Expand Down Expand Up @@ -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 <<web.adoc#webflux-ann-rest-exceptions,`ProblemDetail` support>> 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
Expand Down
Original file line number Diff line number Diff line change
@@ -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<Void> 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

}
Original file line number Diff line number Diff line change
@@ -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<Void> 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

}

0 comments on commit e169ec5

Please sign in to comment.