Go 后端日志管理:结构化日志、轮转与磁盘告警实战
Go 后端日志管理:结构化日志、轮转与磁盘告警实战
先说结论
很多小服务器的第一次宕机不是被流量打垮的,而是被日志撑爆磁盘的。Go 后端默认把日志往标准输出一打就完事,部署时再用 >> app.log 2>&1 攒着,三个月后一个 20GB 的日志文件直接把根分区写满,服务连启动都启动不了。本文给出一个小团队够用的日志方案:结构化日志打底、按天轮转封顶、磁盘水位告警兜底,三步做完,日志就从隐患变成资产。
第一步:用结构化日志代替自由文本
log.Println("用户下单失败") 这种日志,出了问题只能靠人眼翻。结构化日志把每条记录写成固定字段,后续用 grep、jq 都能直接筛。
// 推荐用标准库 log/slog,Go 1.21 起内置,不用引第三方库
logger := slog.New(slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{
Level: slog.LevelInfo,
}))
logger.Info("order_created",
"order_id", "202611060001",
"user_id", 10086,
"amount", 58.5,
"cost_ms", 42,
)
输出是一行 JSON:{"time":"2026-11-06T06:00:00+08:00","level":"INFO","msg":"order_created","order_id":"202611060001","user_id":10086,"amount":58.5,"cost_ms":42}。字段固定后,查“某个用户的所有订单”就是一句 grep '"user_id":10086' app.log,查“慢接口”就是筛 cost_ms 大于 500 的行。记住两条纪律:日志里只记脱敏后的业务标识,绝不记密码、Token、完整手机号;错误日志要带上错误链,logger.Error("db query failed", "err", err) 比一句“查询失败”有用十倍。
第二步:日志轮转——按天切分、按量封顶
只打结构化日志还不够,文件会无限长。轮转的规则可以很简单:每天一个文件、单个文件超过 100MB 就切、只保留最近 14 天。
// 轻量做法:用 lumberjack 做轮转(也可以自己写一个按天重命名的 Writer)
// MaxSize 单位 MB,MaxBackups 保留份数,MaxAge 单位天,Compress 压缩旧文件
logFile := &lumberjack.Logger{
Filename: "/var/log/order/app.log",
MaxSize: 100,
MaxBackups: 14,
MaxAge: 14,
Compress: true,
}
logger := slog.New(slog.NewJSONHandler(logFile, nil))
配合 systemd 的服务,把标准输出也重定向到同一个目录,StandardOutput=append:/var/log/order/app.log,这样 panic 堆栈也不会丢。轮转之后要定期验证:ls -lh /var/log/order/ 看看文件是不是真的在切,du -sh /var/log/order/ 看看总量有没有失控。很多事故的根源就是“配了轮转但没验证”,配置文件写错了路径,日志照样往一个文件里涨。
第三步:磁盘水位告警——最后一道防线
轮转配好了也不是万无一失:突发流量会让日志量翻几倍,14 天的保留期在极端情况下照样能写满磁盘。所以要给磁盘加一道水位线:用量超过 80% 就告警,超过 90% 就自动清理最旧的日志。
# 每小时跑一次的简单巡检脚本,放进 crontab
#!/bin/bash
USAGE=$(df / | awk 'NR==2 {print $5}' | tr -d '%')
if [ "$USAGE" -ge 80 ]; then
echo "磁盘用量 ${USAGE}%,请检查 /var/log/order/" | \
mail -s "服务器磁盘告警" your-mail@example.com
fi
if [ "$USAGE" -ge 90 ]; then
find /var/log/order/ -name "*.log.gz" -mtime +3 -delete
fi
更彻底的做法是把日志级别也做成可调的:正常跑 Info,出问题时通过环境变量切到 Debug,用完再切回来。LOG_LEVEL=debug ./app 一行命令就能改,不用重新编译。线上长期开 Debug 等于给磁盘上刑,排查完务必切回 Info。
一个真实的教训
我自己的点餐小程序后端早期就是 nohup ./app >> app.log 2>&1 & 跑着,三个月没看过日志目录。某天早上服务起不来,查了半天才发现是磁盘 100%,连 systemd 写 PID 文件都失败。清理掉 18GB 的历史日志、补上轮转和 80% 告警之后,同样的机器又稳稳跑了半年。日志管理这件事,成本很低,收益是“半夜不用被叫醒”。
检查清单
- 日志是否结构化:每条记录都有固定字段,能用
grep/jq直接筛。 - 轮转是否生效:
ls能看到按天切分的文件,总量在预期内。 - 水位告警是否跑通:手动把阈值调低触发一次,确认告警真的能收到。
- 敏感信息是否脱敏:日志里搜一下自己的手机号和 Token,确认搜不到。
- 级别是否可调:不用重新编译就能在 Info 和 Debug 之间切换。
把这五条做完,日志就从“出事后才想起的东西”变成“出事前就帮你挡一道的防线”。
- 点赞
- 收藏
- 关注作者
评论(0)