Java未捕获全局异常处理程序

35

我有一个应用程序,需要编写一个自定义的全局未捕获异常处理程序。我已经阅读了所有的stackoverflow线程,但它们都缺少一个清晰简单的实现示例。

考虑以下简单示例:

public static void log(String msg) throws Exception {

    String logPath = "/application/logs/java.log";

    Calendar c = new GregorianCalendar();
    SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    String now = format.format(c.getTime());

    PrintWriter out = new PrintWriter(new BufferedWriter(new FileWriter(logPath, true)));
    out.println(now + "  " + msg);
    out.close();

}

它抛出一个标准异常,只是一个标准输出。我如何实现自己的异常,通过将错误输出到日志文件来覆盖标准异常?显然,实际应用程序要大得多,我们正在谈论未捕获的异常,这就是为什么try/catch块不是选项的原因。
更新:如果您能提供一个非常清晰的完整示例作为答案,那就太好了,因为我找到了数十个像@RamonBoza提供的示例,但我不知道如何实现它们,这就是这个问题的全部内容。
更新2:所以我尝试测试下面唯一的示例,显然它不起作用。我创建了一个新的类文件“ MyRunnable”并将代码粘贴到其中:
package mypackage;

Thread t = new Thread(new MyRunnable());
t.setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {

    public void uncaughtException(Thread t, Throwable e) {
       LOGGER.error(t + " throws exception: " + e);
    }
});
t.start();

//outside that class
class MyRunnable implements Runnable(){
    public void run(){
        throw new RuntimeException("hey you!");
    }
}

毫无疑问,我遇到了很多错误。但是,任何东西怎么可能在类的外面呢?

更新3: 这是最终可行的解决方案:

package mypackage;

public class MyClass {

    public static void main(String[] args) throws Exception {

        Thread.currentThread().setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() { 
            public void uncaughtException(Thread t, Throwable e) { 
                StringWriter sw = new StringWriter();
                e.printStackTrace(new PrintWriter(sw));
                String stacktrace = sw.toString();
                System.out.println(stacktrace);
            }
        });     

        //main method code

    }

    //Rest of the methods here

}

它抛出哪个异常?能否提供堆栈跟踪信息? - vallentin
@Vallentin 这只是应用程序中数十种方法之一。这不仅仅涉及一个特定的异常,我需要处理所有可能的未捕获异常。 - Caballero
1
你在这里的高层目标是什么?当出现未捕获的异常时,你希望发生什么?为什么你想要这样做?我怀疑可能有更好的方法来解决这个问题。 - Tom Anderson
@TomAnderson 这个问题解释了我为什么需要这个:http://stackoverflow.com/questions/19401904/java-error-log-output-ubuntu - Caballero
@Caballero:啊哈。通常情况下,我建议你不要在Java中尝试捕获未捕获的异常,而是让Java进程崩溃,并在更高层次上捕获输出。但看起来这正是你想要解决的问题! - Tom Anderson
为了让它对所有线程可用,请使用您的线程的ThreadGroup,如此处所述:http://stackoverflow.com/questions/33344294/java-uncaught-global-exception-handler-for-all-threads/33344392#33344392 - Benj
4个回答

34

您可以为控制上述代码的线程设置UncaughtExceptionHandler:

// t is the parent code thread
t.setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {

    public void uncaughtException(Thread t, Throwable e) {
       LOGGER.error(t + " throws exception: " + e);
    }
 });

UncaughtExceptionHandler的Java文档 -

当一个线程由于未捕获的异常即将终止时,Java虚拟机将使用Thread.getUncaughtExceptionHandler()查询线程的UncaughtExceptionHandler,并调用处理程序的uncaughtException方法,将线程和异常作为参数传递。

setUncaughtExceptionHandler通常用于释放内存或杀死系统无法杀死且可能仍然处于僵尸状态的线程。

一个真实的例子:

Thread t = new Thread(new MyRunnable());
t.setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {

    public void uncaughtException(Thread t, Throwable e) {
       LOGGER.error(t + " throws exception: " + e);
    }
});
t.start();

//outside that class
class MyRunnable implements Runnable(){
    public void run(){
        throw new RuntimeException("hey you!");
    }
}

