必须用log.error("描述信息", e)传异常对象,才能打印完整堆栈;若拼接字符串仅输出一行文字。需配置日志pattern含%ex/%xEx,禁用toString(),处理包装异常,统一用LoggerFactory,并在@ControllerAdvice和Thread.setDefaultUncaughtExceptionHandler中均传Throwable参数。

关键就一条:用 log.error("描述信息", e),且必须把异常对象 e 作为独立参数传进去,不能拼在字符串里。
为什么必须传异常对象,而不是只打 e.getMessage()
因为只有把 Throwable 实例作为第二个参数传给 log.error(),日志框架(如 Logback、Log4j2)才会自动解析并格式化完整堆栈——包括出错类、方法名、行号、以及所有嵌套的 caused by 链。如果只写 log.error("失败: " + e.getMessage()),日志里就只剩一行文字,没有堆栈,等于没记。
Java项目代码review工具。分析Git变更+完整调用链路上下文,推断业务需求,进行多维度评分和分类汇总,生成完整PRD文档。包含细粒度Java代码审查清单(Null安全、异常处理、Streams、并发、equals/hashCode、资源管理、API设计、性能、MyBatis/ORM、事务边界、SQL/DD...
确保堆栈能完整打印的实操要点
- 检查日志配置:Logback 中 pattern 里要含
%ex或%xEx(后者会递归展开所有 cause);Log4j2 要用%ex且开启includeLocation="true"才带行号 - 避免手动 toString():别写
log.error(e.toString())或log.error(Arrays.toString(e.getStackTrace())),这会破坏格式、可能死循环、还丢失上下文 - 处理包装异常:若 e 是 RuntimeException 包着 SQLException,建议用
ExceptionUtils.getRootCause(e)或e.getCause() != null ? e.getCause() : e再传入 log.error() - 统一用 LoggerFactory:始终通过
LoggerFactory.getLogger(YourClass.class)获取 logger,避免 new 实例导致 MDC 上下文丢失
不同场景都要覆盖到
@ControllerAdvice 处理 Web 请求异常时,在 @ExceptionHandler 方法里调用 log.error("msg", e);非 Web 场景(如定时任务、线程池)需设置 Thread.setDefaultUncaughtExceptionHandler,并在其回调中同样用 log.error("线程异常", t) 记录——两个地方都必须传 Throwable 参数,堆栈才不会丢。

















