You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring Reactive MongoDB如何捕获运行时异常?优化重复键异常日志

如何在Spring Boot Reactive中捕获MongoDB DuplicateKeyException并避免冗长堆栈日志

我完全懂你的困扰——明明用了onErrorResume拦截异常,结果还是会输出一大段扎眼的堆栈跟踪,这确实很影响日志可读性。问题核心在于Reactor的默认错误丢弃处理机制:即便你在流中捕获了错误,被丢弃的错误还是会触发默认的onErrorDropped逻辑,打印完整堆栈。下面是几个实用的解决方案:

1. 自定义onErrorDropped处理被丢弃的错误

直接在流中添加onErrorDropped方法,专门针对DuplicateKeyException打印友好提示,其他错误按需处理:

@Scheduled(fixedDelay = Milliseconds.MONTH)
private void synchronizeForbesOrganizations() {
    System.out.println("Forbes Organizations Synchronizer");
    this.forbesOrganizationService.save(this.forbesAPIService.getGlobal2000())
            .take(ForbesAPI.Global2000.SIZE)
            .subscribeOn(Schedulers.parallel())
            // 精准捕获DuplicateKeyException,返回空流终止当前分支
            .onErrorResume(DuplicateKeyException.class, e -> {
                System.err.println("⚠️ 重复键错误:" + e.getMessage());
                return Flux.empty();
            })
            // 处理被丢弃的错误,替换默认的堆栈打印逻辑
            .onErrorDropped(e -> {
                if (e instanceof DuplicateKeyException) {
                    System.err.println("⚠️ 已丢弃重复键错误:" + e.getMessage());
                } else {
                    // 非重复键错误可按需选择打印堆栈或精简提示
                    System.err.println("❌ 未预期错误:" + e.getMessage());
                }
            })
            .subscribe();
}

2. 使用onErrorContinue跳过重复项,继续处理其他数据

如果你的需求是遇到重复项时跳过,继续同步剩余数据(而不是终止整个流),那onErrorContinue比onErrorResume更贴合场景:

@Scheduled(fixedDelay = Milliseconds.MONTH)
private void synchronizeForbesOrganizations() {
    System.out.println("Forbes Organizations Synchronizer");
    this.forbesOrganizationService.save(this.forbesAPIService.getGlobal2000())
            .take(ForbesAPI.Global2000.SIZE)
            .subscribeOn(Schedulers.parallel())
            .onErrorContinue((e, entity) -> {
                if (e instanceof DuplicateKeyException) {
                    System.err.println("⚠️ 跳过重复条目:" + entity + ",错误信息:" + e.getMessage());
                } else {
                    // 非重复键错误可选择抛出或自定义处理
                    throw new RuntimeException("同步失败:" + e.getMessage(), e);
                }
            })
            .subscribe();
}

3. 调整日志配置,抑制默认堆栈打印

如果不想修改代码,也可以通过日志配置直接控制reactor.core.publisher.Operators的输出格式,只打印错误消息不输出堆栈。在application.yml中添加:

logging:
  level:
    reactor.core.publisher.Operators: WARN
  pattern:
    console: "%d{yyyy-MM-dd HH:mm:ss.SSS} %-5level %logger{36} : %msg%n"

这样默认的onErrorDropped只会打印精简的错误消息,不会输出冗长的堆栈跟踪。

为什么会出现这个问题?

Reactor中当错误被丢弃(比如并行处理中流已终止后出现的错误),默认的onErrorDropped逻辑会强制打印完整堆栈——这和你在流中用onErrorResume捕获错误的逻辑是相互独立的。所以必须要么自定义onErrorDropped,要么通过日志配置来抑制堆栈输出。

内容的提问来源于stack exchange,提问作者1Z10

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.08 21:32:38