journalctl使用方式

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 定级别,三者可以任意组合。

蜀ICP备2026026554号-1 · 川公网安备51010802033276号 公安备案 川公网安备51010802033276号