journalctl 查不到昨天的日志,是 journald 没开持久化
前阵子我一台跑在办公室的小服务半夜挂了,第二天早上九点多才有人发现。我 SSH 上去第一反应就是 journalctl -u xxx.service 看日志,结果翻出来的第一条时间是今天早上八点四十——正是我重启服务的时间点。昨晚三点到早上八点那段,一条都没有。
那一刻我还以为日志被谁删了。查了一圈才发现,这事跟删日志没关系,是 journald 压根就没把日志往磁盘上写。
先确认日志到底存在哪
journald 有两套存放位置:
/run/log/journal/—— 在内存里(tmpfs),重启就没了/var/log/journal/—— 在磁盘上,能跨重启保留
到底用哪个,取决于 /etc/systemd/journald.conf 里的 Storage=。默认值是 auto,而 auto 的意思是:如果 /var/log/journal 这个目录存在,就存磁盘;不存在,就退回到内存。
坑就在这。Debian、Ubuntu 系默认不带 /var/log/journal 这个目录,所以默认状态下你的日志全在 /run/log/journal,每次重启自动清空。我那台机器正是这样。
验证很简单:
# 看实际用了哪种存储
journalctl --list-boots
# 只输出一条 -0(当前这次启动),说明没持久化
ls -la /var/log/journal/
# 目录不存在或者里面是空的,实锤
--list-boots 这个命令很直观。开了持久化的机器会列出一串启动记录,每次开机一行;没开的机器永远只有一行。
改成持久化:两条命令的事
不用改配置文件也能开,创建目录就行——因为 auto 的判定就是看目录在不在:
sudo mkdir -p /var/log/journal
sudo systemd-tmpfiles --create --prefix /var/log/journal
sudo systemctl restart systemd-journald
中间那条 systemd-tmpfiles 别省。它负责把目录的属主、权限按系统规则设好(应该是 root:systemd-journal),少了这步 journald 可能拒绝往里写,日志里会报权限问题。
不过我更推荐直接写进配置,省得哪天有人手贱删了目录又悄悄退化回内存:
sudo vim /etc/systemd/journald.conf
把 [Journal] 段里的 Storage 改成:
[Journal]
Storage=persistent
重启生效后再跑一次 journalctl --list-boots,应该能看到多条记录了。
日志能存了,还得防止它把盘吃掉
持久化的另一面是:journald 会一直写,直到占满分区。它自己有上限策略,默认是分区的 10%(且不超过 4G),对小盘机器还是有点多。我一般会显式写死:
[Journal]
Storage=persistent
SystemMaxUse=500M
SystemKeepFree=1G
MaxRetentionSec=2week
SystemMaxUse:日志总量上限SystemKeepFree:给分区留出的余量,比SystemMaxUse更实用,盘小的时候会先触发MaxRetentionSec:只保留最近两周
手工清理也有现成命令:
journalctl --disk-usage # 先看占了多少
journalctl --vacuum-size=500M # 压到 500M
journalctl --vacuum-time=7d # 只留最近 7 天
这里有个我踩过的坑:--vacuum-* 只处理已经归档(archived)的日志文件,当前正在写的那个 active 文件清不动。所以有时明明超限了却清不下来。解决办法是先强制轮转一次,让当前文件先归档:
sudo systemctl kill --kill-who=main --signal=SIGUSR2 systemd-journald
sudo journalctl --vacuum-size=500M
实在不行就 systemctl restart systemd-journald,效果一样,只是会短暂中断日志收集。
真正查故障时,我常用的几个组合
日志留下来之后,查起来才有的可查。几个高频用法:
# 按时间窗精准切一段(半夜 3 点那次)
journalctl -u myapp.service --since "2026-10-19 02:30" --until "2026-10-19 04:00"
# 上一次启动的日志(服务挂掉后重启的场景)
journalctl -u myapp.service -b -1
# 所有启动的记录一起看
journalctl -u myapp.service -b all
# 只看错误级别
journalctl -u myapp.service -p err -b
# 去掉默认的分页器,方便管道处理
journalctl -u myapp.service --no-pager | grep -i timeout
# 看内核消息,查 OOM kill 很有用
journalctl -k --no-pager | grep -i "out of memory"
--since / --until 的时间格式挺宽松,"yesterday"、"-3h"、"2026-10-19 02:30:00" 都能认,排查时用自然语言写法最省事。
还有个容易被忽略的点:journalctl -u xxx 默认只看本次启动。很多人查历史问题直接敲这条,发现没东西就以为日志丢了,其实加个 -b -1 或 -b all 就出来了。
按进程过滤也挺好使:
journalctl _PID=1234
journalctl _COMM=python3 --since "1 hour ago"
这些下划线开头的字段叫 trusted fields,journald 会自动索引,比 grep 快得多。想知道某条日志有哪些字段可用,用 -o json-pretty 打一条出来看:
journalctl -u myapp.service -n 1 -o json-pretty
顺带说一句 Docker
如果你和我一样把服务跑在容器里,journalctl -u docker.service 只能看到 Docker 守护进程自己的日志,容器里应用输出的 stdout 是不进 journald 的——默认走的是 json-file,落在 /var/lib/docker/containers/<id>/ 下面。
想统一到 journald 里查,可以在 compose 里指定:
services:
myapp:
image: myapp:latest
logging:
driver: journald
options:
tag: myapp
然后就能按容器名查了:
journalctl CONTAINER_NAME=myapp -f
journalctl CONTAINER_TAG=myapp --since "-30min"
这样容器日志也能跨重启保留,还顺带解决了之前写过的 json-file 日志把系统盘写满的问题。
收个尾
现在我的新机器装机清单里多了两条:建 /var/log/journal 目录、写死 journald 的 SystemMaxUse 和 MaxRetentionSec。花不了两分钟,但省掉的是"日志呢?日志去哪了?"这种完全没头绪的十分钟。
排查故障最难受的从来不是问题难,而是证据没了。日志能留下来,问题就已经解决一半了。
- 点赞
- 收藏
- 关注作者
评论(0)