生产环境必须用logger.error("msg", e)而非e.printStackTrace(),因后者仅输出到System.err且不可捕获,日志系统无法收集;System.out.println(e)只调用toString(),无堆栈信息。

直接用 e.printStackTrace() 最快,但生产环境必须改用 logger.error("msg", e) —— 否则堆栈会丢在控制台,日志系统根本收不到。
为什么 System.out.println(e) 只显示一行?
它只调用 Throwable.toString(),输出类似 java.lang.NullPointerException: null,不包含任何栈帧信息。调试时完全没用。
- 真正要的是完整调用链:从抛出点 → 方法调用路径 → 每一层的类名、方法名、行号
-
e.printStackTrace()把这些全打到System.err,肉眼可读,适合本地快速验证 - 但它的输出不可捕获、不可重定向、不走日志框架,线上等于“静默失败”
printStackTrace() 的三个重载怎么选?
本质是控制输出目标,不是功能差异:
-
e.printStackTrace()→ 默认输出到System.err -
e.printStackTrace(System.out)→ 强制输出到标准输出(某些容器或 IDE 控制台对err有特殊处理,可能刷屏或被过滤) -
e.printStackTrace(new PrintWriter(stringWriter))→ 写入内存字符串,用于记录、上报或拼装自定义错误响应(比如 REST API 返回带堆栈的 debug info,仅限开发环境)
Logback/Log4j 里正确记录异常的写法
必须把异常对象作为独立参数传给日志方法,不能拼在字符串里:
立即学习“Java免费学习笔记(深入)”;
logger.error("数据库查询失败,用户ID={}", userId, e); // ✅ 正确
logger.error("数据库查询失败,用户ID={},异常={}", userId, e.toString()); // ❌ 堆栈丢失
- 日志框架识别到第三个参数是
Throwable,会自动附加完整堆栈到日志事件中 - 如果用
String.valueOf(e)或e.getMessage()拼接,只剩异常消息,调用链彻底消失 - SLF4J 绑定 Logback 时,堆栈默认按行拆分,可被 ELK 或 Loki 正确解析为多行日志
IDE 调试时别依赖控制台堆栈
IntelliJ/Eclipse 的断点调试器能直接展开 e 变量,看到 stackTrace 字段里的 StackTraceElement[] 数组——比控制台输出更干净,还能点击跳转到源码行。
- 遇到
Caused by:嵌套异常时,控制台输出容易看串行,而调试器里可逐层展开cause - 如果异常被多次包装(如 Spring 的
RuntimeException包裹SQLException),控制台输出可能截断深层原因,调试器里能一直点到底 - 注意:有些框架(如 Feign)会吃掉原始异常,只抛出新异常并设
cause,不手动展开cause就永远看不到根因
最常被忽略的一点:自定义异常类如果重写了 printStackTrace() 却没调用 super.printStackTrace(),或者覆盖了 fillInStackTrace() 但返回了空数组,堆栈就直接为空——这种异常抛出来,连 e.printStackTrace() 都打不出任何调用路径。











