journalctl 是 systemd 体系里最值得花时间掌握的工具之一。按「原理 → 常用 → 进阶 → 排障」的顺序讲。
一、原理:日志从哪来
1. systemd-journald 是什么
一个常驻的守护进程,负责收集整个系统的日志,而不是像传统 syslog 那样由各个程序自己往文件里写。
它接收这些来源:
| 来源 | 说明 |
|---|---|
| 内核 | /dev/kmsg,dmesg 的内容 |
| systemd 自身 | 服务启动、停止、失败 |
| 服务的 stdout/stderr | 由 systemd 接管的服务,直接输出就被抓走 |
| syslog 套接字 | /dev/log,传统程序写 syslog 也进这里 |
| audit | 内核审计 |
关键点:你用 systemd 跑的服务,console.log、print() 输出的东西不需要配任何东西就会进 journal。Ghost 的日志就是这么来的——它只是往 stdout 打,systemd 把它收走了。
2. 存储在哪
/run/log/journal/<machine-id>/ 内存,重启即丢
/var/log/journal/<machine-id>/ 磁盘,持久
- 用哪个由
/etc/systemd/journald.conf的Storage=决定:volatile(内存)/persistent(磁盘)/auto(有/var/log/journal目录就持久) - 文件名是
system.journal、user-1000.journal这种,二进制格式,所以不能cat,必须用journalctl读
Ubuntu 默认 Storage=auto,所以 /var/log/journal 如果存在就是持久化。云镜像有时被精简掉了,重启后日志就没了——这时 journalctl 会告诉你 -- No entries --。
3. 为什么它比 grep 文本日志强
每条日志不是一行文本,而是一个带字段的结构化条目:
_SYSTEMD_UNIT=ghost_www-test-com.service
_PID=37952
_COMM=node
PRIORITY=3
MESSAGE=Failed to send email...
所以你能按「哪个服务、哪个进程、什么级别、什么时间」精确过滤,而不是在几万行文本里 grep。索引是预先建好的,过滤速度比 grep 快得多。
二、最基础的几条
journalctl # 全部日志,进 less,q 退出
journalctl -n 50 # 最近 50 行
journalctl -f # 实时跟踪(类似 tail -f),Ctrl+C 退出
journalctl -r # 倒序,最新的在前
journalctl --no-pager # 不进 less,直接输出(脚本/管道里必加)
--no-pager 是重点:不加的话输出会进分页器,管道给 grep 时行为很怪,所以在脚本和组合命令里永远加上它。
三、按服务过滤(最常用)
journalctl -u nginx
journalctl -u ghost_www-test-com.service
journalctl -u nginx -u mysql # 多个服务
journalctl -u "ghost_*" # 通配符,注意加引号
-u 是 unit。服务名可以用 systemctl list-units | grep xxx 查到。
为什么排查 Ghost 时一直用它:一个服务的日志和系统其他日志混在一起,-u 直接把范围缩到一个进程。
四、按时间过滤
journalctl --since "2026-10-23 17:00:00"
journalctl --since "1 hour ago"
journalctl --since "10 min ago"
journalctl --since today
journalctl --since yesterday --until "2026-10-23 18:00"
journalctl --since "2026-10-23" --until "2026-10-24"
时间格式很宽松:YYYY-MM-DD HH:MM:SS、today、yesterday、N min/hour/day ago 都行。
实用组合:只看故障时间窗
journalctl -u ghost_www-test-com.service --since "17:10" --until "17:20" --no-pager
五、按优先级过滤
日志级别(数字越小越严重):
| 数字 | 名称 | 含义 |
|---|---|---|
| 0 | emerg | 系统不可用 |
| 1 | alert | 必须立即处理 |
| 2 | crit | 严重 |
| 3 | err | 错误 |
| 4 | warning | 警告 |
| 5 | notice | 正常但重要 |
| 6 | info | 普通信息 |
| 7 | debug | 调试 |
journalctl -p err # err 及以上(err/crit/alert/emerg)
journalctl -p warning # warning 及以上
journalctl -p 3 # 也可以用数字
journalctl -u ghost -p err -n 50
注意 -p err 是「err 以及更严重」,不是「只有 err」。
六、按进程 / 标识过滤
journalctl _PID=37952 # 按 PID
journalctl _COMM=node # 按进程名
journalctl -t sshd # 按 syslog identifier
journalctl _SYSTEMD_UNIT=nginx.service # 等价于 -u
journalctl -k # 只看内核日志,等价 dmesg
journalctl --user # 当前用户的日志
journalctl --user-unit=foo.service # 用户级服务
字段名前面带下划线的是「可信字段」(journald 自己加的,程序改不了)。
七、输出格式
journalctl -o short # 默认,Oct 23 17:19:19 host node[37952]: ...
journalctl -o short-iso # 带 ISO 时间戳,跨时区排查有用
journalctl -o short-precise # 微秒精度
journalctl -o cat # 只有消息,没有时间/主机前缀
journalctl -o json-pretty # 结构化 JSON,适合 jq
journalctl -o verbose # 所有字段全展开
导出给程序处理:
journalctl -u ghost -o json-pretty --no-pager | jq '.[].MESSAGE'
journalctl -u ghost -o json --no-pager | jq -r 'select(.PRIORITY=="3") | .MESSAGE'
八、启动次数
journalctl --list-boots # 列出每次启动的编号
journalctl -b # 本次启动
journalctl -b -1 # 上一次启动(排查重启前的问题)
journalctl -b -1 -p err # 上次启动的所有错误
机器重启后想查「重启之前发生了什么」,-b -1 是唯一入口。
九、解释错误
journalctl -x -u ghost_www-test-com.service
-x 会给已知的错误码附上解释文本(来自 systemd catalog),比裸错误好读。
十、持久化与清理
开启持久化(如果 /var/log/journal 不存在):
sudo mkdir -p /var/log/journal
sudo systemd-tmpfiles --create --prefix /var/log/journal
sudo systemctl restart systemd-journald
或者改配置:
sudo nano /etc/systemd/journald.conf
# Storage=persistent
sudo systemctl restart systemd-journald
看占用:
journalctl --disk-usage
清理:
sudo journalctl --vacuum-size=500M # 保留 500MB
sudo journalctl --vacuum-time=7d # 只留 7 天
sudo journalctl --vacuum-files=3 # 只留 3 个文件
限制总大小(journald.conf):
SystemMaxUse=500M
SystemMaxFileSize=50M
十一、排障套路(对应我们排查 Ghost 的过程)
看服务为什么起不来:
sudo systemctl status ghost_www-test-com.service --no-pager
sudo journalctl -u ghost_www-test-com.service -n 100 --no-pager
sudo journalctl -u ghost_www-test-com.service -b -p err --no-pager
只看某个时间段的错误:
journalctl -u ghost_www-test-com.service --since "1 hour ago" -p err --no-pager
跟踪某类关键字:
journalctl -u ghost_www-test-com.service -f | grep -iE "mail|smtp|error"
看完整错误上下文(grep 会丢上下文,用 -A/-B):
journalctl -u ghost_www-test-com.service -n 200 --no-pager | grep -B3 -A8 "EmailError"
跨服务看同一时刻发生了什么:
journalctl --since "17:18" --until "17:20" --no-pager
不带 -u,看那两分钟里 nginx、ghost、kernel 的所有输出——定位「是服务挂了还是网络断了」时特别有用。
十二、几个坑
| 坑 | 说明 |
|---|---|
不加 --no-pager |
管道给 grep 时可能卡住或输出异常 |
| 默认只存内存 | 重启后日志消失,重要环境先开持久化 |
-p err 理解错 |
是 err 及以上,不是只要 err |
grep 过滤丢了上下文 |
用 -A/-B,或者先 -o cat 再 grep |
| 日志太多被轮转 | SystemMaxUse 配太小,关键日志可能已经被删 |
用户服务要 --user |
普通 -u 看不到用户级 systemd 服务 |
| 时间用本地时区 | --since 按服务器本地时间,跨时区注意 |
十三、速查表
journalctl -u 服务名 # 某服务
journalctl -u 服务名 -n 100 --no-pager # 最近 100 行
journalctl -u 服务名 -f # 实时跟踪
journalctl -u 服务名 -p err # 只看错误
journalctl -u 服务名 --since "1 hour ago"
journalctl -b -1 -p err # 上次启动的错误
journalctl -k # 内核
journalctl --disk-usage # 占用
journalctl --vacuum-time=7d # 清 7 天前
记住三条就够日常用:-u 定服务、--since 定时间、-p err 定级别,三者可以任意组合。
川公网安备51010802033276号