日志去哪了:journalctl / syslog / logrotate 一次说清
这是 Linux 系列的第 22 篇。上一篇讲怎么看机器状态——这一篇专题讲"机器发生过什么"——日志。
0. 三条日志路线,新手要先认全
Linux 上日志去哪儿,本质是看应用怎么写的:
应用 A:写 stdout/stderr → systemd → journald → /var/log/journal/
应用 B:调 syslog() 函数 → /dev/log → rsyslog/syslog-ng → /var/log/*.log
应用 C:自己 open 文件 write → /var/log/<app>/ 或别的位置
现代 systemd 系统 + 主流软件 → 路线 A(journald)最常见。 NGINX / 数据库这类老软件 → 多半自己写文件(路线 C)。 邮件 / DHCP / 内核老组件 → syslog(路线 B)。
下面分别讲。
1. 路线 A:systemd journal(最现代)
systemd 的服务 stdout/stderr 默认进 journald。已经在 17 systemd-services 讲过 journalctl,这里补几个进阶 trick。
最高频 5 条命令
# 跟某服务的日志
$ journalctl -u nginx -f
$ journalctl -u nginx --since '10 min ago'
$ journalctl -u nginx -p err # 错误及以上
# 系统所有错误(救火第一条)
$ journalctl -p err -b # 本次启动以来的错误
# 内核日志
$ journalctl -k
# 按某 PID 过滤
$ journalctl _PID=12345
# 按可执行文件过滤
$ journalctl /usr/bin/python3
journal 输出格式
# JSON 格式(适合脚本)
$ journalctl -u nginx -o json
# 紧凑 JSON
$ journalctl -u nginx -o json-pretty
# 仅纯消息(无元数据)
$ journalctl -u nginx -o cat
# verbose 显示所有字段
$ journalctl -u nginx -o verbose
verbose 输出会让你看到 journal 收集了多少额外字段(_PID / _UID / _COMM / _BOOT_ID / _SYSTEMD_UNIT / _HOSTNAME...)——这些字段都能用 key=value 过滤:
$ journalctl _SYSTEMD_UNIT=nginx.service _PID=12345 PRIORITY=3
一个杀手 trick:跨多条件 --grep
$ journalctl -u nginx --grep "5\d\d" -p notice # 含 5xx 状态码的 nginx notice
加 SyslogIdentifier 给自己应用打 tag
写 .service 文件时加:
[Service]
SyslogIdentifier=myapp
之后:
$ journalctl -t myapp -f
不用记 unit 名。
2. 路线 B:传统 syslog
syslog 是 Unix 1980 年代的日志协议。今天的实现一般是 rsyslog 或 syslog-ng。
# 看 syslog 的"主入口"
$ ls /var/log/
syslog messages auth.log kern.log mail.log
syslog.1.gz messages.1 auth.log.1 ...
这些是应用调 syslog() 函数后,rsyslog 根据规则分流到的文件。
syslog 的 facility(设施)+ severity(级别)
应用调 syslog 时带两个参数:
| facility | 默认日志去哪 |
|---|---|
| kern | /var/log/kern.log |
| auth, authpriv | /var/log/auth.log |
| /var/log/mail.log | |
| cron | /var/log/cron.log |
| daemon | /var/log/daemon.log |
| user | /var/log/user.log |
| local0..local7 | 自定义用 |
| severity | 含义 |
|---|---|
| 0 emerg | 系统不可用 |
| 1 alert | 立刻处理 |
| 2 crit | 严重 |
| 3 err | 错误 |
| 4 warning | 警告 |
| 5 notice | 通知 |
| 6 info | 信息 |
| 7 debug | 调试 |
rsyslog 配置
# 主配置
$ sudo cat /etc/rsyslog.conf
$ ls /etc/rsyslog.d/
典型规则一行:
mail.* /var/log/mail.log
authpriv.* /var/log/auth.log
*.err /var/log/err.log
local0.* /var/log/myapp.log
读法:<facility>.<severity-及以上> <文件路径>。
现代用法
systemd-journald 在大多发行版上同时也会把日志转发给 rsyslog——所以两边都有。/var/log/syslog 和 journalctl 显示的内容很大部分重叠。
新业务直接走 journald 就行——文本日志慢慢退场。但老软件、远程集中化日志还是 syslog 的天下。
远程集中化(一句话)
rsyslog / syslog-ng 都支持把日志转发到中央 log server。常见架构:
N 台业务机器 → rsyslog → 中央日志机 → Elasticsearch/Loki → Kibana/Grafana
云上更常用 vector / fluent-bit + 对接 SLS / CloudWatch / Loki。这是单独的话题。
3. 路线 C:应用自己的日志文件
NGINX、PostgreSQL、Redis、Java 应用——很多软件自己 open 文件写,不走 journald 也不走 syslog。
# 典型路径
/var/log/nginx/access.log
/var/log/nginx/error.log
/var/log/postgresql/postgresql-15-main.log
/var/log/redis/redis-server.log
~/app/logs/2026-05-25.log
怎么找?
# 方法 1:看进程打开了哪些文件
$ sudo lsof -p $(pidof nginx | awk '{print $1}') | grep log
# 方法 2:看应用配置
$ cat /etc/nginx/nginx.conf | grep -i log
# 方法 3:去 /var/log 翻
$ sudo find /var/log -type f -name '*.log' | head
tail -f + 几个进阶
$ tail -f /var/log/nginx/access.log
# 多文件同时跟
$ tail -f /var/log/nginx/{access,error}.log
$ tail -f /var/log/{nginx,redis}/*.log
# 跟着文件 rotate(推荐!tail 默认 rotate 后跟错)
$ tail -F /var/log/nginx/access.log # -F 大写:跟踪文件名,rotate 后切到新的
# 只看新产生的,含正则
$ tail -F /var/log/nginx/error.log | grep --line-buffered -E '500|502|timeout'
--line-buffered 让 grep 在管道里也按行刷新——否则会卡 4KB 缓冲。
multitail:多文件并排
$ sudo apt install multitail
$ multitail -i /var/log/nginx/access.log -i /var/log/nginx/error.log
多窗口排版,加颜色。比 tmux 分屏轻量。
lnav:日志浏览器(强烈推荐)
$ sudo apt install lnav
$ lnav /var/log/syslog
lnav 自动识别 nginx / apache / syslog / json 等几十种格式,提供:
- 时间轴 + 状态码热度图
- 语法高亮
- SQL 查询模式:
;SELECT log_msg FROM access_log WHERE sc_status >= 500 - 实时跟踪 + 过滤
学会 lnav,日志分析效率翻倍。
4. logrotate:防止日志撑爆磁盘
雪崩第二大杀手(第一是内存 OOM)就是日志不轮转——一两个月磁盘塞满,应用写不动崩溃。
看现有规则
$ ls /etc/logrotate.d/
nginx postgresql-common rsyslog apt cron ...
$ cat /etc/logrotate.d/nginx
/var/log/nginx/*.log {
daily
missingok
rotate 14
compress
delaycompress
notifempty
create 0640 www-data adm
sharedscripts
postrotate
if [ -f /var/run/nginx.pid ]; then
kill -USR1 `cat /var/run/nginx.pid`
fi
endscript
}
读法:
| 字段 | 含义 |
|---|---|
daily |
每天 rotate(也可 weekly / monthly / size 100M) |
rotate 14 |
保留 14 份历史 |
compress |
旧的压缩成 .gz |
delaycompress |
上一份不压,再前面才压(防止应用还在写新文件名时被压) |
create 0640 user grp |
新文件用什么权限 |
postrotate ... endscript |
rotate 后跑的 shell(典型:发 SIGUSR1 让应用重开 fd) |
notifempty |
空文件不 rotate |
missingok |
文件不存在也不报错 |
给自己应用加一份 logrotate 配置
新建 /etc/logrotate.d/myapp:
/var/log/myapp/*.log {
daily
rotate 7
compress
delaycompress
notifempty
create 0644 myapp myapp
postrotate
systemctl reload myapp.service > /dev/null 2>&1 || true
endscript
}
每天 rotate 一次,保留 7 份,rotate 后通知应用 reopen 文件。
立刻测一遍(不等明天)
$ sudo logrotate -d /etc/logrotate.d/myapp # -d debug 干跑
$ sudo logrotate -f /etc/logrotate.d/myapp # -f force 立刻执行
postrotate 必须做的事
应用持有 log 文件的 fd 不放——logrotate mv 旧文件后,应用还在写已被改名的文件,新 access.log 还是空的。
解决方案 3 选 1:
- 发 SIGUSR1(nginx / apache / haproxy 模式):触发应用重新 open
- systemctl reload:让 systemd 帮你(适合现代应用)
- copytruncate:logrotate 复制完旧文件清空原文件(不改 inode),应用不察觉
/var/log/nginx/*.log {
daily
rotate 14
compress
copytruncate # ← 不用通知应用
}
但 copytruncate 有短暂窗口会丢日志(cp 期间的 write)——能用 SIGUSR1 还是优先 SIGUSR1。
5. journald 怎么不爆
/var/log/journal/ 不轮转的话也会涨。journald 自己管:
# 看当前占多少
$ journalctl --disk-usage
Archived and active journals take up 4.5G in the file system.
# 限制大小
$ sudo journalctl --vacuum-size=500M
# 限制时间
$ sudo journalctl --vacuum-time=14d
或者改配置 /etc/systemd/journald.conf:
[Journal]
SystemMaxUse=500M
SystemKeepFree=1G
MaxRetentionSec=2week
ForwardToSyslog=no # 不转发给 syslog 节省 IO
改完:
$ sudo systemctl restart systemd-journald
6. 集中日志(生产建议)
单机看日志的极限——几台还行,几十台就废。生产环境一定要集中化。
3 种主流方案:
A. ELK / Elastic Stack
应用 → filebeat → logstash → Elasticsearch → Kibana
老牌,功能强,资源吃得多(ES 至少 4G 内存起)。
B. Loki + Grafana(推荐中小规模)
应用 → promtail / vector → Loki → Grafana
Loki 像 "Prometheus for logs",只 index 标签不 index 内容——便宜很多。
C. 云厂商托管
阿里云 SLS、AWS CloudWatch Logs、GCP Cloud Logging。省事但贵 + lock-in。
中小规模(< 10 台机器、< 100GB 日志/天)→ Loki + Grafana 是甜点。
7. 日志分析常见 1 行
# nginx access log 出现最多的 IP
$ awk '{print $1}' /var/log/nginx/access.log | sort | uniq -c | sort -rn | head
# 状态码分布
$ awk '{print $9}' /var/log/nginx/access.log | sort | uniq -c | sort -rn
# 慢请求(假设第 10 列是 ms)
$ awk '$10 > 1000 {print $7, $10}' /var/log/nginx/access.log | sort -k2 -rn | head
# auth.log 暴力破解尝试
$ grep 'Failed password' /var/log/auth.log \
| awk '{print $11}' | sort | uniq -c | sort -rn | head
# journald 里 systemd 失败的服务
$ journalctl -p err -u systemd | tail -30
# 找内核 OOM kill
$ dmesg -T | grep -E 'oom-killer|killed process'
参考 07 text-pipes 那一篇组合大法。
8. 排查清单:应用挂了,5 分钟内定位
# 1. 服务跑没跑
$ systemctl status myapp.service
# 2. systemd 自己的视角
$ journalctl -u myapp.service -n 50 --no-pager
# 3. 应用自己的日志
$ ls -lhrt /var/log/myapp/ # 最新的在最下
$ tail -100 /var/log/myapp/error.log
# 4. 内核 / 硬件视角
$ dmesg -T | tail -50
$ journalctl -k --since '10 min ago'
# 5. 跟应用相关的最近所有日志
$ journalctl --since '30 min ago' | grep -i myapp
# 6. 是不是磁盘满 / inode 满
$ df -h
$ df -i
# 7. 是不是被 OOM 杀
$ dmesg -T | grep -iE 'killed process'
按这条清单走 → 95% 的"挂了"都能 5 分钟定位。
9. 现在做一件事
# 1. 看你系统现在日志总占用
$ sudo du -sh /var/log
$ journalctl --disk-usage
# 2. 找过去 24 小时的所有错误
$ journalctl -p err --since '24 hours ago' | tail -50
# 3. 看你机器有哪些 logrotate 规则
$ ls /etc/logrotate.d/
# 4. 测试 logrotate 配置不会跑挂
$ sudo logrotate -d /etc/logrotate.conf 2>&1 | head -30
# 5. 装个 lnav,体验下现代日志浏览器
$ sudo apt install lnav
$ lnav /var/log/syslog
日志能力是事后复盘的基本功——比看监控指标更细。
下一篇:kernel-tuning——
/proc/sys / sysctl / cgroup怎么调内核行为,常见 net / vm / fs 参数,让一个 1G 小机器的并发上限翻几倍。
$ ls related/
-
systemd 全攻略:systemctl / journalctl / .service 文件 2026-06-30
Linux 系列第 17 篇。开机自启服务、看日志、定时任务、依赖管理——这些以前要拼接 init 脚本 + crontab + rsyslog 的活,现代 Linux 都让 systemd 一个工具搞定。这一篇拆 systemctl 怎么用、写一个 .service 文件、看 journalctl、配 timer。
-
SSH 不只是登录:tunnel / agent / 跳板机 / config 模板 2026-07-03
Linux 系列第 20 篇。SSH 不仅是"远程登录"——它能转发端口(本地 / 远程 / 动态 SOCKS)、托管密钥、跳板穿透多层内网、批量 push 文件。这一篇把这些高级用法拆开,最后给一份 `~/.ssh/config` 让你 5 倍效率地管理几十台机器。
-
Linux 防火墙全栈:iptables / nftables / ufw / firewalld 几层 2026-07-02
Linux 系列第 19 篇。新手第一次看到 `iptables -nvL` 输出几百行就懵——而且还有 nftables / ufw / firewalld / Docker 加的规则一锅端。这一篇拆 netfilter 的核心 5 个钩子点、iptables → nftables 的迁移现状、ufw 和 firewalld 各自适用谁、Docker 怎么搅动这一锅。