如何检测应用程序中何时抛出异常?
每当在我的 Java 桌面应用程序中的任何地方引发异常时,我都会尝试自动向自己发送一封电子邮件。我认为这样我可以更积极主动。
我知道我可以在发生异常时明确记录并通知自己,但我必须在任何地方都这样做,我可能(更有可能)会错过一些。
有什么建议么?
您可能不想邮寄任何例外情况。JDK中有很多代码实际上依赖异常才能正常工作。我认为您更感兴趣的是未捕获的异常。如果您正在捕获异常,您应该在那里处理通知。
在桌面应用程序中,有两个地方需要担心这一点,即事件调度线程(EDT) 和 EDT 之外。Globaly,您可以注册一个实现java.util.Thread.UncaughtExceptionHandler
并通过java.util.Thread.setDefaultUncaughtExceptionHandler
. 如果异常结束到堆栈底部并且线程没有在线程或 ThreadGroup 上的当前线程实例上设置处理程序,则会调用此方法。
EDT 有一个不同的钩子来处理异常。系统属性'sun.awt.exception.handler'
需要使用具有零参数构造函数的类的完全限定类名称进行注册。这个类需要一个实例方法handle( Throwable
)来完成你的工作。返回类型无关紧要,因为每次都会创建一个新实例,所以不要指望保持状态。
因此,如果您不关心样本中发生异常的线程可能如下所示:
class ExceptionHandler implements Thread.UncaughtExceptionHandler {
public void uncaughtException(Thread t, Throwable e) {
handle(e);
}
public void handle(Throwable throwable) {
try {
// insert your e-mail code here
} catch (Throwable t) {
// don't let the exception get thrown out, will cause infinite looping!
}
}
public static void registerExceptionHandler() {
Thread.setDefaultUncaughtExceptionHandler(new ExceptionHandler());
System.setProperty("sun.awt.exception.handler", ExceptionHandler.class.getName());
}
}
将这个类添加到一些随机包中,然后调用该registerExceptionHandler
方法,您应该准备好了。
Java 1.5 中的新调试钩子让您可以做到这一点。它可以在调试器中启用例如“中断任何异常”。
这是您需要的特定 Javadoc 。
查看Thread.UncaughtExceptionHandler。您可以为每个线程设置它,也可以为整个 VM 设置一个默认值。
这至少会帮助你抓住你错过的那些。
如果您使用的是Spring等 Web 框架,则可以将 web.xml 委托给页面,然后使用控制器发送电子邮件。例如:
在 web.xml 中:
<error-page>
<error-code>500</error-code>
<location>/error/500.htm</location>
</error-page>
然后将 /error/500.htm 定义为控制器。您可以从参数 javax.servlet.error.exception 访问异常:
Exception exception = (Exception) request.getAttribute("javax.servlet.error.exception");
如果你只是在运行一个普通的 Java 程序,那么我想你会被 public static void main(String[] args) { try { ... } catch (Exception e) {} }
如果您使用的是 java 1.3/1.4,则 Thread.UncaughtExceptionHandler 不可用。在这种情况下,您可以使用基于 AOP 的解决方案在抛出异常时触发一些代码。Spring 和/或 aspectJ 可能会有所帮助。
在我当前的项目中,我面临着关于错误检测的类似要求。为此,我应用了以下方法:我使用 log4j 在我的应用程序和任何地方进行日志记录,在捕获异常的地方我做标准的事情: log.error("Error's description goes here", e);
,其中 e 是抛出的异常(有关初始化的详细信息,请参阅 log4j 文档的“日志”)。为了检测错误,我使用了自己的 Appender,它扩展了 log4j AppenderSkeleton 类:
import org.apache.log4j.AppenderSkeleton;
import org.apache.log4j.spi.LoggingEvent;
public class ErrorsDetectingAppender extends AppenderSkeleton {
private static boolean errorsOccured = false;
public static boolean errorsOccured() {
return errorsOccured;
}
public ErrorsDetectingAppender() {
super();
}
@Override
public void close() {
// TODO Auto-generated method stub
}
@Override
public boolean requiresLayout() {
return false;
}
@Override
protected void append(LoggingEvent event) {
if (event.getLevel().toString().toLowerCase().equals("error")) {
System.out.println("-----------------Errors detected");
this.errorsOccured = true;
}
}
}
log4j 配置文件必须只包含新附加程序的定义及其对选定记录器的附件(在我的情况下为根):
log4j.rootLogger = OTHER_APPENDERS, ED
log4j.appender.ED=com.your.package.ErrorsDetectingAppender
您可以在程序执行流程中的某个重要点调用 ErrorsDetectingAppender 的 errorsOccured() 方法,也可以通过在 append() 方法中的 if 块中添加功能来立即做出反应。这种方法与语义一致:检测到您认为错误并记录它们的事物。如果您稍后认为选定的错误不那么重要,您只需将日志记录级别更改为 log.warn() 并且不会发送报告。
在这种情况下,我认为您最好的选择可能是编写一个自定义类加载器来处理应用程序中的所有类加载,并且每当请求异常类时,您都会返回一个包装所请求的异常类的类。这个包装器调用被包装的异常,但也记录异常事件。
我假设您的意思不是任何异常,而是任何未捕获的异常。
如果是这种情况,Sun 网站上的这篇文章有一些想法。您需要将顶级方法包装在一个try-catch
块中,并且还需要做一些额外的工作来处理其他线程。
如果遇到 OutOfMemoryError 或 StackOverflow 等运行时异常,则可能无法发送电子邮件。很可能您将不得不生成另一个进程并捕获它引发的任何异常(使用上面提到的各种技术)。
根本没有充分的理由被告知每个抛出的异常。我猜您假设抛出的异常表明您“需要”知道的“问题”。但这是错误的。如果抛出、捕获和处理异常,一切都很好。您唯一需要担心的是抛出但未处理(未捕获)的异常。但是您可以自己在try
...catch
子句中执行此操作。