如何检测应用程序中的任何位置何时抛出异常?
每当我的Java桌面应用程序中的任何地方抛出异常时,我都会尝试自动向我发送电子邮件。我这样想,我可以更积极主动。
我知道我可以在发生异常时明确地记录并通知自己,但我必须在任何地方都这样做,我可能(更有可能)会错过一对。
有什么建议吗?
答案 0 :(得分:37)
你可能不希望邮寄任何例外。 JDK中有很多代码,它们依赖于异常才能正常工作。我认为你更不感兴趣的是未被捕获的例外。如果要捕获异常,则应在那里处理通知。
在桌面应用程序中,有两个地方需要担心,在event-dispatch-thread(EDT)和EDT之外。全局,您可以注册一个实现java.util.Thread.UncaughtExceptionHandler
的类,并通过java.util.Thread.setDefaultUncaughtExceptionHandler
进行注册。如果异常结束到堆栈的底部并且线程没有在线程或ThreadGroup上的当前线程实例上设置处理程序,则会调用此方法。
EDT有一个不同的钩子来处理异常。系统属性'sun.awt.exception.handler'
需要使用零参数构造函数的类的完全限定类名注册。这个类需要一个实现你工作的实例方法句柄(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
方法,您应该准备好了。
答案 1 :(得分:5)
Java 1.5中的新调试挂钩允许您执行此操作。它使例如在调试器中“中断任何异常”。
答案 2 :(得分:4)
结帐Thread.UncaughtExceptionHandler。您可以为每个线程设置它,也可以为整个VM设置默认值。
这至少可以帮助你找到你想念的那些。
答案 3 :(得分:1)
如果您使用的是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){}}
答案 4 :(得分:1)
如果您使用的是java 1.3 / 1.4,则Thread.UncaughtExceptionHandler不可用。 在这种情况下,您可以使用基于AOP的解决方案在抛出异常时触发某些代码。 Spring和/或aspectJ可能会有所帮助。
答案 5 :(得分:1)
在我目前的项目中,我遇到了关于错误检测的类似要求。为此我已经应用了以下方法:我使用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配置文件必须只包含新appender的定义及其对所选记录器的附件(在我的情况下为root):
log4j.rootLogger = OTHER_APPENDERS, ED
log4j.appender.ED=com.your.package.ErrorsDetectingAppender
您可以在程序执行流程中的某个重要位置调用ErrorsDetectingAppender的errorsOccured()方法,也可以通过在append()方法中向if块添加功能来立即做出反应。这种方法与语义一致:检测到您认为错误并将其记录的事物。如果您稍后会考虑选择的错误并不那么重要,您只需将日志记录级别更改为log.warn()并且不会发送报告。
答案 6 :(得分:0)
在这种情况下,我认为您最好的选择是编写一个自定义类加载器来处理应用程序中的所有类加载,并且每当请求异常类时,您都会返回一个包装请求的异常类的类。此包装器调用包装的异常,但也记录异常事件。
答案 7 :(得分:0)
我认为你并不是指任何异常,而是指任何未被捕获的异常。
如果是这种情况this article on the Sun Website有一些想法。您需要将顶级方法包装在try-catch
块中,并且还需要做一些额外的工作来处理其他线程。
答案 8 :(得分:0)
如果您收到OutOfMemoryError或StackOverflow等运行时异常,则可能无法发送电子邮件。很可能你必须产生另一个进程并捕获它抛出的任何异常(使用上面提到的各种技术)。
答案 9 :(得分:0)
没有充分的理由通知每个抛出的异常。我猜您假设抛出的异常表示您“需要”知道的“问题”。但这是错误的。如果引发,捕获和处理异常,一切都很好。您唯一需要担心的是抛出但未处理(未捕获)的异常。但是您可以自己在try
... catch
子句中做到这一点。