日志系统基础:日志级别、格式规范与切割归档
日志这东西,说白了就是系统的「黑匣子」。
我刚入行那会儿,觉得日志就是随便打印点东西,能跑就行。直到有一次线上故障,排查了整整一个通宵,最后发现是日志级别设错了,关键信息全被淹没了。嗯,从那以后,我对日志系统再也不敢马虎了。
一、日志级别:你的「信号灯」系统
日志级别就像交通信号灯。不同颜色代表不同紧急程度。我习惯把日志级别分成五档,每一档都有明确的用途。
| 级别 | 含义 | 使用场景 |
|---|---|---|
| DEBUG | 调试信息 | 开发环境、问题复现时开启 |
| INFO | 常规信息 | 系统正常运行的状态记录 |
| WARN | 警告 | 潜在问题,不影响当前运行 |
| ERROR | 错误 | 功能异常,需要人工介入 |
| FATAL | 致命错误 | 系统即将崩溃,立即处理 |
核心原则:生产环境只开 INFO 及以上级别。DEBUG 日志量太大,会拖垮磁盘 IO。
我在项目中遇到过一件事。有个同事把关键业务日志全打在 DEBUG 级别。上线后一切正常,直到某天出故障了,翻日志发现啥都没有。为什么?因为生产环境默认只打 INFO。他当时就傻眼了。
所以我的建议是:ERROR 级别只记录「需要人处理」的问题。那些能自动恢复的异常,用 WARN 就够了。
二、日志格式规范:别让排查变成猜谜
日志格式乱,排查起来就像在垃圾堆里找针。我个人习惯用这套标准格式:
[时间] [线程] [级别] [类名] [请求ID] - 日志内容
举个例子:
[2024-01-15 14:30:22.123] [http-nio-8080-exec-3] [ERROR] [com.example.OrderService] [req-abc123] - 订单创建失败,订单号:ORD20240115001
这里面每个字段都有讲究:
- 时间戳:精确到毫秒。我见过有人只精确到秒,结果同一秒内几十条日志,根本分不清先后顺序。
- 线程名:排查并发问题时的救命稻草。
- 请求ID:这是我最看重的字段。一次用户请求可能经过多个服务,没有请求ID,你根本串不起来。
小技巧:请求ID可以在网关层生成,用 MDC(Mapped Diagnostic Context)传递。这样整个调用链的日志都能关联起来。
我曾经排查过一个慢查询问题。日志里只有「查询超时」四个字,连哪个 SQL、哪个参数都不知道。后来我强制团队在日志里加上关键参数,排查效率提升了不止一倍。
三、日志切割与归档:别让磁盘爆了
日志如果不切割,后果很严重。我见过一个案例:某系统一天产生 50GB 日志,三天没管,磁盘直接写满,服务全挂了。
常用的切割策略有三种:
- 按时间切割:每小时或每天切一个文件。适合流量平稳的系统。
- 按大小切割:比如每个文件 100MB,满了就切。适合流量波动大的场景。
- 混合策略:时间和大小双重限制,哪个先到就切。这是最稳妥的做法。
以 Logback 为例,配置很简单:
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>/var/log/myapp/app.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>/var/log/myapp/app.%d{yyyy-MM-dd}.%i.log</fileNamePattern>
<maxHistory>30</maxHistory>
<totalSizeCap>10GB</totalSizeCap>
</rollingPolicy>
</appender>
这里有个坑要注意:maxHistory 和 totalSizeCap 要配合使用。光设保留天数,不设总大小,万一某天日志量暴增,磁盘还是会爆。
警告:归档后的日志要定期清理或转存到对象存储。我见过有人保留了三年的日志,结果每次查日志都卡得要死。建议线上保留 7-30 天,长期归档放到低成本存储上。
四、知识体系总览
下面这张图是我梳理的日志系统核心逻辑。你看一眼就能明白各个模块之间的关系。
你看,日志级别、格式规范、切割归档这三件事是环环相扣的。级别设错了,格式再漂亮也没用;格式乱了,切割再勤快也白搭。
最后说一句:日志系统不是写完了就完事了。我建议每季度做一次日志审计,看看有没有漏打的、打错的、打太多的。毕竟,日志是你排查问题的第一道防线,这道防线要是垮了,后面再牛的技术也救不了你。