1
我该如何将这个放入我的代码中?“t”到底是什么,我应该在哪里和如何初始化它? - Caballero
在注释中,t 是包含您的代码的父线程,顺便说一下,您可以在主线程上使用 setUncaughtExceptionHandler 方法来捕获所有未捕获的异常。例如:Thread t = new Thread(); t.setUncaughtExceptionHandler(...); - RamonBoza
1
抱歉直言,但我还是要问一下 - 我该如何使用它,放在哪里?我的应用程序甚至没有使用多线程,那么“父线程”是什么? - Caballero
Thread.currentThread() 获取当前线程的对象。你可以将UncaughtExceptionHandler附加到它上面。 - Tom Anderson
所有的Java应用程序都至少有一个线程,因此如果您没有手动创建任何线程,请将其放在主线程中,并使用Thread.currentThread().setUncaughtExceptionHandler。 - RamonBoza
显示剩余4条评论

5
您可以使用 UncaughtExceptionHandler 来处理导致线程突然终止的异常。 UncaughtExceptionHandler 的 Java 文档如下:

接口,用于处理由于未捕获的异常而导致线程突然终止时调用的处理程序。当一个线程即将因未捕获的异常而终止时,Java 虚拟机将使用 Thread.getUncaughtExceptionHandler() 查询线程的 UncaughtExceptionHandler,并调用处理程序的 uncaughtException 方法,传递线程和异常作为参数。

您需要实现此接口并使用 setUncaughtExceptionHandler 方法为线程设置 UncaughtExceptionHandler 的实现。有两种方法可以做到这一点:
  1. setUncaughtExceptionHandler
  2. setDefaultUncaughtExceptionHandler
1) setUncaughtExceptionHandler - 这将是特定于线程的异常处理程序。因此,在某些未处理的异常导致该线程终止时,将使用此处理程序。
2) setDefaultUncaughtExceptionHandler - 如果没有特定的未捕获异常处理程序,则将使用此默认异常处理程序。
示例:
class MyExceptionHandler implements UncaughtExceptionHandler{
    @Override
    public void uncaughtException(Thread arg0, Throwable arg1) {
        System.out.println("[DEFAULT EXCEPTION HANDLER] Caught some exception");
    }
}

class MyThread extends Thread{
    public MyThread() {
        setUncaughtExceptionHandler(new UncaughtExceptionHandler() {
            @Override
            public void uncaughtException(Thread t, Throwable e) {
                System.out.println("[THREAD SPECIFIC] My exception handler");
            }
        });
    }
    public void run(){
        throw new RuntimeException();
    }
}

public class Test {

    public static void main(String[] args) {
        Thread.setDefaultUncaughtExceptionHandler(new MyExceptionHandler());
        new MyThread().start();
        // current thread does not have a thread specific exception handler
        throw new RuntimeException();
    }

}

0

Main.java

import static org.app.DefaultUncaughtExceptionHandler.registerDefaultUncaughtExceptionHandler;

static {
    registerDefaultUncaughtExceptionHandler();
}

DefaultUncaughtExceptionHandler.java

import java.lang.Thread.UncaughtExceptionHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * @see ThreadGroup#uncaughtException(Thread, Throwable)
 */
public class DefaultUncaughtExceptionHandler implements UncaughtExceptionHandler {

    private static final Logger logger = LoggerFactory.getLogger(DefaultUncaughtExceptionHandler.class);
    private static final UncaughtExceptionHandler parent;
    private static final DefaultUncaughtExceptionHandler instance;

    static {
        parent = Thread.getDefaultUncaughtExceptionHandler();
        instance = new DefaultUncaughtExceptionHandler();
        Thread.setDefaultUncaughtExceptionHandler(instance);
    }

    public static final void registerDefaultUncaughtExceptionHandler() {
        // registered
    }

    @Override
    public void uncaughtException(Thread t, Throwable e) {
        if (!(e instanceof ThreadDeath))
            logger.error("Exception in thread \"" + t.getName() + "\" ", e);
        if (parent != null)
            parent.uncaughtException(t, e);
    }
}

-1

你可以尝试设置自己的未捕获异常处理程序, 但我强烈建议不要这样做。如果你的应用程序有可能出现问题,而且如果该异常在代码中断的位置没有得到适当处理,那么你的整个程序将经常出现故障。


11
异常的整个意义在于不必在抛出异常的位置处理异常,而是在一个更高的层次上进行处理,该层次更了解情况。 - gpeche

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接