获取 Java 中的所有异常并远程发送

共8个回答, 标签: java spring aspect spring-aspects

我有一个巨大的 Java 应用程序。我想拦截所有 Java 异常,并通过电子邮件发送它们。我不能到处添加通过发送代码的代码try-catch那么,是否可以使用例如方面来拦截异常到低级别的类,并获得异常内容?

或者有没有办法重写一些内部 Java 类并获得异常有效载荷?

什么是可能的?

第1个答案

您可以使用@AfterThrowing的建议春天-aop.

@ 方面
@ 组件
公共类 MailExceptionAspect {

@ Afterriting (值 = “执行 (* com.example..*。* (.)”,投掷 = “ex”)
公共无效邮件投掷 (Throwable ex) {
//做点什么来发送电子邮件
}
}

这将拦截包中所有未处理的异常com.example。请注意,应用程序中处理 (捕获) 的异常不能被截获。

另一个解决方案是使用应用程序的日志框架。许多框架,如 logback,log4j 提供内置配置,可以通过电子邮件发送日志。

第2个答案

看看春天@ControllerAdvice annotation. We use that to do exactly what I think you want. We have a web application that has a number of @Controllers and @RestControllers. This will send an email with a number of details about the request that triggered it whenever an error is thrown by any method in those controllers. We don't send emails for ClientAbortExceptions,因为这些经常发生在用户在处理请求时关闭浏览器。

@ ControllerAdvice
公共类 GlobalExceptionHandler {

私有最终记录器记录器 = LoggerFactory.getLogger (GlobalExceptionHandler.class);

私有静态最终字符串 ERROR_EMAIL_ADDRESS = "foo@bar.com";
私有静态最终字符串 APPLICATION_ERROR_SUBJECT = “出现 Foo 错误”;
私有静态最终字符串 USER_AGENT = "user-agent";

@ ExceptionHandler (值 = Exception.class)
@ ResponseStatus (HttpStatus.INTERNAL_SERVER_ERROR)
Public ResponseEntity defaulterrollandler (最终 HttpServletRequest 请求,最终主要主体,最终异常 e) {
最终字符串 userTime = principal.getName () “在” 新日期 () 时触发错误;
最终字符串 userAgent = "User-Agent:" StringUtils.trimToEmpty (request.getHeader (USER_AGENT));
最终字符串 url = "URL:" StringUtils.trimToEmpty (request.getRequestURL ().toString ());
最终字符串 httpMethod = "HTTP 方法:" request.getMethod ();

最终 StringBuilder emailSb = 新 StringBuilder ();
EmailSb.append (用户时间).append ("\ n");
EmailSb.append (用户代理).append ("\ n");
EmailSb.append (url).append ("\ n");
EmailSb.append (httpMethod).append ("\ n");

If (e instanceof ClientAbortException) {
Logger.debug (“不发送 socketExceptions 的电子邮件”);
} 其他 {
EmailSb.append (ExceptionUtils.getStackTrace (e));
//只是一个简单的 util 类,我们用 javax.mail api 发送电子邮件
EmailUtil.sendEmail (ERROR_EMAIL_ADDRESS,ERROR_EMAIL_ADDRESS,APPLICATION_ERROR_SUBJECT,
EmailSb.toString ();
}

返回新的响应实体 (HttpStatus.INTERNAL_SERVER_ERROR);
}

}
第3个答案

如果你有 ht 控制中的所有运行线程,你可以用你的 Thread.UncaughtExceptionHandler 的实现来标记它们。当然,如果应用程序具有深的多线程性质,这可能会有点棘手。

第4个答案

有关错误处理,请阅读此

Https://www.toptal.com/java/spring-boot-rest-api-error-handling

有关错误详细信息和发送电子邮件,请获取打印轨道

公共字符串 printTraceMessage (Exception ex) {
StringWriter 错误 = 新 StringWriter ();
例如。printStackTrace (新的 PrintWriter (错误));
返回错误。toString ();
}

或者你可以使用单独的线程非阻止响应并发送电子邮件

第5个答案

所以,这就是我们对基于 Spring 的 web 应用程序所做的。

为了捕捉所有意外的异常,我们有一个异常 servlet 过滤器,它是过滤器链中的第一个/最后一个过滤器。

此过滤器将捕获任何异常,然后向我们发送电子邮件。顺便说一句,我们有一份不报告的例外白名单。认为客户中止例外。对我们来说,真的没有任何理由报告这些。

对于由于用户请求而发生但不应该干扰用户结果的任务, 我们用一个 try/catch 来包装这些动作,然后如果那个侧面动作失败,我们会发送一封电子邮件。

一个附带操作的例子是,如果有人将新数据保存到数据库中,则更新搜索索引。最终用户只想知道他们的项目已成功保存到数据库中,但他们不需要知道搜索索引的更新失败。我们 (开发人员),但总的来说,最终用户不在乎。

然后对于需要自己线程的后端任务,我们创建了一个执行 try/catch 语句的线程,如果抛出异常,将发送一封电子邮件。

像这样的任务的一个例子是重新索引你的搜索索引。这可能是一个长时间运行的过程,我们不想在该过程运行的整个时间内保持 http 连接打开,所以我们创建了一个新的线程来运行重新索引。如果出了问题,我们想知道。

以下是一些示例代码,向您展示我们如何实现我们的服务.

@ Transactional
公共 UUID saveRecord (记录请求) {

Record newRecord = this.recordFactory.create (recordRequest);

这个.recordRepository.add (newRecord);

这个.updateSearch (newRecord);
}

私人无效更新搜索 (记录记录) {

尝试 {

本.searchIndex.add (记录);

Catch (例外 e) {

This.errorService.reportException (e);
}
}

以下是异常处理过滤器的代码:

公共 void doFilter (ServletRequest 请求,ServletResponse 响应,FilterChain filterChain) 抛出 IOException,ServletException {

尝试 {

FilterChain.doFilter (请求,响应);

} Catch (Throwable exception) {

This.handleException (请求、响应、异常);
}
}

私有 void handleException (ServletRequest 请求,ServletResponse 响应,Throwable throwable) {

尝试 {

这个.doHandleException (请求,响应,throwable);

} Catch (异常处理异常) {

日志。错误 (“未由 UnhandledExceptionFilter 处理的此异常”,可 throwable);
日志。错误 (“此异常发生,报告未处理的异常,请参见上面的“ 异常 ”,handlingException);
}
}

私有 void doHandleException (ServletRequest 请求,ServletResponse 响应,Throwable throwable) 抛出异常 {

这个.errorResponse.send (请求,响应);

This.reportException (请求、响应、可 throwable);

}

/* *
* 报告异常。
*
* @ Param 请求请求
* @ Param 响应响应
* @ Param throwable throwable
*/
受保护的 void reportException (ServletRequest 请求,ServletResponse 响应,Throwable throwable) {

UnhandledException unhandledException = this.setupExceptionDetails (HttpServletRequest) request,(HttpServletResponse) response,throwable);

This.exceptionHandlingService.Handle unexpectedexception (unhandledException);
}

私有 UnhandledException setupExceptionDetails (HttpServletRequest 请求,HttpServletResponse 响应,Throwable throwable) {

UnhandledException unhandledException = 新的 UnhandledException (throwable);

如果 (response.isCommitted ()) {
UnhandledException.put (“会话 Id”,“响应已经提交,无法获得会话 Id”);
} 其他 {
UnhandledException.put (“会话 Id”,request.getSession ().getId ());
}
UnhandledException.put (“远程地址”,request.getRemoteAddr ());
未处理
第6个答案

您可以按照这些步骤远程发送错误。我正在使用 html,将其添加到虚拟机文件 (Apache-Velocity-Template) 中

enter image description here

Api 演示

@ RequestMapping (值 = “/xyz”,方法 = RequestMethod.POST,产生 = MediaType.APPLICATION_JSON_VALUE)
公共 APIResponse xyz (@ RequestBody 字符串 json) {
Long startTime = System.currentTimeMillis ();
尝试 {
} Catch (异常前) {
记录器。错误 (“错误: =” + ex);
在 errovo 中添加构造函数
//ProfileType 表示服务器像 (staging | prod)
ErrorVo apiError = 新 ErrorVo (“/xyz”,这个。 profileType,“XYZRestApi”,“方法名称”,LocalDateTime。现在,这个。 extUtil。 printTraceMessage (ex);
This.extUtil.sendErrorEmail (apiError);
}
Logger.info (“响应时间: = = {} MS = =:”,System.currentTimeMillis ()-startTime);
返回此.apiResponse;
}

将这些依赖项添加到Pom.xml文件


    Org.apache.velocity
    速度
    1.7



    Org.apache.velocity
    速度-工具
    2.0

在 error.vm 中添加 Html,并将其放在资源/模板文件夹





无标题文档





        Api 中断警报。


        请尽快与支持团队联系以解决问题。




                  联系电话:$ Request.getPhoneNumber ()




                  电子邮件:$ Request.Getelectors ()




                  终点:$ Request.getEndPoint ()




第7个答案

最简单的方法,我将如何做到这一点 (如果它是一个网络应用程序) 是创建一个过滤器,并将其映射到所有请求,并放置一个尝试捕捉围绕 filterChain.doFilter,这将是一个单独的地方做想要的东西。

您可以使用 logger 的 mail Appender 发送邮件,而无需编写任何额外的代码。从我的 log4j2.xml 片段

公共类 extends 扩展了 OncePerRequestFilter {

@ 覆盖
受保护的 void doFilterInternal (HttpServletRequest 请求、 HttpServletResponse 响应、 FilterChain filterChain)
抛出 ServletException,IOException {
尝试 {
FilterChain.doFilter (requestCopier,responseCopier);

}
Catch (例外 e) {

记录器。错误 (“错误信息”,e)
扔 e;
}

最后 {

}
}

}

Log4j2.xml






            $ {MAIL_LOG_PATTERN}




第8个答案

有可能实现自己的java.lang.Throwable类。为了让 JVM 使用它,在启动进程时必须设置 JVM bootclasspath。Windows 上使用 Java 8 的示例:

Java.exe-Xbootclasspath/p: C: \..\ ReplceJavaLangClasses \ bin-classpath。..MyApp

在本例中,文件夹C:\..\ReplaceJavaLangClasses\bin contains the class of the modified copy of the original java.lang Throwable.java code, as usual in the proper package sub folder java/lang/Throwable.class。现在,您可以添加自己的异常管理,例如:

...
公共 Throwable (字符串消息) {
FillInStackTrace ();
DetailMessage = message;
System.out.println (“################ 我的附加代码 code”);
}

通过修改所有构造函数,您可以对所有异常实例做出反应。

相关问题

Java 是 "逐项传递" 还是 "按值传递"? 如何比较 Java 中的字符串? 什么是 Null Pointerexception, 我如何修复它? Spring Boot 2-在 bean 初始化之前做一些事情 获取 Java 中的所有异常并远程发送 如果 Spring 可以在 @ Configuration 类中成功拦截类内函数调用,为什么它在常规 bean 中不支持它?