我有一个使用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/