Spring WebFlux性能日志切面异常时无法记录结束信息求助
问题根源分析
你的代码只在Mono/Flux成功完成时才会执行日志逻辑:
- 当Mono/Flux抛出异常时,
flatMap(Mono场景)和collectList().flatMapMany(Flux场景)这些操作符不会被触发,因为错误信号会跳过正常的流处理逻辑,直接终止流,导致logExecutionTime完全没机会执行。 - 同时未停止的
StopWatch也会一直处于运行状态,无法记录准确耗时。
解决方案:用
doFinally统一处理最终逻辑 响应式流中,doFinally操作符会在流无论成功完成、出错还是被取消时都执行,完美适配这种需要统一记录结束日志的场景。同时不需要修改原流的结构(比如避免用collectList把Flux转成Mono,破坏原有流式特性)。
修改后的完整代码
@Aspect @Component @Slf4j public class LoggingAspect { @Around("execution(* com.company.service..*(..)))") public Object logEndpointPerformance(ProceedingJoinPoint proceedingJoinPoint) throws Throwable { MethodSignature methodSignature = (MethodSignature) proceedingJoinPoint.getSignature(); String className = methodSignature.getDeclaringType().getSimpleName(); String methodName = methodSignature.getName(); final StopWatch stopWatch = new StopWatch(); log.debug("Start: {}.{}", className, methodName); stopWatch.start(); Object result = proceedingJoinPoint.proceed(); if(result instanceof Mono){ return ((Mono<?>) result) .doFinally(signalType -> logExecutionTime(className, methodName, stopWatch)); } else if(result instanceof Flux){ return ((Flux<?>) result) .doFinally(signalType -> logExecutionTime(className, methodName, stopWatch)); } else{ logExecutionTime(className, methodName, stopWatch); return result; } } private void logExecutionTime(final String className, final String methodName, final StopWatch stopWatch){ if(stopWatch.isRunning()){ stopWatch.stop(); } log.debug("Exit: {}.{}", className, methodName); log.debug("[ {} mls ] lasted execution of {}.{}", stopWatch.getTotalTimeMillis(), className, methodName); } }
关键修改说明
- 替换
flatMap/collectList为doFinally:- 不管Mono/Flux是成功完成还是抛出异常,
doFinally都会执行,确保Exit日志和耗时记录不会丢失。 - 保留了原响应式流的特性:Flux不需要先收集所有元素再返回,依然是流式输出,不会改变业务逻辑。
- 不管Mono/Flux是成功完成还是抛出异常,
- 增加StopWatch运行状态判断:
- 在
logExecutionTime里检查stopWatch.isRunning(),避免重复停止或者未启动就停止的情况,确保耗时计算准确。
- 在
- 优化日志格式:用SLF4J的占位符
{}代替字符串拼接,更高效且避免潜在的性能问题。
额外注意事项
- 如果需要区分成功/失败/取消场景的日志,可以通过
doFinally的SignalType参数判断(比如SignalType.ON_COMPLETE/SignalType.ON_ERROR/SignalType.CANCEL),按需调整日志内容。 - 原代码中
collectList会导致Flux必须等待所有元素生成完成后才返回,这可能不符合响应式编程的流式设计初衷,修改后避免了这个问题。
内容的提问来源于stack exchange,提问作者AD90
相关产品推荐
相关产品推荐

