主题
journalctl 日志排查
简介
journalctl 是 systemd 日志系统 systemd-journald 的查询工具。它把内核消息、systemd 自身日志、服务 stdout/stderr、syslog 转发、审计事件等收集到一个结构化、带索引的二进制日志中,再用 journalctl 按时间、服务、优先级、字段等条件检索。
和传统的 /var/log/messages、/var/log/syslog 相比,journal 的优势是:
- 每条日志都有结构化字段,例如
_PID、_UID、_SYSTEMD_UNIT、PRIORITY - 支持按服务、启动会话、时间范围精确过滤
- 服务 stdout/stderr 可以直接进 journal,不需要自己写日志文件
- 支持
json、verbose等输出,便于脚本处理
journal 文件是二进制格式,不要直接对 /var/log/journal 跑 grep。先用 journalctl 过滤,或者用 -o cat 转成纯文本后再交给 grep/awk。
配套阅读
journal 负责“日志怎么查”,systemd 负责“服务怎么跑”。服务启动失败时,通常先看 systemd 使用详解,再用本文的 journalctl -xeu 定位原因。
日志存在哪里
| 路径 | 说明 |
|---|---|
/var/log/journal/ | 持久化日志,重启后保留 |
/run/log/journal/ | 内存日志,重启后丢失 |
/etc/systemd/journald.conf | journald 配置 |
默认 Storage=auto:
- 如果
/var/log/journal存在且可写,日志持久化 - 否则日志只保存在
/run/log/journal,重启即丢失
所以“为什么看不到上次启动的日志”通常不是 journalctl 的问题,而是 journal 没有持久化。
权限
journalctl 默认只能看到当前用户有权限查看的日志。查看系统日志通常需要:
rootsystemd-journal组成员- 部分发行版还会把用户加入
adm组
把普通用户加入 systemd-journal 组:
bash
sudo usermod -aG systemd-journal "$USER"
# 重新登录后生效基本查询
查看全部日志
bash
journalctl默认从最早一条开始,使用 pager 分页。常用控制:
bash
journalctl --no-pager # 不分页,适合管道和脚本
journalctl -e # 直接跳到末尾
journalctl -n 100 # 最近 100 条
journalctl -r # 最新的在前
journalctl -f # 持续跟踪新日志journalctl 不带参数可能输出非常多内容,日常排查更推荐先加时间或服务过滤。
按服务查看
bash
journalctl -u nginx.service
journalctl -u nginx.service -b
journalctl -u nginx.service -n 200 --no-pager
journalctl -u nginx.service -f多个 -u 之间是“或”的关系:
bash
journalctl -u nginx.service -u php-fpm.service -b查看用户服务:
bash
journalctl --user-unit myapp.service
journalctl --user-u 只匹配系统 unit;用户单元要用 --user-unit,或者先加 --user。
按启动会话查看
bash
journalctl --list-boots
journalctl -b
journalctl -b -1
journalctl -b -2-b 表示当前启动,-b -1 表示上一次启动,-b <BOOT_ID> 可以指定 --list-boots 里的 ID。
-b -1 依赖持久化 journal。如果日志只存在内存里,重启后只能看到当前启动。
查看内核日志
bash
journalctl -k
journalctl -k -b
journalctl -k -b -1-k 等价于 --dmesg,默认显示当前启动的内核消息;配合 -b -1 可以看上一次启动。它和 dmesg 的区别是 journal 会保留历史启动(如果有持久化日志),并且能和其他服务日志按时间混排。
按标识符查看
bash
journalctl -t sshd
journalctl -t kernel
journalctl -t sudo-t 过滤 SYSLOG_IDENTIFIER,适合 syslog 风格的程序。systemd 服务更推荐用 -u,因为 -u 直接按 unit 过滤,不依赖程序设置的标识符。
按 facility 查看
bash
journalctl --facility=daemon
journalctl --facility=kern
journalctl --facility=auth常见 facility:kern、user、daemon、auth、authpriv、cron、local0 到 local7。
按优先级查看
日志级别:
| 数字 | 名称 | 含义 |
|---|---|---|
| 0 | emerg | 系统不可用 |
| 1 | alert | 必须立即处理 |
| 2 | crit | 严重错误 |
| 3 | err | 错误 |
| 4 | warning | 警告 |
| 5 | notice | 正常但重要 |
| 6 | info | 信息 |
| 7 | debug | 调试 |
bash
# err 及更严重的日志(emerg..err)
journalctl -p err
# 等价于 -p err,同样是 err 及更严重
journalctl -p 3
# 只看 err 这一级
journalctl -p 3..3
# 指定范围:err 到 notice(3..5)
journalctl -p 3..5
# warning 到 err
journalctl -p warning..err-p err 不是“只看 err”,而是显示优先级数值小于等于 3 的所有日志,也就是 emerg、alert、crit、err。
按时间查看
bash
journalctl --since '2026-10-19 10:00:00'
journalctl --since '2026-10-19' --until '2026-10-19 12:00:00'
journalctl --since today
journalctl --since yesterday
journalctl --since '1 hour ago'
journalctl --since -30min
journalctl --until '10 min ago'--since 和 --until 使用本地时间。日志本身可能带时区,跨时区分析时加 --utc,或者用 -o short-iso 查看带偏移的时间。
按内容搜索
bash
journalctl -g 'timeout'
journalctl -g 'out of memory|oom-kill'
journalctl -g 'TODO' --case-sensitive=no-g 是对 MESSAGE 字段做正则匹配,不是对整个输出做 grep。默认区分大小写;需要忽略大小写时加 --case-sensitive=no。
如果只是简单文本搜索,-g 通常比 journalctl | grep 更快,因为它可以在读取日志时过滤。
字段过滤
journal 的每条记录都是一组字段。FIELD=VALUE 形式的参数可以直接过滤字段:
bash
journalctl _SYSTEMD_UNIT=nginx.service
journalctl _PID=1234
journalctl _UID=1000
journalctl _COMM=sudo
journalctl _EXE=/usr/bin/ssh
journalctl PRIORITY=3
journalctl SYSLOG_IDENTIFIER=sshd
journalctl _TRANSPORT=audit规则:
- 不同字段之间是 AND
- 同一字段的多个匹配之间是 OR
+用来分隔 OR 组
例如:
bash
# 同时满足 unit 和 PID
journalctl _SYSTEMD_UNIT=nginx.service _PID=1234
# 同一字段的 OR:nginx 或 sshd
journalctl _SYSTEMD_UNIT=nginx.service _SYSTEMD_UNIT=sshd.service
# 分组 OR:(nginx 且 PID=123) 或 (sshd)
journalctl _SYSTEMD_UNIT=nginx.service _PID=123 + _SYSTEMD_UNIT=sshd.service常用字段:
| 字段 | 含义 |
|---|---|
MESSAGE | 日志正文 |
PRIORITY | 优先级数字 |
SYSLOG_IDENTIFIER | syslog 标识符 |
SYSLOG_FACILITY | facility |
_PID | 进程 ID |
_UID / _GID | 用户 / 组 ID |
_COMM | 进程名 |
_EXE | 可执行文件路径 |
_CMDLINE | 完整命令行 |
_SYSTEMD_UNIT | 系统 unit |
_SYSTEMD_USER_UNIT | 用户 unit |
_SYSTEMD_CGROUP | cgroup 路径 |
_TRANSPORT | 日志来源,如 kernel、syslog、journal、stdout、audit |
_BOOT_ID | 启动会话 ID |
_HOSTNAME | 主机名 |
以 _ 开头的是 trusted field,由 systemd 填写,应用不能伪造;以 __ 开头的字段更底层,通常用于调试。
列出字段和取值
bash
journalctl -N # 当前日志中出现过的字段名
journalctl -F SYSLOG_IDENTIFIER # 某个字段的所有取值
journalctl -F _SYSTEMD_UNIT
journalctl -F PRIORITY-F 类似 SQL 的 SELECT DISTINCT,在不知道服务名或标识符时很好用。
按可执行文件过滤
journalctl 支持直接传绝对路径:
bash
journalctl /usr/bin/dbus-daemon
journalctl /usr/bin/ssh对可执行文件,这等价于匹配 _EXE;对脚本,可能匹配 _COMM。路径过滤适合定位某个程序产生的日志,但 systemd 服务还是用 -u 更直接。
输出格式
常用格式
| 格式 | 说明 |
|---|---|
short | 默认格式,日期、主机、标识符、消息 |
short-iso | ISO 8601 时间,带时区 |
short-precise | 微秒精度 |
short-monotonic | 从开机开始计时 |
short-unix | Unix 时间戳 |
short-full | 完整日期和时间 |
verbose | 所有字段,适合排查 |
json | JSON,每行一条 |
json-pretty | 格式化 JSON |
json-seq | JSON 序列 |
cat | 只输出消息正文 |
with-unit | 带 unit 名称 |
export | 可移植的二进制格式 |
bash
journalctl -u nginx -o short-iso -n 20
journalctl -u nginx -o cat -n 20
journalctl -u nginx -o verbose -n 1
journalctl -u nginx -o json-pretty -n 1选择字段
--output-fields 可以限制 verbose、export、json 输出中的字段:
bash
journalctl -u nginx -o json --output-fields=MESSAGE,PRIORITY,_PID -n 5处理长行和主机名
bash
journalctl -a # 显示完整字段,不省略长内容
journalctl --no-full # 省略长字段
journalctl -W # 不显示主机名
journalctl --utc # 用 UTC 显示时间默认输出可能截断长消息,排查堆栈、JSON、长命令行时加 -a。
用 jq 处理 JSON
bash
journalctl -u nginx -o json-pretty -n 1 | jq .
journalctl -u nginx -o json --no-pager \
| jq -r 'select((.PRIORITY | tonumber) <= 3) | .MESSAGE'JSON 里的字段值是字符串;需要数值比较时用 tonumber。MESSAGE 可能是字符串,也可能是字节数组,复杂场景要先判断类型。
持久化与配置
让日志持久化
确保 /var/log/journal 存在:
bash
sudo install -d -o root -g systemd-journal -m 2755 /var/log/journal
sudo systemctl restart systemd-journald
journalctl --flush或者在 /etc/systemd/journald.conf 中显式设置:
ini
[Journal]
Storage=persistent然后:
bash
sudo systemctl restart systemd-journaldStorage 的取值:
| 值 | 行为 |
|---|---|
auto | 默认,/var/log/journal 存在则持久化,否则内存 |
volatile | 只写 /run/log/journal,重启丢失 |
persistent | 优先写 /var/log/journal,启动早期可回退到内存 |
none | 不保存日志,只做转发 |
常用 journald 配置
ini
# /etc/systemd/journald.conf
[Journal]
Storage=persistent
Compress=yes
Seal=yes
SystemMaxUse=1G
SystemKeepFree=2G
SystemMaxFileSize=100M
SystemMaxFiles=10
RuntimeMaxUse=200M
MaxRetentionSec=2weeks
RateLimitIntervalSec=30s
RateLimitBurst=10000SystemMaxUse:/var/log/journal总大小上限SystemKeepFree:给文件系统至少保留多少空间SystemMaxFileSize/SystemMaxFiles:单个文件和文件数量RuntimeMaxUse:/run/log/journal内存上限MaxRetentionSec:按时间保留,默认 0 表示不启用RateLimitIntervalSec/RateLimitBurst:限制每个服务的日志产生速率Compress:压缩日志Seal:启用前向安全密封(FSS)
修改后重启 journald:
bash
sudo systemctl restart systemd-journald重启 journald 可能导致极短时间内的日志丢失;重要环境可以先 journalctl --sync。
转发到传统 syslog
如果系统还在用 rsyslog:
ini
[Journal]
ForwardToSyslog=yes这会把 journal 收到的日志再转发给 syslog,可能造成重复记录。要么以 journal 为主,要么以 rsyslog 为主,不要无意识地双写。
访问权限
如果普通用户执行 journalctl 只看到自己的日志,检查:
bash
id
getent group systemd-journal
ls -ld /var/log/journal把用户加入 systemd-journal 组后需要重新登录。不要为了方便直接把 journal 文件改成全局可读,日志里可能包含敏感信息。
日志维护
查看占用
bash
journalctl --disk-usage输出类似:
text
Archived and active journals take up 1.2G in the file system.清理日志
bash
# 保留最近 500M
sudo journalctl --vacuum-size=500M
# 只保留最近两周
sudo journalctl --vacuum-time=2weeks
# 只保留最近 5 个日志文件
sudo journalctl --vacuum-files=5清理只删除已归档的日志文件,当前活跃文件不会被删。需要先轮转时:
bash
sudo journalctl --rotate
sudo journalctl --vacuum-time=2weeks校验日志文件
bash
sudo journalctl --verify--verify 会检查 journal 文件的一致性。大量日志时可能比较慢,但怀疑文件损坏时值得跑一次。
同步与刷新
bash
sudo journalctl --sync # 把未写入磁盘的日志同步到磁盘
sudo journalctl --flush # 把 /run 中的日志刷到 /var--flush 只在持久化目录可用时有意义。
日志被限流
如果日志中出现:
text
Suppressed 1234 messages from ...说明 journald 的速率限制生效了。可以调整:
ini
[Journal]
RateLimitIntervalSec=30s
RateLimitBurst=10000把 RateLimitBurst 设为 0 可以关闭限制,但日志暴增时可能把磁盘写满,生产环境要谨慎。
实战排查
服务启动失败
bash
systemctl status myapp.service --no-pager -l
journalctl -xeu myapp.service -b --no-pager-x 增加 catalog 解释,-e 跳到末尾,-u 限定 unit,-b 限定本次启动。先看最后的错误,再往前找上下文。
只看错误级别:
bash
journalctl -u myapp.service -b -p err --no-pager跟踪服务日志:
bash
journalctl -u myapp.service -f查看上次启动的错误
bash
journalctl --list-boots
journalctl -b -1 -p err --no-pager
journalctl -b -1 -k --no-pager如果 -b -1 没有内容,先确认 journal 是否持久化。
内核 OOM
bash
journalctl -k -b -g 'Out of memory|oom-kill|Killed process'
journalctl -b -p err --no-pagerOOM 通常会看到 Out of memory: Killed process ... 或 oom-kill:。结合 systemctl status 看服务是否被 SIGKILL。
磁盘和文件系统错误
bash
journalctl -k -b -g 'I/O error|EXT4-fs error|XFS.*error|nvme'
journalctl -b -p err --no-pager内核日志能提供设备、文件系统和块层错误。进一步检查可以用 smartctl、dmesg、df。
SSH 登录与 sudo
bash
journalctl -u sshd -b --since today
journalctl -u ssh -b --since today
journalctl _COMM=sshd -b
journalctl _COMM=sudo -g 'FAILED'不同发行版的 SSH unit 名可能是 sshd.service 或 ssh.service,可以先 systemctl list-unit-files | grep ssh。
网络问题
bash
journalctl -u NetworkManager -b
journalctl -u systemd-networkd -b
journalctl -k -b -g 'link is not ready|eth0|wlan0|DHCP'具体接口名、驱动名要按机器替换。
Docker 与容器
bash
journalctl -u docker -b
journalctl -u containerd -b容器内应用日志是否进 journal,取决于 Docker 的 log driver 和容器的 stdout/stderr。很多场景还是用:
bash
docker logs --tail 200 container_name不要把 docker logs 和 journalctl -u docker 混为一谈:后者是 Docker 守护进程的日志,不一定是容器内应用的日志。
定时器任务
bash
journalctl -u backup.timer -u backup.service -b
systemctl list-timers --alltimer 只负责触发,真正执行的是同名 service。排查时两个 unit 都要看。
用户服务
bash
journalctl --user-unit myapp.service -b
systemctl --user status myapp.service用户服务日志在用户 journal 中,系统级 journalctl -u 看不到。需要 --user-unit 或 --user。
审计事件
bash
journalctl _TRANSPORT=audit -b
journalctl _TRANSPORT=audit -g 'denied'需要审计子系统配合;更专业的审计查询可以用 ausearch、aureport。
崩溃与核心转储
bash
journalctl --list-boots
journalctl -b -1 -p err --no-pager
coredumpctl list
coredumpctl info PID如果系统崩溃后重启,上一次启动的日志是否还在取决于持久化配置。
与 grep、awk 配合
journalctl 的结构化过滤通常比纯文本 grep 更准确,但简单场景仍可以组合:
bash
journalctl -u nginx -o cat --no-pager | grep -i 'timeout'
journalctl -u nginx -o short-iso --no-pager | awk '{print $1, $2, $3}'更推荐先缩小范围:
bash
journalctl -u nginx -b -p warning..err --no-pager
journalctl -u nginx -b -g 'timeout|refused' --no-pager -o cat需要做统计时,可以输出 cat 或 JSON 再交给 awk/jq:
bash
journalctl -u nginx -b -o cat --no-pager | awk '{count[$0]++} END {for (l in count) print count[l], l}' | sort -rn | head文本处理细节见 grep 命令使用详解 和 awk 命令使用详解。
常见坑
1. 看不到上次启动的日志
默认 Storage=auto,如果 /var/log/journal 不存在,日志只在 /run,重启就没了。解决:
bash
sudo mkdir -p /var/log/journal
sudo systemctl restart systemd-journald
journalctl --flush或者设置 Storage=persistent。
2. Permission denied
普通用户不在 systemd-journal 组,或者目录权限不对。检查:
bash
id
ls -ld /var/log/journal
journalctl -q -n 1不要用 chmod 777 解决日志权限问题。
3. -u 没有输出
可能原因:
- unit 名写错,例如
ssh.service和sshd.service - 服务没有把日志写入 journal
- 服务属于用户实例,需要
--user-unit - 看的是错误的启动会话,试试
-b -1 - 日志被
StandardOutput=null或重定向到文件
先确认 unit 和日志去向:
bash
systemctl cat myapp.service
systemctl show myapp.service -p StandardOutput,StandardError
journalctl -u myapp.service -b --no-pager4. -p err 不等于只看 err
-p err 会包含 emerg、alert、crit、err。需要精确范围时用 -p 3..3。
5. -g 默认区分大小写
bash
journalctl -g 'ERROR' --case-sensitive=no-g 只匹配 MESSAGE,不会匹配 unit 名、PID 等字段。按字段过滤要用 FIELD=VALUE。
6. 多个 -u 是 OR,多个字段是 AND
bash
# nginx 或 php-fpm
journalctl -u nginx -u php-fpm
# unit 是 nginx 且 PID 是 123
journalctl _SYSTEMD_UNIT=nginx.service _PID=123如果同一字段写多个值,它们之间是 OR;需要复杂组合时用 + 分组。
7. 时间过滤受时区影响
--since/--until 使用本地时区,日志显示也可能是本地时区。跨时区分析时:
bash
journalctl --utc --since '2026-10-19 00:00:00'
journalctl -o short-iso8. 输出被 pager 截断
脚本里加 --no-pager;查看长消息加 -a:
bash
journalctl -u myapp -a --no-pager9. 日志太多,看不到重点
先按服务、启动、优先级、时间缩小范围:
bash
journalctl -u myapp -b -p warning..err --since '1 hour ago' --no-pager不要直接 journalctl | grep 全量日志。
10. journald 限流丢日志
看到 Suppressed ... messages 时,调整 RateLimitBurst / RateLimitIntervalSec,或者修复刷屏的服务。把限制设为 0 只是临时手段。
11. 日志重复
如果同时启用了 journal 和 rsyslog,并且 ForwardToSyslog=yes,同一日志可能出现在两处。确认以哪个系统为主。
12. 容器里没有 journal
普通 Docker 容器通常没有运行 systemd-journald,journalctl 会提示没有日志文件或无法连接。需要在宿主机查,或者让容器把日志输出到 stdout/stderr,再用 docker logs。
13. 日志文件损坏
bash
sudo journalctl --verify如果确认损坏,先备份,再考虑清理相关归档文件;不要直接删除正在写入的活跃日志。
14. 清理日志后历史没了
--vacuum-* 会删除归档日志,属于不可逆操作。清理前确认保留期限和合规要求。
15. 应用日志不在 journal
journal 只收集写入 journal/syslog 的内容。应用自己写 /var/log/app.log 时,journalctl -u app 可能只有启动信息,没有业务日志。需要看应用自己的日志文件,或者修改 StandardOutput/日志配置。
速查表
bash
# 最近 100 条
journalctl -n 100
# 不分页
journalctl --no-pager
# 跟踪
journalctl -f
# 当前启动
journalctl -b
# 上一次启动
journalctl -b -1
# 启动列表
journalctl --list-boots
# 内核日志
journalctl -k -b
# 指定服务
journalctl -u nginx.service -b
# 多个服务
journalctl -u nginx -u php-fpm -b
# 用户服务
journalctl --user-unit myapp.service
# 错误及以上
journalctl -p err -b
# 优先级范围
journalctl -p 3..5 -b
# 时间范围
journalctl --since '1 hour ago'
journalctl --since today --until '12:00'
# 内容搜索
journalctl -g 'timeout'
journalctl -g 'timeout' --case-sensitive=no
# 字段过滤
journalctl _SYSTEMD_UNIT=nginx.service
journalctl _PID=1234
journalctl _COMM=sudo
journalctl _TRANSPORT=audit
# 列出字段值
journalctl -F SYSLOG_IDENTIFIER
journalctl -F _SYSTEMD_UNIT
# 只输出消息
journalctl -u nginx -o cat
# JSON
journalctl -u nginx -o json-pretty -n 1
# 查看日志占用
journalctl --disk-usage
# 清理
sudo journalctl --vacuum-size=500M
sudo journalctl --vacuum-time=2weeks
# 校验
sudo journalctl --verify
# 持久化后刷新
sudo journalctl --flush总结
journalctl 的核心是“按维度过滤”:
- 时间:
--since、--until、-b - 来源:
-u、--user-unit、-k、-t - 级别:
-p - 内容:
-g - 字段:
FIELD=VALUE - 输出:
-o short-iso、-o cat、-o json
排查服务问题时,最常用的组合是:
bash
systemctl status myapp.service
journalctl -xeu myapp.service -b --no-pager
journalctl -u myapp.service -b -p err --no-pager如果日志重启后消失,先检查 journal 是否持久化;如果日志太多,先用时间、服务和优先级缩小范围,再考虑 grep/awk/jq。