Java异常日志需结构化输出:用logger.error("msg", e)保留堆栈,显式携带订单号等业务上下文,脱敏敏感信息,区分ERROR/WARN级别,并采用JSON结构化日志便于ELK检索。

Java 中规范异常日志输出,核心是让日志具备可读性、可追溯性和可排查性。关键不是只打印 e.toString() 或空泛的“出错了”,而是结构化呈现:谁(类/方法)、何时(时间戳)、在哪(行号)、发生了什么(异常类型+消息)、为什么(完整堆栈+上下文参数)。
用 logger.error() 记录异常,且传入 Throwable 对象
这是最基础也最容易被忽略的一点。不要手动拼接堆栈或只打异常消息。
- ✅ 正确写法:logger.error("订单支付失败,订单号: {}", orderNo, e); —— SLF4J / Logback 会自动将
e的完整堆栈写入日志末尾 - ❌ 错误写法:logger.error("订单支付失败,订单号: {},异常: {}", orderNo, e.getMessage()); —— 丢失堆栈,无法定位根因
- ❌ 错误写法:logger.error("订单支付失败", e); —— 缺少关键业务上下文(如订单号、用户ID),查问题时要反复翻日志关联
在日志中显式带上关键业务上下文
异常本身是技术事实,但“为什么在这个场景发生”依赖业务参数。仅靠堆栈无法还原现场。
Java项目代码review工具。分析Git变更+完整调用链路上下文,推断业务需求,进行多维度评分和分类汇总,生成完整PRD文档。包含细粒度Java代码审查清单(Null安全、异常处理、Streams、并发、equals/hashCode、资源管理、API设计、性能、MyBatis/ORM、事务边界、SQL/DD...
- 记录能唯一标识操作的字段:订单号、用户ID、请求ID(traceId)、接口路径等
- 避免记录敏感信息:密码、身份证号、银行卡号等需脱敏或禁止落盘
- 示例:logger.error("调用短信网关超时,手机号: {}, templateId: {}, 耗时: {}ms", maskPhone(phone), templateId, costMs, e);
区分 error 和 warn,避免日志污染
不是所有异常都该记为 ERROR 级别。日志级别混乱会让真正的问题被淹没。
立即学习“Java免费学习笔记(深入)”;
- ERROR:系统级故障、数据不一致、外部服务不可用、无法降级的失败(如扣款成功但通知失败)
- WARN:预期外但可恢复的情况,如缓存穿透后查库成功、第三方接口返回非200但业务可兜底(如默认头像)
- 对可预期的业务异常(如“余额不足”),通常不打 ERROR,而是在业务逻辑中处理并返回友好提示;若需审计,可用 INFO + 结构化字段
配置日志框架支持结构化与可检索
格式再规范,如果日志是纯文本且无字段分隔,排查效率仍低。建议启用结构化日志能力:
- Logback + logstash-logback-encoder:输出 JSON 格式,含 timestamp、level、class、method、line、traceId、orderNo、exception 等字段
- 确保 pattern 中包含 %throwable 或 encoder 显式处理异常对象,避免堆栈被截断
- 在日志平台(如 ELK、阿里SLS)中按 traceId 聚合全链路日志,结合异常堆栈快速下钻

















