必须用 logger.error(String, Throwable) 传异常对象,否则丢失堆栈;日志配置需含 %ex 或 %xEx 才输出堆栈;资源关闭异常不可吞没;ERROR 仅用于影响稳定性的异常,校验类失败用 WARN。

捕获异常时必须用 logger.error(String, Throwable) 而非拼接字符串
直接 logger.error("failed: " + e.getMessage(), e) 看似合理,但会丢失堆栈根源——日志框架只在传入 Throwable 第二参数时才记录完整堆栈。若写成 logger.error("failed: " + e),e.toString() 会被调用,仅输出类名+消息,堆栈彻底消失。
正确做法是始终把异常对象作为最后一个参数传入:
try {
doSomething();
} catch (IOException e) {
logger.error("File operation failed", e); // ✅ 堆栈完整
}常见错误场景:在 catch 块里先做业务补偿逻辑(如回滚、重试),最后才打日志,结果补偿过程又抛新异常,掩盖了原始异常;应优先记录原始异常,再处理补偿。
logback-spring.xml 中需启用 %ex 或 %xEx 才能输出异常堆栈
即使代码中传了 Throwable,如果日志配置的 pattern 没包含异常字段,控制台或文件里仍只显示消息行,不显示堆栈。默认的 %d %p %c - %m%n 就不包含异常。
立即学习“Java免费学习笔记(深入)”;
必须显式加入:
-
%ex:标准异常全堆栈(含 cause 链) -
%xEx:带 MDC 上下文的扩展异常(推荐用于微服务追踪)
示例配置片段:
%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n%ex
注意:%throwable 是别名,但部分旧版 logback 不识别,优先用 %ex。
不要在 finally 或 try-with-resources 中吞掉 close() 异常
资源关闭失败(如 SocketException、SQLException)本身是重要故障信号,但很多人习惯这样写:
try (BufferedReader r = new BufferedReader(...)) {
return r.readLine();
} catch (IOException e) {
logger.error("read failed", e);
throw new ServiceException(e);
}这段代码看似没问题,但 BufferedReader.close() 若抛异常(比如网络连接已断),会被静默丢弃——JVM 只保留 try 块主异常,close() 异常被压制(suppressed),除非你主动检查。
正确做法:
- 用
try-catch显式包裹close()并记录 - 或升级到 Java 7+ 后,用
try-with-resources+ 检查getSuppressed()(仅调试必要) - 更实用的是:对关键资源(如数据库连接、HTTP 客户端),封装关闭逻辑并统一打 error 日志
区分 logger.error 和 logger.warn 的使用边界
不是所有异常都该记为 ERROR 级别。日志级别混乱会导致告警泛滥或漏报。
判断依据看是否影响当前请求完整性与系统稳定性:
-
NullPointerException、SQLException(连接池耗尽)、HttpClientTimeoutException→ 必须error -
NumberFormatException(来自用户输入校验失败)、IllegalArgumentException(参数非法)→ 多数情况应warn,避免污染 error 日志流 - 第三方 SDK 返回的“业务失败”(如微信支付返回
ORDERPAID)不是异常,不应打 error,也不该抛RuntimeException
一个容易被忽略的点:Spring 的 @ExceptionHandler 方法里,如果统一捕获 Exception,需根据具体子类型分支决定日志级别,不能一概 logger.error。










