应递归遍历异常链获取根本原因,依据最底层或特定类型异常(如SQLException、IOException)匹配预设规则映射日志级别,支持继承判断,避免仅依赖顶层异常导致误判。

在AOP日志切面中,不能仅靠 exception.toString() 或简单打印来区分错误严重性;关键是在捕获异常后,递归调用 getCause() 遍历整个异常链,根据**最底层或特定类型异常**决定日志级别(如 ERROR、WARN、INFO)。
遍历异常链识别根本原因
很多异常是包装型的(如 RuntimeException 包裹 SQLException),真实问题藏在 cause 里。需循环获取 getCause(),直到为 null 或命中预设关注类型:
- 用
Throwable cause = ex;初始化,进入while (cause != null)循环 - 每次检查
cause.getClass()是否匹配你定义的“高危类型”(如SQLException.class、IOException.class) - 若匹配,记录为
ERROR;若匹配的是可预期异常(如ValidationException),可降级为WARN或INFO
定义异常-日志级别映射规则
避免硬编码判断,建议用 Map<Class<? extends Throwable>, LogLevel> 统一管理:
- 键为异常类(支持继承匹配:用
isAssignableFrom判断是否属于某类或其子类) - 值可封装为枚举
LogLevel.ERROR / WARN / INFO,便于扩展和测试 - 例如:
ruleMap.put(SQLException.class, LogLevel.ERROR);ruleMap.put(ConstraintViolationException.class, LogLevel.WARN);
在环绕通知中动态打日志
在 @Around("execution(* com.example..*.*(..))") 切面中,捕获异常后不直接 log.error(),而是:
立即学习“Java免费学习笔记(深入)”;
- 调用一个私有方法
resolveLogLevel(Throwable ex),返回LogLevel - 根据返回值选择
log.error()、log.warn()或log.info() - 日志内容保留完整异常链(用
ex.printStackTrace(new PrintWriter(stringWriter))或org.apache.commons.lang3.exception.ExceptionUtils.getStackTrace(ex))
注意包装异常的常见陷阱
Spring 常将原始异常包装成 UndeclaredThrowableException、InvocationTargetException 等。若只看顶层异常,会漏判:
- 必须递归遍历,不能只看第一层
getCause() - 对
InvocationTargetException,应优先取getTargetException()而非getCause() - 对
ExecutionException,用getCause()获取实际执行异常


















