journalctl 实用指南:查日志的姿势对了,排障才不用猜

journalctl 实用指南:查日志的姿势对了,排障才不用猜

journalctlsystemd日志Ubuntu运维
📶 入门 🧩 systemd · Ubuntu

凌晨两点服务挂了,SSH 上去第一件事是翻日志。很多人这时候敲的是 journalctl -xe,然后在一屏滚不到头的输出里肉眼翻找——那几行屏幕装不下的日志里,九成跟这次故障无关。journalctl 的价值在过滤,不在翻页。 按 unit、时间、优先级三个维度把范围收窄到十几行,故障原因通常就在眼前。

日志写入由 systemd-journald 统一接管:所有服务的输出、内核消息、定时任务记录都进同一个二进制日志库,journalctl 负责查询。它跟传统 syslog 不冲突,但 Ubuntu 24.04 上查故障,journalctl 是第一入口。systemd 本身的基本概念见 systemd 入门,日志文件本身的轮转归档由 logrotate 负责,见 logrotate 配置详解。

先把日志保住:持久化确认

journal 默认分两种存储模式:volatile 只写内存,重启即清空;persistent 写到 /var/log/journal/,重启后还在。Ubuntu 24.04 装机时 /var/log/journal/ 目录已存在,默认就是持久化。验证一下:

ls /var/log/journal/
journalctl --list-boots | tail -5

--list-boots 列出历次启动的编号。如果能看到上一次、上上次启动的记录,持久化就是开着的。排障时 journalctl -b -1 直接查上一次启动的日志——服务器半夜莫名重启,原因往往记在挂掉前最后几分钟的日志里:

journalctl -b -1 -e          # 上一次启动的日志末尾
journalctl -b -1 -p err      # 上一次启动里的错误及以上级别

裸机 Arch 等发行版默认 Storage=auto,目录不存在就只写内存。配置在 /etc/systemd/journald.conf 的 [Journal] 段,改成下面这样并重启服务后生效:

[Journal]
Storage=persistent
sudo systemctl restart systemd-journald

三个过滤维度:unit、时间、优先级

查日志的效率取决于范围收得多窄。按服务查,-u 后跟 unit 名,.service 后缀可省:

journalctl -u nginx -f            # 实时跟踪 nginx 日志,类似 tail -f
journalctl -u nginx --since today # 今天的 nginx 日志

按时间查,--since / --until 接受人类可读的时间写法:

journalctl --since "2026-10-07 09:00" --until "2026-10-07 10:30"
journalctl --since "30 min ago"
journalctl --since yesterday -u ssh

按优先级查。日志级别沿用 syslog 的 0-7 数值,数字越小越严重:

级别关键字含义
0-2emerg / alert / crit系统不可用、需要立即处理、崩溃
3err报错,服务还能跑但功能受损
4warning预警,不处理可能演变成故障
5-6notice / info正常运行记录,占日志大头
7debug调试信息,默认多数服务不输出
journalctl -p err -b          # 本次开机的所有错误及以上
journalctl -p 3..4 -u mysql   # 只要 err 和 warning,不带 info 噪音

三个维度可以自由组合。定位 OOM 击杀这类问题的经典组合拳:

journalctl -k -b -1 | grep -i "out of memory"   # 上次开机的内核消息里找 OOM
journalctl -u myapp --since "1 hour ago" -p warning

内核消息也能脱离 grep 直接用 journalctl -k 拿到,等价于 dmesg 但带时间戳排序和持久化。

再进阶两步:grep 下推和字段过滤

journalctl | grep 关键词 能用,但更好的写法是让 journalctl 自己过滤——它只解码匹配的条目,日志量大时快得多:

journalctl --grep "Segmentation"

journal 的每条记录都是结构化字段,可以直接按字段等值过滤。常用字段:_PID(进程号)、_COMM(进程名)、_SYSTEMD_UNIT(所属 unit)、SYSLOG_IDENTIFIER(日志标识):

journalctl _SYSTEMD_UNIT=ssh.service
journalctl SYSLOG_IDENTIFIER=sudo

输出格式用 -o 切换。写脚本处理日志时用 JSON,字段一目了然:

journalctl -u nginx --since today -o json-pretty | head -30

磁盘配额:给日志划上限

持久化日志默认最多吃掉所在文件系统 10% 的空间,上限 4 GiB;同时 journald 保证文件系统至少剩余 15% 可用(SystemKeepFree 默认值),两个限制取更小的那个执行。小盘 VPS 上 10% 也不算少,20 GB 的盘就是 2 GB 日志。

小盘机器上手动设上限。新建 drop-in 配置而不是改主配置:

sudo mkdir -p /etc/systemd/journald.conf.d
sudo tee /etc/systemd/journald.conf.d/size.conf <<'EOF'
[Journal]
SystemMaxUse=500M
EOF
sudo systemctl restart systemd-journald

改完验证当前生效的限制:

journalctl -b -u systemd-journald | grep -i "system journal"

查看现状和手动收缩:

journalctl --disk-usage            # 日志当前占用
sudo journalctl --vacuum-size=200M # 收缩到 200M 以内
sudo journalctl --vacuum-time=2weeks # 删掉两周前的日志

vacuum 只对已归档(rotate 过)的日志文件生效,先执行 --rotate 让 journald 切换出新文件再收缩,一条命令搞定:

sudo journalctl --rotate --vacuum-size=200M

跟 logrotate 的分工:journal 有自己的配额机制(SystemMaxUse + vacuum),应用自己写的日志文件才交给 logrotate。两套机制各管一摊,别把 journald 的日志再塞给 logrotate 转一遍。

常见问题

journalctl 提示没有权限看系统日志? 普通用户默认只能读自己的用户日志。把自己加进 systemd-journal 组(adm 组成员也行)即可读系统日志:sudo usermod -aG systemd-journal $USER,重新登录生效。

Docker 容器的日志能用 journalctl 查吗? 不能直接查。容器 stdout/stderr 由 Docker 的 json-file 驱动落到 /var/lib/docker/containers/,用 docker logs 查。宿主机上 dockerd 自身的日志才有 journalctl -u docker 的份。容器日志管理见 Docker 安装速查。

日志里全是 info,想看某个服务为什么启动失败? 服务启动失败先看 systemctl status myapp 给出的最后 10 行日志,再 journalctl -u myapp -b -p err 收窄。启动失败的前因往往在 err 级别以上,info 里翻是浪费时间。

--since "20 min ago" 这类写法失败报错? 带空格的时间表达式要加引号,--since "20 min ago" 而非 --since 20 min ago。也支持 today、yesterday、"2026-10-07 09:00:00" 等格式。

日志文件被误删了怎么办? 先 systemctl restart systemd-journald,journald 会重建文件结构继续记录。已删的日志无法找回,journal 是二进制格式但没有备份副本,重要日志要外发(rsyslog 转发或定期导出 JSON)才有恢复余地。

收束

排障效率的分水岭在过滤熟练度:-u 收服务、--since 收时间、-p err 收级别,三步下去日志范围缩到十几行,故障点自己浮出来。新机器到手先确认持久化开着、SystemMaxUse 配额跟盘容量匹配,这两件事五分钟做完,之后每一次故障排查都省时间。