# 构建可维护的日志体系:一个普通开发者的实践经验
日志是后端开发中常被忽视但极其关键的一环。它是系统运行状态的“黑匣子”,当系统出现异常、性能下降或用户反馈问题时,日志往往是我们排查的第一入口。
本文不讨论日志框架的用法(如 Logback、Log4j、Slf4j 等),而是从实际开发出发,分享如何构建一套**清晰、稳定、可维护**的日志体系,避免“日志堆满硬盘也找不到问题”的尴尬。
## 一、明确日志的用途
不同类型的日志服务不同目的。建议从三个层面思考日志结构:
- **业务日志**:记录核心业务流程中的关键行为,如“订单已支付”、“用户登录失败”。
- **系统日志**:记录服务状态,如启动信息、配置加载、服务健康检查等。
- **异常日志**:记录所有运行时异常、接口调用失败、超时等非正常情况。
明确分类是后续格式、输出等级、采集策略的基础。
---
## 二、日志输出应具备结构化
日志不是写给人看的,是写给“人和系统”同时看的。
建议日志输出统一格式,避免杂乱字符串拼接。推荐方式:
```json
{
"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() 就结束了”的事。清晰、稳定的日志体系需要团队制定规范、统一输出格式、分级管理、搭配采集工具。
日志是一种能力,越早重视,越能在系统出问题时抢回主动权。
如果你在搭建日志体系的过程中踩过坑,欢迎在评论区留言讨论。