Linux 日志:journalctl、/var/log、rsyslog 与 logrotate
在现代 Linux 发行版中,日志由 journald 收集,并通过 journalctl 读取:journalctl -u nginx 显示某个服务的记录,-f 实时跟踪,--since 和 -p 按时间和重要程度过滤。文本日志保存在 /var/log 中,由 rsyslog 或应用本身写入,logrotate 定期压缩并删除旧文件,防止磁盘被占满。
服务在夜里崩溃时,答案几乎总在日志里,关键是知道去哪里找。在现代发行版(Ubuntu、Debian、RHEL、Fedora、Arch)中,日志由 journald 服务收集,用 journalctl 命令读取。同时,许多程序会把普通文本文件写入 /var/log,其大小则由 logrotate 控制。下面介绍如何在实践中使用 journalctl 查看 Linux 日志,其他日志保存在哪里,以及轮转时如何不丢失重要信息。
Linux 日志保存在哪里
日志有三个来源,把它们当作一张地图来记会很有帮助:
- journald — systemd 的二进制日志。它接收所有服务的 stdout 和 stderr、内核消息以及 syslog。保存在
/var/log/journal/(持久保存)或/run/log/journal/(直到重启)。 - /var/log — 文本文件。由
rsyslog(例如/var/log/syslog、/var/log/auth.log)或应用程序本身写入:nginx、PostgreSQL、MySQL 等。 - 内核环形缓冲区 — 驱动程序和硬件的消息,可以用
dmesg或journalctl -k查看。
| 文件 | 内容 |
|---|---|
/var/log/syslog(Debian/Ubuntu) | 通用系统日志 |
/var/log/messages(RHEL/Fedora) | Red Hat 系列中的同类日志 |
/var/log/auth.log 或 /var/log/secure | 登录、sudo、SSH |
/var/log/kern.log | 内核消息 |
/var/log/dpkg.log、/var/log/apt/ | 软件包安装记录 |
/var/log/nginx/ | Web 服务器的 access.log 和 error.log |
/var/log/wtmp、/var/log/btmp | 二进制的登录记录,通过 last 和 lastb 读取 |
/var/log 中的大多数文件只有 root 或 adm 组可以读取,因此读取命令往往需要通过 sudo 运行。
journalctl:基本命令
不带参数时,journalctl 会通过分页器 less 输出全部日志,从最早的记录开始。这很少需要——通常会先缩小范围。
journalctl # 全部日志,从旧到新
journalctl -e # 直接跳到末尾
journalctl -n 50 # 最后 50 行
journalctl -r # 倒序,最新的在上面
journalctl --no-pager # 不使用 less,便于脚本和 grep
在分页器中可以使用熟悉的 less 快捷键:/ 搜索,G 跳到末尾,q 退出。过长的行会按屏幕宽度截断;用左右方向键可以横向滚动。
某个服务的日志:journalctl -u
最常见的场景是查看某个 systemd 单元的输出。不需要查找文件,也不需要用 grep:
$ sudo journalctl -u nginx -n 20
Oct 14 03:12:41 web1 systemd[1]: Starting nginx.service - A high performance web server...
Oct 14 03:12:41 web1 nginx[1834]: nginx: [emerg] bind() to 0.0.0.0:80 failed (98: Address already in use)
Oct 14 03:12:41 web1 systemd[1]: nginx.service: Control process exited, code=exited, status=1/FAILURE
Oct 14 03:12:41 web1 systemd[1]: nginx.service: Failed with result 'exit-code'.
-u 可以重复使用:journalctl -u nginx -u php8.2-fpm 会按时间顺序混合显示两个服务的日志,适合排查发生在两者交界处的错误。查看用户级单元(systemctl --user)的日志时,使用 journalctl --user -u 名称。
实时日志:journalctl -f
sudo journalctl -u nginx -f
这相当于 tail -f:命令先输出最后 10 条记录,然后持续等待新记录。在重启服务或重现错误时,把它放在另一个终端里很方便。按 Ctrl+C 退出。
按时间和启动次数过滤
如果知道故障发生的时间,可以用 --since 和 --until 选择时间段。它们既接受精确日期,也接受相对时间:
journalctl --since "2026-10-14 02:50" --until "2026-10-14 03:30"
journalctl --since "1 hour ago"
journalctl --since yesterday --until today
journalctl -u nginx --since "10 min ago"
另一种有用的切分方式是按系统启动次数。如果服务器自己重启了,原因通常在上一次会话的末尾:
$ journalctl --list-boots
-2 8f1c... Sat 2026-10-12 09:01:12 UTC Sun 2026-10-13 22:47:03 UTC
-1 a7d2... Sun 2026-10-13 22:48:30 UTC Tue 2026-10-14 03:05:51 UTC
0 c3e9... Tue 2026-10-14 03:07:02 UTC Tue 2026-10-14 09:15:40 UTC
$ journalctl -b -1 -e # 上一次启动的结尾
$ journalctl -b # 仅当前启动
只有日志保存在磁盘上时,才能列出启动记录。如何开启这一功能见下文关于 journald 配置的部分。
按重要程度过滤:journalctl -p
每条记录都有一个 0 到 7 的 syslog 优先级。参数 -p 会显示指定级别及更严重的记录:
| 编号 | 名称 | 使用场景 |
|---|---|---|
| 0 | emerg | 系统不可用 |
| 1 | alert | 需要立即处理 |
| 2 | crit | 严重错误 |
| 3 | err | 错误 |
| 4 | warning | 警告 |
| 5 | notice | 重要但属正常的事件 |
| 6 | info | 信息性消息 |
| 7 | debug | 调试信息 |
journalctl -p err -b # 本次启动以来的所有错误
journalctl -p warning..err -u ssh # 单个服务的级别范围
在重启或更新之后,journalctl -p err -b 是很好的第一项检查:几秒钟内就能看出哪里出了问题。
搜索及其他过滤条件
journald 为每条记录保存结构化字段,可以直接按字段过滤,无需 grep:
journalctl _PID=1834 # 按进程号
journalctl _COMM=sshd # 按程序名
journalctl _UID=1000 # 按用户
journalctl /usr/sbin/sshd # 按可执行文件路径
journalctl -k # 只显示内核消息(类似 dmesg)
journalctl -t CRON # 按 syslog 标识符
journalctl -g "Out of memory" # 按文本搜索,支持正则表达式
参数 -g(--grep)从 systemd 237 开始提供;在较旧的系统上,使用普通管道 journalctl --no-pager | grep ...。不同字段的过滤条件之间是“与”的关系,因此 journalctl -u nginx -p err --since today 只返回今天 nginx 的错误。
输出格式
参数 -o 用于改变输出格式,常用的有:
-o short-iso— ISO 8601 格式的时间,便于与应用日志对照。-o verbose— 显示记录的所有字段,包括_SYSTEMD_UNIT、_PID、_BOOT_ID。-o json或json-pretty— 供脚本或jq处理。-o cat— 只输出消息文本,不带时间和主机名。
journalctl -u myapp -o json --since today | jq -r '.MESSAGE'
配置 journald:存储与大小
配置位于 /etc/systemd/journald.conf(覆盖设置更适合放在 /etc/systemd/journald.conf.d/*.conf 中)。最主要的参数是 Storage:
persistent— 日志写入磁盘的/var/log/journal,重启后依然保留。volatile— 只保存在内存中,位于/run/log/journal。auto(默认)— 如果/var/log/journal目录存在,则写入磁盘。
在一些精简的系统镜像上,该目录不存在,重启后历史记录就会丢失。要开启持久保存:
sudo mkdir -p /var/log/journal
sudo systemd-tmpfiles --create --prefix /var/log/journal
sudo systemctl restart systemd-journald
为了防止日志占满整个磁盘,可以设置上限:
# /etc/systemd/journald.conf.d/size.conf
[Journal]
Storage=persistent
SystemMaxUse=500M
MaxRetentionSec=1month
默认情况下,SystemMaxUse 为文件系统大小的 10%,但不超过 4 GB。
如何清理 journalctl 日志
$ journalctl --disk-usage
Archived and active journals take up 1.8G in the file system.
sudo journalctl --vacuum-size=300M # 最多保留 300 MB
sudo journalctl --vacuum-time=2weeks # 删除两周以前的记录
sudo journalctl --rotate # 关闭当前活动文件,使其也可以被清理
不建议用 rm 删除 /var/log/journal 中的文件:vacuum 会安全地完成同样的工作,并且不会动正在写入的活动文件。
rsyslog:经典的文本日志
rsyslog 是一个守护进程,它接收 syslog 消息(包括从 journald 转发来的消息),并按规则把它们写入不同的文件。配置位于 /etc/rsyslog.conf 和 /etc/rsyslog.d/*.conf。每条规则由“来源.级别”选择器和动作组成:
# /etc/rsyslog.d/50-default.conf(节选)
auth,authpriv.* /var/log/auth.log
*.*;auth,authpriv.none -/var/log/syslog
kern.* -/var/log/kern.log
路径前面的减号表示每写入一行不立即同步到磁盘——速度更快,但发生故障时最后几行可能丢失。修改配置后,先检查语法,再重启服务:
sudo rsyslogd -N1
sudo systemctl restart rsyslog
把 rsyslog 与 journald 放在一起的主要原因是把日志转发到中央服务器。一行配置就能通过 TCP 发送全部内容:
# /etc/rsyslog.d/90-remote.conf
*.* @@logs.example.com:514
单个 @ 表示 UDP,两个 @@ 表示 TCP。即使服务器整体故障,日志副本仍保存在另一台机器上。要从脚本中向系统日志写入自己的消息,使用 logger:
logger -t backup -p user.err "数据库备份失败"
journalctl -t backup -n 5
logrotate:日志轮转
文本日志会无限增长,除非有人把它截断。logrotate 负责这件事:每天(通过 cron 或 logrotate.timer 定时器)它会重命名当前文件、压缩旧文件,并删除最旧的文件。通用设置位于 /etc/logrotate.conf,各程序的规则位于 /etc/logrotate.d/。
# /etc/logrotate.d/myapp
/var/log/myapp/*.log {
daily
rotate 14
compress
delaycompress
missingok
notifempty
create 0640 myapp adm
postrotate
systemctl reload myapp > /dev/null 2>&1 || true
endscript
}
| 指令 | 含义 |
|---|---|
daily / weekly / size 100M | 何时轮转 |
rotate 14 | 保留多少个旧文件 |
compress、delaycompress | 用 gzip 压缩,但最新的归档保持不压缩 |
missingok、notifempty | 文件不存在时不报错,空文件不轮转 |
create 0640 user group | 新文件使用的权限 |
copytruncate | 复制后清空原文件——适用于不会重新打开日志的程序 |
postrotate ... endscript | 轮转之后执行的命令,通常是通知应用重新打开文件 |
可以在不做任何更改的情况下测试规则,也可以立即强制轮转:
sudo logrotate -d /etc/logrotate.d/myapp # 调试模式的空运行
sudo logrotate -f /etc/logrotate.d/myapp # 立即轮转
常见错误是忘了 postrotate 或 copytruncate。这时应用会继续通过旧的文件描述符写入已重命名的 app.log.1,而新的 app.log 则始终为空。
阅读文本日志
对于 /var/log 中的文件,标准工具就足够了:
sudo tail -n 100 /var/log/syslog # 最后几行
sudo tail -F /var/log/nginx/error.log # 持续跟踪,轮转后仍有效
sudo less +G /var/log/auth.log # 直接打开到末尾
sudo zgrep "Failed password" /var/log/auth.log.*.gz # 在压缩归档中搜索
sudo grep -c " 500 " /var/log/nginx/access.log # 500 响应的数量
注意 tail -F 中的大写 F:与 -f 不同,它在 logrotate 替换文件后会重新打开文件。
分步分析一次故障
“服务夜里崩溃了”这类情况可以按以下顺序排查:
systemctl status myapp— 当前状态以及日志的最后几行。journalctl -u myapp --since "03:00" --until "03:30"— 服务在崩溃时段写入了什么。journalctl -p err -b— 同一时间系统中是否还有其他错误。journalctl -k --since "03:00" -g "Out of memory|Killed process"— 内核的 OOM Killer 是否杀死了该进程。- 应用自身在
/var/log/myapp/中的日志,包括.gz归档。 - 如果服务器曾经重启——
journalctl -b -1 -e。
总结
日常处理 Linux 日志只需要几个命令:journalctl -u 查看服务,-f 查看实时输出,--since 和 -p err 缩小范围,-b -1 分析重启。文本日志保存在 /var/log 中,rsyslog 负责分类和转发,而 logrotate 以及 journalctl --vacuum-* 防止日志占满磁盘。journalctl、日志轮转和负载诊断的实际操作,我们在 Linux 课程的讲座中都有演示。
常见问题
为什么 journalctl 只显示上一次启动以来的记录?
日志只保存在内存中(/run/log/journal)。创建 /var/log/journal 目录,或在 journald.conf 中设置 Storage=persistent,然后重启 systemd-journald。
如何让普通用户不用 sudo 也能读取系统日志?
把用户加入 systemd-journal 组(在 Debian 和 Ubuntu 上是 adm 组):sudo usermod -aG systemd-journal 用户名,然后重新登录。
能否查看其他系统的日志,例如挂载磁盘上的日志?
可以,用 -D 参数指定日志目录:journalctl -D /mnt/var/log/journal。参数 --file 可以读取单个 .journal 文件。
有了 journald 还需要 rsyslog 吗?
不一定。如果需要为旧工具提供文本文件,或者需要把日志转发到中央服务器,它就有用;在很多精简镜像上已经不再安装它。
如何把某个服务的日志导出到文件发给同事?
执行 journalctl -u 名称 --since today --no-pager > service.log,得到的就是普通文本文件。