Java 中如何记录未捕获的异常?

Java 记录未捕获异常:实现 Thread.UncaughtExceptionHandler 接口并设为默认处理器,逃出线程 run() 的异常可触发处理器,Future 保存的异常需另行观察。本文给出完整实现。

最佳实践
Java 中如何记录未捕获的异常?封面

核心:实现 Thread.UncaughtExceptionHandler 接口,在 uncaughtException() 方法中用日志框架记录异常,然后通过 Thread.setDefaultUncaughtExceptionHandler() 注册为全局默认处理器。

完整实现

import java.util.logging.Level;
import java.util.logging.Logger;

public class GlobalExceptionHandler implements Thread.UncaughtExceptionHandler {
    private static final Logger LOGGER = Logger.getLogger(GlobalExceptionHandler.class.getName());

    @Override
    public void uncaughtException(Thread t, Throwable e) {
        LOGGER.log(Level.SEVERE, "线程 " + t.getName() + " 中出现未捕获异常", e);
    }
}

在程序入口注册:

public class Main {
    public static void main(String[] args) {
        Thread.setDefaultUncaughtExceptionHandler(new GlobalExceptionHandler());

        // 正常业务逻辑...
    }
}

此后逃出线程 run() 的异常按该线程的处理器规则处理;线程专属处理器或 ThreadGroup 可优先于默认处理器,框架内部捕获的异常不会因此进入默认处理器。

对单个线程设置

Thread thread = new Thread(() -> {
    throw new RuntimeException("出错了");
});
thread.setUncaughtExceptionHandler((t, e) -> {
    System.err.println("线程 " + t.getName() + " 异常: " + e.getMessage());
});
thread.start();

线程池中的注意事项

ThreadPoolExecutor 的 execute() 提交的 Runnable 抛出的异常会走 UncaughtExceptionHandler;但 submit() 返回的 Future 中的异常不会——它被封装在 Future 里,调用 get() 时才抛出。

常见问题(FAQ)

Q:Spring Boot 项目需要手动注册吗?
A:Spring Boot 的 @ControllerAdvice + @ExceptionHandler 处理 Controller 层的异常;非 Web 层的异常仍建议注册全局 UncaughtExceptionHandler。

Q:捕获后进程会退出吗?
A:发生异常的线程终止;当所有已启动的非守护线程结束后,JVM 才开始关闭。main 线程异常时若仍有其他非守护线程运行,进程不会仅因此立即退出。

Q:log4j2/SLF4J 怎么用?
A:把上面示例中的 java.util.logging 替换为你项目使用的日志框架即可,逻辑不变。

获取专属方案

联系我们

加入社区

微信扫码
加入官方交流群

立即体验

在线开通,按量计费,真正的云服务!

立即开始

选择观测云版本

代码托管平台