日志管理:journald、logrotate 与日志该放哪
日志是排错时唯一的线索来源,但它也是把磁盘写满的头号嫌疑。这篇讲清楚日志往哪去、怎么看、怎么管。
一、两套日志系统
现代 Linux 有两套并存的日志:
| 系统 | 位置 | 管什么 |
|---|---|---|
| systemd-journald | 二进制,/var/log/journal | 所有服务的 stdout/stderr + 结构化字段 |
| rsyslog | 文本,/var/log/*.log | 传统 syslog,按 facility 分文件 |
简单理解:用 systemd 跑的服务看 journalctl,Nginx 这类自己写文件的看 /var/log/nginx/。
# journald 是否持久化(默认有些发行版只存内存)
ls /var/log/journal
# 目录不存在说明只存内存,重启就没了
# 打开持久化
sudo mkdir -p /var/log/journal
sudo systemd-tmpfiles --create --prefix /var/log/journal
sudo systemctl restart systemd-journald二、journalctl 高频用法
# 看某个服务
journalctl -u nginx
journalctl -u nginx -f # 实时跟随
journalctl -u nginx -n 100 --no-pager # 最近 100 行,不分页
# 按时间
journalctl --since "2026-02-19 10:00" --until "11:00"
journalctl --since "30 min ago"
journalctl --since today
journalctl --since yesterday --until today
# 按优先级
journalctl -p err -b # 本次启动的所有错误
journalctl -p warning --since today
# 级别:emerg(0) alert crit err warning notice info debug
# 按启动次数
journalctl -b # 本次启动
journalctl -b -1 # 上一次启动(排查崩溃必备)
journalctl --list-boots
# 按进程/用户
journalctl _PID=1234
journalctl _UID=1000
# 输出格式
journalctl -u nginx -o json-pretty
journalctl -u nginx -o cat # 只要正文
# 反向显示(最新的在前)
journalctl -u nginx -r -n 50三、控制 journald 的体积
journalctl --disk-usage # 看占了多少
# 按大小清理
sudo journalctl --vacuum-size=500M
# 按时间清理
sudo journalctl --vacuum-time=14d
# 按文件数清理
sudo journalctl --vacuum-files=5永久配置 /etc/systemd/journald.conf:
[Journal]
SystemMaxUse=500M # 最大占用
SystemKeepFree=1G # 至少给磁盘留这么多
MaxRetentionSec=1month # 最多保留一个月
Compress=yessudo systemctl restart systemd-journald四、logrotate:管住文本日志
Nginx 的日志是 rsyslog 之外的独立文件,由 logrotate 定期轮转。
cat /etc/logrotate.d/nginx/var/log/nginx/*.log {
daily # 每天轮转
rotate 14 # 保留 14 份
compress # 压缩(.gz)
delaycompress # 延迟一轮再压(避免程序还握着文件)
missingok # 文件不存在不报错
notifempty # 空文件不轮转
create 0640 www-data adm
sharedscripts # 多个文件只执行一次脚本
postrotate
[ -f /run/nginx.pid ] && kill -USR1 $(cat /run/nginx.pid)
endscript
}关键参数:
| 参数 | 作用 |
|---|---|
daily/weekly/monthly | 轮转周期 |
rotate N | 保留几份 |
size 100M | 达到大小就轮转(可与时间并存) |
compress | gzip 压缩 |
copytruncate | 复制后清空原文件(程序不支持 reopen 时用) |
postrotate | 轮转后执行的命令 |
调试与验证:
sudo logrotate -d /etc/logrotate.d/nginx # 调试模式,只显示会做什么
sudo logrotate -f /etc/logrotate.d/nginx # 强制执行一次
cat /var/lib/logrotate/status # 上次轮转时间五、自己写的日志也要轮转
备份脚本、应用日志很容易被忘掉。加一份配置:
# /etc/logrotate.d/baize
/var/log/baize/*.log {
weekly
rotate 8
compress
missingok
notifempty
create 0640 baize baize
}六、日志里该记什么
一条好日志至少要有:时间、级别、谁、发生了什么。
# 一个够用的日志函数
log() { echo "[$(date '+%F %T')] [$${1:-INFO}] $*" >> /var/log/baize/app.log; }
log INFO "备份开始"
log ERROR "数据库连接失败"几个原则:
- 别记密码、token、身份证号(日志泄露是最常见的数据事故);
- 错误日志要带上下文(哪个用户、哪个 ID),否则没法查;
- 别把日志打到 stdout 又同时写文件,会重复;
- 结构化(JSON)日志便于后续分析,但小项目没必要。
七、看日志的实用组合
# 统计访问日志里的 TOP IP
awk '{print $1}' /var/log/nginx/access.log | sort | uniq -c | sort -rn | head -20
# 找 5xx 请求
awk '$9 >= 500' /var/log/nginx/access.log | head -20
# 找耗时最长的请求(最后一列是响应时间)
awk '{print $NF, $7}' /var/log/nginx/access.log | sort -rn | head -10
# 某个时间段的日志
sed -n '/19/Feb/2026:10:00/,/19/Feb/2026:11:00/p' /var/log/nginx/access.log
# 实时看错误(只看新增)
tail -f /var/log/nginx/error.log八、保留策略建议
| 类型 | 保留期 | 理由 |
|---|---|---|
| 访问日志 | 7~30 天 | 量大,够排查就行 |
| 错误日志 | 30~90 天 | 体积小,价值高 |
| 安全日志(auth.log) | 90 天以上 | 审计需要 |
| 应用业务日志 | 按需 |
日志策略就两条:磁盘别被写满,出事时查得到。
