completablefuture.whencomplete不捕获异常,需在回调中通过throwable非null判断异常并记录日志,推荐用logger.error("msg", throwable)保留完整堆栈,且不可用tostring()替代;它仅观察不改变结果,异常处理应选exceptionally或handle。

CompletableFuture.whenComplete 本身不会捕获异常,它只是在阶段完成(无论成功或失败)时执行回调,所以要记录异常日志,必须在回调中显式检查是否发生异常。
判断是否异常完成
whenComplete 的参数是 (result, throwable) → {},其中 throwable 非 null 就表示该 CompletableFuture 以异常方式完成(即调用了 completeExceptionally() 或上游抛出了未捕获异常)。
- 如果
throwable != null,说明有异常,可直接记录日志 - 如果
throwable == null,说明正常完成,result是有效返回值
推荐的日志记录写法
避免只打印 throwable 而忽略上下文。建议包含任务标识、异常堆栈和关键参数(如 ID、操作类型):
- 使用 SLF4J 或 Log4j 等框架的
logger.error("msg", throwable)形式,确保堆栈完整输出 - 不要用
throwable.toString()或getMessage()替代完整异常对象,否则丢失堆栈信息 - 可在日志中附加业务上下文,例如:
"Failed to process order #12345: {}"
注意与 handle/exceptionally 的区别
whenComplete 不改变结果或异常,仅用于“观察”;若需拦截并处理异常(比如转为默认值),应选 handle 或 exceptionally:
-
whenComplete:适合纯监控、审计、清理资源(如关闭连接)、打点统计 -
exceptionally:适合兜底返回默认值,且会吞掉异常(下游不再感知) -
handle:适合统一处理成功/失败两种情况,并可决定返回什么
实际示例代码
以下是一个典型用法:
future.whenComplete((result, ex) -> {
if (ex != null) {
logger.error("Async task failed for user id: {}, operation: updateProfile", userId, ex);
} else {
logger.info("Async task succeeded for user id: {}, result: {}", userId, result);
}
});Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











