如何检测何时在应用程序中的任何地方引发了异常?
在Java桌面应用程序中的任何地方引发异常时,我都会尝试自动发送电子邮件给自己。我认为通过这种方式我可以更加主动。
我知道只要发生异常,我都可以明确地记录日志并通知自己,但是我必须在所有地方都做,而且我可能(很可能会)错过一对夫妇。
有什么建议吗?
您可能不想在任何例外情况下寄出邮件。 JDK中有很多代码实际上依赖于异常才能正常工作。我认为您更感兴趣的是未捕获的例外。如果您正在捕获异常,则应在此处处理通知。
在桌面应用程序中,有两个地方需要担心这一点:事件分发线程(EDT)和EDT之外。在全局范围内,您可以注册一个实现java.util.Thread.UncaughtExceptionHandler的类,然后通过java.util.Thread.setDefaultUncaughtExceptionHandler注册它。如果异常结束到堆栈的底部并且该线程尚未在该线程或ThreadGroup的当前线程实例上设置处理程序,则将调用此方法。
EDT具有用于处理异常的不同钩子。需要使用零参数构造函数的类的"完全合格的类名"注册系统属性'sun.awt.exception.handler'。此类需要完成您的工作的实例方法handle(Throwable)。返回类型无关紧要,并且由于每次都会创建一个新实例,因此不要指望保持状态。
因此,如果您不在乎哪个线程,则示例中发生的异常可能看起来像这样:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| 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设置默认值。
这至少可以帮助您找到想念的人。
在我当前的项目中,我面临有关错误检测的类似要求。为此,我采用了以下方法:我使用log4j跨应用程序进行日志记录,并且在所有捕获到异常的地方使用标准方法:log.error("Error's description goes here", e);,其中e是抛出的异常(有关详细信息,请参见log4j文档关于"日志"的初始化)。
为了检测错误,我使用了自己的Appender,它扩展了log4j AppenderSkeleton类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| 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配置文件必须仅包含新附加程序的定义,并将其附加到所选记录器(在我的情况下为root):
1 2
| log4j.rootLogger = OTHER_APPENDERS, ED
log4j.appender.ED=com.your.package.ErrorsDetectingAppender |
您可以在程序执行流程的某个重要时刻调用ErrorsDetectingAppender的errorsOccured()方法,也可以通过向append()方法中的if块添加功能来立即做出反应。这种方法与语义一致:检测到您认为错误并记录错误的内容。如果以后再考虑选择的错误不是那么重要,则只需将日志记录级别更改为log.warn(),就不会发送报告。
如果使用的是Java 1.3 / 1.4,则Thread.UncaughtExceptionHandler不可用。
在这种情况下,可以使用基于AOP的解决方案在引发异常时触发一些代码。 Spring和/或AspectJ可能会有所帮助。
如果您使用的是Spring之类的Web框架,则可以在web.xml中委派一个页面,然后使用控制器发送电子邮件。例如:
在web.xml中:
1 2 3 4
| <error-page>
<error-code>500</error-code>
<location>/error/500.htm</location>
</error-page> |
然后将/error/500.htm定义为控制器。您可以从参数javax.servlet.error.exception访问异常:
1
| Exception exception = (Exception) request.getAttribute("javax.servlet.error.exception"); |
如果您只是在运行常规Java程序,那么我想您会陷入公共静态void main(String [] args){试试{...} catch(Exception e){}}
没有充分的理由通知每个抛出的异常。我猜您假设抛出的异常表示您"需要"知道的"问题"。但这是错误的。如果引发,捕获和处理异常,一切都很好。您唯一需要担心的是引发但未处理(未捕获)的异常。但是您可以自己在try ... catch子句中做到这一点。
如果遇到运行时异常(例如OutOfMemoryError或StackOverflow),则可能无法发送电子邮件。您很有可能必须生成另一个进程并捕获由它引发的任何异常(使用上述各种技术)。
我假设您不是任何异常,而是任何未捕获的异常。
在这种情况下,Sun网站上的这篇文章有一些想法。您需要将顶级方法package在try-catch块中,还需要做一些额外的工作来处理其他线程。
在这种情况下,我认为最好的选择是编写一个自定义类加载器以处理应用程序中的所有类加载,并且每当请求异常类时,您都将返回package所请求的异常类的类。该package器调用package的异常,但也记录异常事件。