我有一个使用Sentry进行异常跟踪的Spring Boot应用程序,出现了一些错误,如下所示:

ClientAbortExceptionorg.apache.catalina.connector.OutputBuffer in realWriteBytes
errorjava.io.IOException: Broken pipe

我的理解是,这只是网络错误,因此我通常应该忽略它们。我想要做的是将所有其他IOExceptions报告给Librato,并将损坏的日志记录到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();
    }
}

最佳答案

如果您查看类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;
    }
}

通过在Integer.MIN_VALUE中返回getOrder,可以确保它首先被调用。即使您将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。即使您更正它,它仍然可能成为无限循环。当异常原因返回本身时,它将被卡住。我尚未对其进行彻底的测试,但我相信它应该像下面这样
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();
    }
}

关于java - 避免在Spring Boot应用程序中将损坏的管道错误报告给Sentry,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/48914391/

10-15 17:13