Skip to content

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 定位原因。

如果要把日志进一步做文本处理,可以配合 grep 和 awk。

日志存在哪里 ​

路径说明
/var/log/journal/持久化日志,重启后保留
/run/log/journal/内存日志,重启后丢失
/etc/systemd/journald.confjournald 配置

默认 Storage=auto:

  • 如果 /var/log/journal 存在且可写,日志持久化
  • 否则日志只保存在 /run/log/journal,重启即丢失

所以“为什么看不到上次启动的日志”通常不是 journalctl 的问题,而是 journal 没有持久化。

权限 ​

journalctl 默认只能看到当前用户有权限查看的日志。查看系统日志通常需要:

  • root
  • systemd-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。

按优先级查看 ​

日志级别:

数字名称含义
0emerg系统不可用
1alert必须立即处理
2crit严重错误
3err错误
4warning警告
5notice正常但重要
6info信息
7debug调试
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_IDENTIFIERsyslog 标识符
SYSLOG_FACILITYfacility
_PID进程 ID
_UID / _GID用户 / 组 ID
_COMM进程名
_EXE可执行文件路径
_CMDLINE完整命令行
_SYSTEMD_UNIT系统 unit
_SYSTEMD_USER_UNIT用户 unit
_SYSTEMD_CGROUPcgroup 路径
_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-isoISO 8601 时间,带时区
short-precise微秒精度
short-monotonic从开机开始计时
short-unixUnix 时间戳
short-full完整日期和时间
verbose所有字段,适合排查
jsonJSON,每行一条
json-pretty格式化 JSON
json-seqJSON 序列
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-journald

Storage 的取值:

值行为
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=10000
  • SystemMaxUse:/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-pager

OOM 通常会看到 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 --all

timer 只负责触发,真正执行的是同名 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-pager

4. -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-iso

8. 输出被 pager 截断 ​

脚本里加 --no-pager;查看长消息加 -a:

bash
journalctl -u myapp -a --no-pager

9. 日志太多,看不到重点 ​

先按服务、启动、优先级、时间缩小范围:

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 的核心是“按维度过滤”:

  1. 时间:--since、--until、-b
  2. 来源:-u、--user-unit、-k、-t
  3. 级别:-p
  4. 内容:-g
  5. 字段:FIELD=VALUE
  6. 输出:-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。

相关文章 ​

参考 ​