[spring-projects/spring-boot]执行器“http.server.requests”指标对于错误响应具有错误的“状态”标签

2024-04-29 292 views
7

执行器报告状态 = 200 的指标,但控制器实际上响应了错误的请求。

MetricsWebFilter.error(ServerWebExchange Exchange, long start, Throwable Cause) 被调用,原因如下:

org.springframework.web.server.ServerWebInputException:响应状态 400,原因为“类型不匹配”;嵌套异常是 org.springframework.beans.TypeMismatchException:无法将类型“java.lang.String”的值转换为所需类型“java.util.List”;嵌套异常是 java.lang.NumberFormatException:对于输入字符串:“invalidSkuId”

但是,DefaultWebFluxTagsProvider 将状态标记解析为 200,因为它从交换获取状态(在本例中为 null),并且不考虑异常原因。

public static Tag status(ServerWebExchange exchange) {
    HttpStatus status = exchange.getResponse().getStatusCode();
    if (status == null) {
        status = HttpStatus.OK;
    }
    return Tag.of("status", status.toString());
}

实际指标名称 httpServerRequests.exception.ServerWebInputException.method.POST.status。200.uri./GoodsService/getActiveGoods.do

预期指标名称为 httpServerRequests.exception.ServerWebInputException.method.POST.status。400.uri./GoodsService/getActiveGoods.do

回答

0

感谢您的错误报告。

IMO,标签提供商没有考虑异常原因是正确的。为此,需要了解 WebFlux 的状态映射逻辑异常。相反,最好是在ExceptionHandlingWebHandler调用标签提供者并在交换上设置响应状态之后再调用标签提供者。但是,我不确定这是否可能,因为目前,WebHttpHandlerBuilder装饰FilteringWebHandlerExceptionHandlerWebHandler在过滤后执行异常处理。

@rstoyanchev @sdeleuze 有没有办法让我们连接到 Web 处理管道,以便我们能够可靠地获取将返回给客户端的状态?

/抄送@jkschneider

5

不久前 servlet 端出现了一个相关问题https://github.com/micrometer-metrics/micrometer/pull/264。我们最终不得不做这个相当难看的 hack,因为最终 servlet 容器最终成为设置响应代码的东西,而在它离开 Spring Boot 的控制之后。

我咨询了@adriancole,我们在 openzipkin/brave 中做了同样的事情。

关于我们(即跟踪和指标)进一步提升仪器堆栈、连接到特定容器实现进行了一些讨论。这主要是由计时精度驱动的,但也会对此类问题产生影响。

7

@wilkinsona,唯一想到的是装饰 HttpHandler本身,以在所有处理完成后检查响应状态。

请记住,还有少量剩余情况,特别是ServerHttpResponse#isCommitted在异常处理时返回 true 时,服务器适配器代码(Servet 容器、Undertow)或服务器本身中的状态可能会更改为 500 更深(反应堆网络)。在这些情况下,HttpHandler将返回错误信号,因此至少您可以识别它们,并且此时可以安全地假设将产生 500 响应。

3

@jkschneider 我不知道你正在使用HandlerMappingIntrospector.提前完成整个请求映射以获取处理程序似乎有点浪费。直观上,启动指标并在实际选择处理程序后填写处理程序是有意义的。您是否考虑过WebMvcConfigurer安装全局拦截器?

9

提前完成整个请求映射以获取处理程序似乎有点浪费

我不太明白这一点。你说这matchableHandlerMapping.getHandler(request)很浪费吗?

0

您实际上是在执行完整处理程序匹配算法的额外运行 - 遍历每个HandlerMapping、检查每个@RequestMapping、查找静态资源等等。

4

IIRC spring-security 和 Sleuth 也在做同样的事情。看起来,如果这是一个浪费的操作,那么如果没有其他办法来处理对同一 URI 的后续请求,我们最好在匹配算法中使用缓存来提供服务?

5

此外,可观察性代码(指标/跟踪)有机会在使用 servlet 过滤器的 spring-security 之前拦截请求,这一点也至关重要。不确定WebMvcConfigurer基于 - 的拦截器是否排在所有 servlet 过滤器之前。

这是一场以我为先的军备竞赛。

1

WebMvcConfigurer基于 的拦截器(根据定义)排序在所有 servlet 过滤器之后(它们由 servlet 应用)。

8

我创建了SPR-16693作为正确的抽象级别,它是WebHandlerDecorator.一旦 SPR-16693 得到修复,这个问题现在将变成MetricsWebFilter一个问题。MetricsWebHandlerDecorator

2

实际上,我已经修复了这个问题,无需对 Spring 框架进行任何更改;我可能会创建一个新的 Spring Boot 问题来根据SPR-16693MetricsWebFilter的结果更新实现。

7

@bclozel 看起来不错。 77be10e 的另一种情况是doOnError——如果响应已提交,则立即调用错误方法,因为beforeCommit在这种情况下不会调用错误方法。非错误状态代码 + 的组合Throwable表示在提交响应后发生错误,这在大多数情况下会导致关闭连接,或者在某些情况下(缓冲区填满之前的 Servlet API)可能会导致 500 still 。

这使得决议相当完整,我认为我们现在可以关闭 SPR-16693。 Hooks onHttpWebHandlerAdapter不会提供任何我能看到的东西。要更进一步,需要更深入地了解服务器适配器或服务器级别。

6

谢谢@rstoyanchev,我在 b2e7be1 中改进了这一点。