避免在 Spring Boot 应用程序中向 Sentry 报告 Broken Pipe 错误
Avoid reporting Broken Pipe errors to Sentry in a Spring Boot application
我有一个 Spring 启动应用程序,它使用 Sentry 进行异常跟踪,但我收到一些如下所示的错误:
ClientAbortExceptionorg.apache.catalina.connector.OutputBuffer in realWriteBytes
errorjava.io.IOException: Broken pipe
我的理解是这只是一个网络错误,因此我通常应该忽略它们。我想做的是报告所有其他 IOExceptions
并将损坏的管道记录到 Librato,这样我就可以关注我得到了多少(尖峰可能意味着客户端有问题,这也是由开发的我在 Java):
我想到了这个:
@ControllerAdvice
@Priority(1)
@Order(1)
public class RestExceptionHandler {
@ExceptionHandler(ClientAbortException.class)
@ResponseStatus(HttpStatus.SERVICE_UNAVAILABLE)
public ResponseEntity<?> handleClientAbortException(ClientAbortException ex, HttpServletRequest request) {
Throwable rootCause = ex;
while (ex.getCause() != null) {
rootCause = ex.getCause();
}
if (rootCause.getMessage().contains("Broken pipe")) {
logger.info("count#broken_pipe=1");
} else {
Sentry.getStoredClient().sendException(ex);
}
return null;
}
}
这是处理此问题的可接受方法吗?
我按照文档这样配置了 Sentry:
@Configuration
public class FactoryBeanAppConfig {
@Bean
public HandlerExceptionResolver sentryExceptionResolver() {
return new SentryExceptionResolver();
}
@Bean
public ServletContextInitializer sentryServletContextInitializer() {
return new SentryServletContextInitializer();
}
}
如果你看 class SentryExceptionResolver
public class SentryExceptionResolver implements HandlerExceptionResolver, Ordered {
@Override
public ModelAndView resolveException(HttpServletRequest request,
HttpServletResponse response,
Object handler,
Exception ex) {
Sentry.capture(ex);
// null = run other HandlerExceptionResolvers to actually handle the exception
return null;
}
@Override
public int getOrder() {
// ensure this resolver runs first so that all exceptions are reported
return Integer.MIN_VALUE;
}
}
通过在 getOrder
中返回 Integer.MIN_VALUE
,它确保它首先被调用。即使您已将 Priority
设置为 1
,它也不会起作用。所以你想改变你的
@Configuration
public class FactoryBeanAppConfig {
@Bean
public HandlerExceptionResolver sentryExceptionResolver() {
return new SentryExceptionResolver();
}
@Bean
public ServletContextInitializer sentryServletContextInitializer() {
return new SentryServletContextInitializer();
}
}
至
@Configuration
public class FactoryBeanAppConfig {
@Bean
public HandlerExceptionResolver sentryExceptionResolver() {
return new SentryExceptionResolver() {
@Override
public int getOrder() {
// ensure we can get some resolver earlier than this
return 10;
}
};
}
@Bean
public ServletContextInitializer sentryServletContextInitializer() {
return new SentryServletContextInitializer();
}
}
这将确保您可以让您的处理程序更早 运行。在您的代码中,获取 rootCause
的循环不正确
while (ex.getCause() != null) {
rootCause = ex.getCause();
}
这是一个无限循环,因为您使用了 ex
而不是 rootCause
。即使你更正它,它仍然会变成一个无限循环。当异常导致 returns 本身时,它将被卡住。我还没有彻底测试过它,但我相信它应该像下面这样
while (rootCause.getCause() != null && rootCause.getCause() != rootCause) {
rootCause = rootCause.getCause();
}
这是解决您的问题的一种方法。但是您需要自己将异常发送给 Sentry。所以还有另一种方法可以满足您的要求
方式 2
在这种情况下,您可以在配置中完成整个逻辑并将其更改为以下内容
@Configuration
public class FactoryBeanAppConfig {
@Bean
public HandlerExceptionResolver sentryExceptionResolver() {
return new SentryExceptionResolver() {
@Override
public ModelAndView resolveException(HttpServletRequest request,
HttpServletResponse response,
Object handler,
Exception ex) {
Throwable rootCause = ex;
while (rootCause .getCause() != null && rootCause.getCause() != rootCause) {
rootCause = rootCause.getCause();
}
if (!rootCause.getMessage().contains("Broken pipe")) {
super.resolveException(request, response, handler, ex);
}
return null;
}
};
}
@Bean
public ServletContextInitializer sentryServletContextInitializer() {
return new SentryServletContextInitializer();
}
}
我有一个 Spring 启动应用程序,它使用 Sentry 进行异常跟踪,但我收到一些如下所示的错误:
ClientAbortExceptionorg.apache.catalina.connector.OutputBuffer in realWriteBytes
errorjava.io.IOException: Broken pipe
我的理解是这只是一个网络错误,因此我通常应该忽略它们。我想做的是报告所有其他 IOExceptions
并将损坏的管道记录到 Librato,这样我就可以关注我得到了多少(尖峰可能意味着客户端有问题,这也是由开发的我在 Java):
我想到了这个:
@ControllerAdvice
@Priority(1)
@Order(1)
public class RestExceptionHandler {
@ExceptionHandler(ClientAbortException.class)
@ResponseStatus(HttpStatus.SERVICE_UNAVAILABLE)
public ResponseEntity<?> handleClientAbortException(ClientAbortException ex, HttpServletRequest request) {
Throwable rootCause = ex;
while (ex.getCause() != null) {
rootCause = ex.getCause();
}
if (rootCause.getMessage().contains("Broken pipe")) {
logger.info("count#broken_pipe=1");
} else {
Sentry.getStoredClient().sendException(ex);
}
return null;
}
}
这是处理此问题的可接受方法吗?
我按照文档这样配置了 Sentry:
@Configuration
public class FactoryBeanAppConfig {
@Bean
public HandlerExceptionResolver sentryExceptionResolver() {
return new SentryExceptionResolver();
}
@Bean
public ServletContextInitializer sentryServletContextInitializer() {
return new SentryServletContextInitializer();
}
}
如果你看 class SentryExceptionResolver
public class SentryExceptionResolver implements HandlerExceptionResolver, Ordered {
@Override
public ModelAndView resolveException(HttpServletRequest request,
HttpServletResponse response,
Object handler,
Exception ex) {
Sentry.capture(ex);
// null = run other HandlerExceptionResolvers to actually handle the exception
return null;
}
@Override
public int getOrder() {
// ensure this resolver runs first so that all exceptions are reported
return Integer.MIN_VALUE;
}
}
通过在 getOrder
中返回 Integer.MIN_VALUE
,它确保它首先被调用。即使您已将 Priority
设置为 1
,它也不会起作用。所以你想改变你的
@Configuration
public class FactoryBeanAppConfig {
@Bean
public HandlerExceptionResolver sentryExceptionResolver() {
return new SentryExceptionResolver();
}
@Bean
public ServletContextInitializer sentryServletContextInitializer() {
return new SentryServletContextInitializer();
}
}
至
@Configuration
public class FactoryBeanAppConfig {
@Bean
public HandlerExceptionResolver sentryExceptionResolver() {
return new SentryExceptionResolver() {
@Override
public int getOrder() {
// ensure we can get some resolver earlier than this
return 10;
}
};
}
@Bean
public ServletContextInitializer sentryServletContextInitializer() {
return new SentryServletContextInitializer();
}
}
这将确保您可以让您的处理程序更早 运行。在您的代码中,获取 rootCause
的循环不正确
while (ex.getCause() != null) {
rootCause = ex.getCause();
}
这是一个无限循环,因为您使用了 ex
而不是 rootCause
。即使你更正它,它仍然会变成一个无限循环。当异常导致 returns 本身时,它将被卡住。我还没有彻底测试过它,但我相信它应该像下面这样
while (rootCause.getCause() != null && rootCause.getCause() != rootCause) {
rootCause = rootCause.getCause();
}
这是解决您的问题的一种方法。但是您需要自己将异常发送给 Sentry。所以还有另一种方法可以满足您的要求
方式 2
在这种情况下,您可以在配置中完成整个逻辑并将其更改为以下内容
@Configuration
public class FactoryBeanAppConfig {
@Bean
public HandlerExceptionResolver sentryExceptionResolver() {
return new SentryExceptionResolver() {
@Override
public ModelAndView resolveException(HttpServletRequest request,
HttpServletResponse response,
Object handler,
Exception ex) {
Throwable rootCause = ex;
while (rootCause .getCause() != null && rootCause.getCause() != rootCause) {
rootCause = rootCause.getCause();
}
if (!rootCause.getMessage().contains("Broken pipe")) {
super.resolveException(request, response, handler, ex);
}
return null;
}
};
}
@Bean
public ServletContextInitializer sentryServletContextInitializer() {
return new SentryServletContextInitializer();
}
}