Linux 日志太多,如何设计合理的日志策略
Linux服务器磁盘突然告警“/var/log 已满 95%”,然后服务卡死、进程挂掉、甚至系统重启都卡住——这种场景几乎每个做过线上服务的同学都踩过坑。日志是运维的眼睛,但眼睛太多也会把硬盘“撑瞎”。
本文系统聊聊:在Linux服务器上,如何设计一套合理、可扩展、不容易把磁盘撑爆的日志策略。从基础配置到高级架构,从单机到分布式,全流程覆盖。目标是:日志该有的都有,不该占的别占,排查问题时还能快速找到。全文干货为主,建议直接收藏,下次磁盘又满的时候直接对照执行。
一、为什么日志会“爆炸”?先搞清楚根因
Linux日志主要来源:
- 系统日志:rsyslog / systemd-journald(/var/log/messages、secure、kern.log 等)
- 服务日志:nginx、apache、mysql、redis、tomcat、java应用等
- 应用日志:业务代码自己打的(Spring Boot、Node.js、Python Flask/Django 等)
- 容器/编排日志:Docker daemon、Kubernetes kubelet/pod logs
爆炸常见原因:
- 日志级别太低(DEBUG/ALL)上线没改回INFO/WARN
- 高并发业务每秒打几千行日志
- 没有轮转(rotation)或轮转策略太宽松
- 没有压缩、没有清理旧日志
- journald 默认无限增长(尤其是老系统没配限制)
- 多实例/容器日志都写宿主机同一目录
设计日志策略的核心目标:可控体积 + 可检索 + 高可用 + 低成本
二、基础层:单机日志轮转与清理(logrotate + journald)
2.1 logrotate —— Linux 日志轮转的王牌工具
几乎所有主流发行版都预装了 logrotate,它通过 cron 每天/每周执行一次。
默认配置文件:/etc/logrotate.conf + /etc/logrotate.d/ 目录下每个服务的独立配置文件。
推荐的通用最佳实践配置模板(适用于大多数服务):
# 示例:/etc/logrotate.d/nginx
/var/log/nginx/*.log {
daily # 每天轮转(或 weekly / size 100M)
rotate 7 # 保留7份(一周)
missingok # 日志文件不存在不报错
dateext # 轮转文件带日期,如 access.log-20260204
dateformat -%Y%m%d # 日期格式
compress # 压缩旧日志(默认 gzip)
delaycompress # 延迟压缩(当前正在写的文件不压缩)
notifempty # 空文件不轮转
create 0640 www-data adm # 创建新日志文件时的权限和属主
sharedscripts # 多个文件共用 postrotate 脚本
postrotate
[ -f /var/run/nginx.pid ] && kill -USR1 `cat /var/run/nginx.pid`
endscript
}关键参数解释与推荐值:
- daily / weekly / monthly:中小型服务 daily,大日志服务 weekly
- rotate 4~12:合规要求留3~6个月则 rotate 90~180,普通业务7~30天够用
- size 50M / 100M:混合使用 size + time,例如 size 100M + daily,文件超100M且当天就轮转
- compress + compresscmd /usr/bin/xz + compressext .xz:用 xz 代替 gzip,压缩比更高(节省50%+空间)
- maxage 30:超过30天的无论多少份都删(防止长期积累)
- minage 1:防止当天轮转的文件立刻被删
针对高频写日志的服务(如nginx、业务jar)额外优化:
- 用 copytruncate(复制后截断原文件)代替 create + 信号重启,适合不能重启写文件的进程 但注意:可能丢失极少量日志(截断瞬间)
- 或让应用自己支持 reopen(如java -XX:+PrintGCDateStamps + 配置 logback reopen)
测试配置是否正确(强烈推荐上线前跑):
logrotate -d -f /etc/logrotate.d/your-service # debug 模式,模拟不实际执行
logrotate -v /etc/logrotate.d/your-service # verbose 执行一次2.2 systemd-journald 配置(现代系统必调)
CentOS 8+/Ubuntu 20.04+ 默认用 journald,很多日志直接进 /var/log/journal 而不是传统文件。
默认 journald 可能无限增长!必须限制。
编辑 /etc/systemd/journald.conf(或 drop-in /etc/systemd/journald.conf.d/override.conf):
[Journal]
Storage=persistent # 持久化到磁盘(默认 volatile 只存内存)
SystemMaxUse=4G # journal 总大小上限 4GB(推荐物理内存的10-20%)
SystemKeepFree=20% # 至少保留20%磁盘空间
SystemMaxFileSize=500M # 单个 journal 文件最大 500MB
MaxRetentionSec=30d # 最多保留30天(或 MaxFileSec=7d)
SyncIntervalSec=1m # 同步间隔,减少频繁写盘
RateLimitIntervalSec=30s
RateLimitBurst=1000 # 限流,防止日志风暴修改后重启服务:
systemctl restart systemd-journald查看当前使用:
journalctl --disk-usage查询日志:
journalctl -u nginx -S "2026-02-01" # 从某天开始
journalctl --since "1 hour ago" -p err # 最近1小时 error 级别三、中级层:日志分级 + 降噪 + 结构化
3.1 日志级别分环境管理
- 开发/测试:DEBUG / TRACE 全开
- 生产:INFO 或 WARN 起步,ERROR 必须有
- 监控/告警日志单独输出(不混业务日志)
Spring Boot 示例(logback-spring.xml):
<springProfile name="production">
<root level="INFO">
<appender-ref ref="ASYNC_FILE" />
</root>
</springProfile>3.2 避免日志风暴
- 加采样:比如每分钟只记录一次同类错误
- 用 MDC / traceId 代替重复打印上下文
- 敏感信息脱敏(身份证、手机号、密码等)
3.3 结构化日志(JSON)
现代最佳实践:所有应用输出 JSON 格式,便于后续 ELK / Loki 解析。
logback 示例:
<appender name="JSON" class="ch.qos.logback.core.rolling.RollingFileAppender">
<encoder class="net.logstash.logback.encoder.LogstashEncoder" />
...
</appender>输出示例:
{"@timestamp":"2026-02-04T18:10:00","level":"INFO","message":"处理订单成功","traceId":"abc123","orderId":456}四、高级层:集中式日志收集 + 存储
单机轮转只能治标,真正解决“日志太多”的是集中化 + 采样 + 对象存储。
主流方案对比(2026年主流):
| 方案 | 采集端轻量 | 存储成本 | 查询性能 | 推荐场景 |
|---|---|---|---|---|
| ELK Stack | Filebeat | 中等 | 高 | 中大型企业,自建 |
| Loki + Grafana | Promtail | 低 | 中高 | Kubernetes、云原生 |
| Graylog | Sidecar | 中等 | 高 | 需要权限控制、告警 |
| 云服务 | Agent | 按量 | 高 | 无运维团队、快速上线 |
推荐最小化自建方案(Loki + MinIO / S3):
- Promtail 采集 → Loki 存储(索引只存元数据,日志本体压缩存对象存储)
- 日志保留:热数据7天,冷数据90天(S3 Glacier)
- Grafana 做看板 + 告警
采集端轻量化:
- Filebeat / Promtail 只采集,不解析
- 关闭本地文件轮转,让收集器自己处理
五、监控与应急机制
必须有这些告警:
- 磁盘使用率 > 80%(/var/log 单独监控)
- journald 大小 > 2GB
- 单日志文件增长速度 > 100MB/h
- 错误日志速率 > 100条/分钟
应急自愈脚本示例(crontab 每5分钟跑):
#!/bin/bash
USED=$(df -h /var/log | awk 'NR==2 {print $5}' | tr -d '%')
if [ "$USED" -gt 90 ]; then
find /var/log -type f -name "*.gz" -mtime +14 -delete
journalctl --vacuum-time=7d
# 或者 kill 高日志进程(慎用)
fi结语
一套合理的日志策略应该满足:“够用、不浪费、可查、可追溯、可审计”。
推荐分层路径:
- 小型项目/单机:logrotate + journald 限制 + INFO级别
- 中型业务:结构化JSON + Filebeat → Elasticsearch / Loki
- 大型/分布式:Loki + 对象存储 + 采样 + 冷热分离 + 告警自愈
日志不是越多越好,而是越“有用”越好。把 DEBUG 留在本地,把 ERROR 送到大脑,把 TB 级的原始日志安心扔到冷存储。