Affects: 6.0.14


Hi,

I'm using spring-boot:3.0.13 spring-framework:6.0.14

And I'm facing an issue with the observation feature. I don't know if it's really a bug or if I'm misusing it.

The problem is that if an application exception is thrown in the controller code I see the proper status and outcome tags in the traces but not the exception itself (exception is always none).

From what I've been gathering it is due to the fact that the SimpleHttpServerObservationFilter checks for an exception in the request attributes for the key RequestDispatcher.ERROR_EXCEPTION but the exception is stored in the request attribute DispatcherServlet.EXCEPTION_ATTRIBUTE: https://github.com/spring-projects/spring-framework/blob/main/spring-web/src/main/java/org/springframework/web/filter/ServerHttpObservationFilter.java#L148

In turn the Observation is never marked as an error: https://github.com/spring-projects/spring-framework/blob/main/spring-web/src/main/java/org/springframework/web/filter/ServerHttpObservationFilter.java#L120

Or am I just using it wrong and I have to register/configure some other bean?

Thanks in advance,

Daniel

Comment From: bclozel

Can you share a sample application? I cannot reproduce.

@RestController
public class TestController {

    @GetMapping("/")
    public String test() {
        throw new IllegalStateException("custom");
    }
}
$ http http://localhost:8080/actuator/metrics/http.server.requests\?tag\=outcome:SERVER_ERROR

HTTP/1.1 200
Connection: keep-alive
Content-Disposition: inline;filename=f.txt
Content-Type: application/vnd.spring-boot.actuator.v3+json
Date: Thu, 08 Feb 2024 08:31:26 GMT
Keep-Alive: timeout=60
Transfer-Encoding: chunked

{
    "availableTags": [
        {
            "tag": "exception",
            "values": [
                "IllegalStateException"
            ]
        },
        {
            "tag": "method",
            "values": [
                "GET"
            ]
        },
        {
            "tag": "error",
            "values": [
                "IllegalStateException"
            ]
        },
        {
            "tag": "uri",
            "values": [
                "/"
            ]
        },
        {
            "tag": "status",
            "values": [
                "500"
            ]
        }
    ],
    "baseUnit": "seconds",
    "measurements": [
        {
            "statistic": "COUNT",
            "value": 1.0
        },
        {
            "statistic": "TOTAL_TIME",
            "value": 0.028471921
        },
        {
            "statistic": "MAX",
            "value": 0.028471921
        }
    ],
    "name": "http.server.requests"
}

Comment From: bender316

Yes, I got it working with the code you provided. But as soon as I throw a "custom" exception the whole stuff is behaving differently. DispatcherServlet is no longer throwing the exception and hence the observation knows nothing about the exception.

See the demo-project I provided: https://github.com/bender316/spring-boot-demo-project

Throw the IllegalStateException and the span is logged to the console (as well as the exception itself). Throw the BadRequestException and the console goes silent.

Comment From: bclozel

Thanks for the sample application, this helps a lot.

This is the expected behavior, as your custom exception is annotated with @ResponseStatus. This means that this exception is handled by an exception handler. This might be a perfectly "normal" exception to get in an application, for example a ResourceNotFoundException that should be translated into an HTTP 404. Arguably, this is the regular behavior for the application and should not result in a SERVER_ERROR outcome with the exception tag populated, as those are used to alert when errors happen.

Because you might want to collect metrics for those still, you can do so as explained in the reference documentation. From a Framework perspective, we can't really mark them all in observations as there would be no easy way to opt out - we consider that handling an exception at the MVC level is a signal that we should take into account.

Comment From: bender316

Ok, I misunderstood the passage in the docs in the first place. I now know what to do,

Thanks for your time

Comment From: bclozel

Feel free to suggest here changes to the documentation. Maybe the @ResponseStatus annotation is not obvious as a way for an error to be handled.