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
相关产品推荐
相关产品推荐

