日志是后端开发中常被忽视但极其关键的一环。它是系统运行状态的“黑匣子”,当系统出现异常、性能下降或用户反馈问题时,日志往往是我们排查的第一入口。
本文不讨论日志框架的用法(如 Logback、Log4j、Slf4j 等),而是从实际开发出发,分享如何构建一套清晰、稳定、可维护的日志体系,避免“日志堆满硬盘也找不到问题”的尴尬。
一、明确日志的用途
不同类型的日志服务不同目的。建议从三个层面思考日志结构:
- 业务日志:记录核心业务流程中的关键行为,如“订单已支付”、“用户登录失败”。
- 系统日志:记录服务状态,如启动信息、配置加载、服务健康检查等。
- 异常日志:记录所有运行时异常、接口调用失败、超时等非正常情况。
明确分类是后续格式、输出等级、采集策略的基础。
二、日志输出应具备结构化
日志不是写给人看的,是写给“人和系统”同时看的。
建议日志输出统一格式,避免杂乱字符串拼接。推荐方式:
{
"timestamp": "2025-06-20T12:34:56",
"level": "ERROR",
"thread": "main",
"logger": "com.example.service.UserService",
"message": "用户登录失败,用户名为空",
"traceId": "abc123xyz",
"context": {
"userId": 123,
"ip": "192.168.1.2"
}
}
优点:
- 日志分析平台(如 ELK)易于解析与聚合
- 支持 traceId 全链路追踪
- 可快速按字段过滤日志(如定位特定用户请求)
三、日志等级建议
不清楚日志等级该如何划分?参考这个标准:
日志级别 | 使用场景 |
---|---|
ERROR | 不可恢复的异常,需关注或告警 |
WARN | 可恢复但不符合预期的场景 |
INFO | 正常操作日志,如登录、操作成功 |
DEBUG | 调试用信息,开发或测试时开启 |
TRACE | 更底层的细粒度跟踪,一般不推荐使用 |
不要滥用 INFO
和 ERROR
,避免日志充斥无效内容或造成“狼来了”效应。
四、日志与 traceId
如果系统为微服务架构,traceId 是必备的。否则你无法追踪一个请求在多个服务间的流转过程。
做法:
- 接口入口(如网关或 controller)生成 traceId
- 使用日志 MDC(映射诊断上下文)机制自动注入 traceId 到日志中
- 传递 traceId 到下游服务(通常放在 header 中)
五、日志清理与滚动策略
不要等磁盘爆满才去关心日志清理。
- 本地日志建议按时间+大小切分(如每日一个文件,或 100MB 一分段)
- 使用 Logrotate、Logback 的滚动策略定期归档
- 保存时长根据业务情况设置(如最近 7 天)
生产建议接入集中式日志系统,如 ELK、Loki、SLS 等。
六、实践中的几点建议
- 日志内容中禁止打印用户敏感信息(密码、手机号等)
- 出错日志中一定要打印异常堆栈
- 每个入口请求建议记录 URI、参数、处理时间
- 不要在循环或高频方法中频繁打印日志
- 异步任务或定时任务应明确记录启动/异常信息
小结
日志并不是“写个 log.info() 就结束了”的事。清晰、稳定的日志体系需要团队制定规范、统一输出格式、分级管理、搭配采集工具。
日志是一种能力,越早重视,越能在系统出问题时抢回主动权。
如果你在搭建日志体系的过程中踩过坑,欢迎在评论区留言讨论。