关键在于用logger.error(String, Throwable)传入异常对象,而非拼接字符串或调用toString();SLF4J等框架会自动输出完整堆栈及嵌套异常链,需配合%ex等配置确保日志格式支持多行输出。

Java 中全局异常日志要记录完整的堆栈信息,关键在于捕获异常时**不吞掉原始异常对象**,而是用 logger.error(String, Throwable) 重载方法传入异常实例,让日志框架自动打印完整堆栈。
使用带 Throwable 参数的 logger.error()
这是最常见也最容易被忽略的一点。很多开发者习惯写:
logger.error("处理订单失败: " + e.getMessage()); // ❌ 只记了消息,无堆栈正确做法是:
logger.error("处理订单失败", e); // ✅ 完整堆栈会自动追加到日志末尾Logback、Log4j2 等主流框架都会在日志输出中自动附加 at xxx.xxx.xxx(...) 形式的完整调用链。
立即学习“Java免费学习笔记(深入)”;
Java项目代码review工具。分析Git变更+完整调用链路上下文,推断业务需求,进行多维度评分和分类汇总,生成完整PRD文档。包含细粒度Java代码审查清单(Null安全、异常处理、Streams、并发、equals/hashCode、资源管理、API设计、性能、MyBatis/ORM、事务边界、SQL/DD...
统一配置全局异常处理器(如 Spring Boot)
在 Web 应用中,推荐用 @ControllerAdvice + @ExceptionHandler 拦截未处理异常:
- 捕获
Exception或更具体的异常类型 - 日志记录必须传入异常对象,而非
e.toString()或e.getMessage() - 可额外记录请求路径、用户 ID、时间戳等上下文信息,便于排查
示例:
@ExceptionHandler(Exception.class)
public ResponseEntity<String> handleGlobalException(HttpServletRequest req, Exception e) {
logger.error("全局异常 | URI={} | Method={} | Cause={}",
req.getRequestURI(), req.getMethod(), e.getClass().getSimpleName(), e);
return ResponseEntity.status(500).body("服务异常");
}确保日志框架配置支持多行堆栈输出
有些日志配置(尤其是自定义 PatternLayout)可能截断或忽略换行,导致堆栈显示不全。检查你的 logback-spring.xml 或 log4j2.xml:
- Pattern 中不要用
%m单独输出消息(它不包含堆栈),应依赖%ex或%xEx显式输出异常 - 推荐使用
%d %-5level [%thread] %logger{36} - %msg%n%ex这类格式 - 确认控制台/文件 Appender 没有启用
maxFileSize或prudent=true导致日志写入异常中断
避免手动 toString() 或 printStackTrace()
以下写法都不可取:
-
e.printStackTrace();→ 输出到标准错误流,难聚合、无时间戳、不走日志系统 -
logger.error(e.toString());→ 仅输出异常类名+消息,无堆栈 -
logger.error(Arrays.toString(e.getStackTrace()));→ 格式混乱、丢失 cause 链、不兼容嵌套异常
交给日志框架处理,它能正确展开 Cause、Suppressed 和嵌套异常(如 ExecutionException 包裹的原始异常)。

















