journalctl 命令详解与参数实测(Ubuntu 22.04)
以 Ubuntu 22.04(systemd 249)实测逐参数讲解 journalctl:日志格式逐列解读、-u/-t 过滤差异、-f 跟随与 -n/-e 定位、-b 启动过滤、--since/--until 时间窗、-p 优先级、-o 六种输出格式对照、-k 内核、--grep 检索、_PID 字段过滤与持久化机制,附排障实战与常见问题。
journalctl 命令详解与参数实测(Ubuntu 22.04)
目标:全面掌握 journalctl 的每一个常用参数——无参浏览、-u/-t 过滤、-f 跟随、-n/-e 定位、-b 启动过滤、--since/--until 时间窗、-p 优先级、-o 多格式输出、-k 内核消息、--disk-usage、-x 解释行、--grep 检索、_PID= 字段过滤与日志持久化机制,全部命令与回显已在 Ubuntu 22.04(systemd 249)实测通过。
一、环境信息
本文所有命令均在以下环境实测,回显逐字取自真实执行结果:
| 项目 | 值 |
|---|---|
| 操作系统 | Ubuntu 22.04.5 LTS |
| 内核版本 | 6.8.0-138-generic |
| init 系统版本 | systemd 249(249.11-0ubuntu3.23) |
| 主机名 | b8307d66e0c6(回显中主机名部分会原样出现) |
| 系统时区 | UTC(回显中的时间戳均为 UTC 时间) |
| 执行用户 | root |
| 测试数据目录 | /root/demo/journalctl |
| 演示服务 | /etc/systemd/system/demo-svc.service(每 30 秒 logger 写入一条 heartbeat) |
| 日志存储 | /var/log/journal/(持久化模式,详见 3.17 节) |
演示服务单元文件如下(journalctl 的 -u 过滤对象就是它):
$ cat /etc/systemd/system/demo-svc.service
[Unit]
Description=Demo Service for systemctl practice
After=network.target
[Service]
Type=simple
ExecStart=/bin/bash -c 'while :; do logger -t demo-svc "heartbeat"; sleep 30; done'
Restart=on-failure
[Install]
WantedBy=multi-user.target
启动服务并注入测试日志(含普通、warning、error 三种优先级与跟随标记行,供后文各参数实测):
$ systemctl start demo-svc.service
$ logger -t demo-svc "manual inject line A"
$ logger -p warning -t demo-svc "manual inject warning line B"
$ logger -p err -t demo-svc "manual inject error line C"
二、命令概览
2.1 语法与测试数据
journalctl 的语法是「选项 + 匹配项(MATCHES)」。匹配项是形如 FIELD=VALUE 的字段等式(如 _PID=1、_COMM=logger),可叠加多个实现"与"关系过滤;不带匹配项时显示全部日志:
journalctl [OPTIONS...] [MATCHES...]
本文实测围绕两类日志展开:其一是 demo-svc.service 单元的 systemd 生命周期消息(Started/Stopped 等)与 heartbeat 业务日志;其二是用 logger 命令注入的演示日志(line A 为 info 级、line B 为 warning 级、line C 为 err 级)。logger 的 -t 参数决定日志的 SYSLOG_IDENTIFIER(标识符),-p 参数决定优先级,这两者正是 journalctl -t 与 -p 过滤的实测依据。
💡 技术拓展:Linux 系统日志体系不止 systemd-journald 一家,主要方案对比——
方案 存储形态 特点 systemd-journald 二进制索引文件(.journal) 结构化字段、按任意字段过滤、速度快;journalctl 是唯一查看入口 rsyslog 纯文本(/var/log/syslog 等) 传统格式、grep 可读、配置成熟,Ubuntu 默认与 journald 并存转发 syslog-ng 文本/数据库 类 rsyslog,过滤与转发语法不同 filebeat/fluentd 等 转发采集器 不落地本地查询,面向集中式日志平台 Ubuntu 22.04 默认 journald + rsyslog 双轨:journald 负责短期结构化查询,rsyslog 负责文本归档。本文主角是 journald 的查询工具 journalctl。
2.2 --help 全量输出(分段照抄)
journalctl --help 按官方原文分段照抄(原文中 -M --machine 一行面向本机多实例管理,与本文主线无关,省略并在此注明,其余逐字照抄)。
Options(选项):
journalctl [OPTIONS...] [MATCHES...]
Query the journal.
Options:
--system Show the system journal
--user Show the user journal for the current user
-S --since=DATE Show entries not older than the specified date
-U --until=DATE Show entries not newer than the specified date
-c --cursor=CURSOR Show entries starting at the specified cursor
--after-cursor=CURSOR Show entries after the specified cursor
--show-cursor Print the cursor after all the entries
--cursor-file=FILE Show entries after cursor in FILE and update FILE
-b --boot[=ID] Show current boot or the specified boot
--list-boots Show terse information about recorded boots
-k --dmesg Show kernel message log from the current boot
-u --unit=UNIT Show logs from the specified unit
--user-unit=UNIT Show logs from the specified user unit
-t --identifier=STRING Show entries with the specified syslog identifier
-p --priority=RANGE Show entries with the specified priority
--facility=FACILITY... Show entries with the specified facilities
-g --grep=PATTERN Show entries with MESSAGE matching PATTERN
--case-sensitive[=BOOL] Force case sensitive or insensitive matching
-e --pager-end Immediately jump to the end in the pager
-f --follow Follow the journal
-n --lines[=INTEGER] Number of journal entries to show
--no-tail Show all lines, even in follow mode
-r --reverse Show the newest entries first
-o --output=STRING Change journal output mode (short, short-precise,
short-iso, short-iso-precise, short-full,
short-monotonic, short-unix, verbose, export,
json, json-pretty, json-sse, json-seq, cat,
with-unit)
--output-fields=LIST Select fields to print in verbose/export/json modes
--utc Express time in Coordinated Universal Time (UTC)
-x --catalog Add message explanations where available
--no-full Ellipsize fields
-a --all Show all fields, including long and unprintable
-q --quiet Do not show info messages and privilege warning
--no-pager Do not pipe output into a pager
--no-hostname Suppress output of hostname field
-m --merge Show entries from all available journals
-D --directory=PATH Show journal files from directory
--file=PATH Show journal file
--root=ROOT Operate on files below a root directory
--image=IMAGE Operate on files in filesystem image
--namespace=NAMESPACE Show journal data from specified namespace
--interval=TIME Time interval for changing the FSS sealing key
--verify-key=KEY Specify FSS verification key
--force Override of the FSS key pair with --setup-keys
Commands(命令):
Commands:
-h --help Show this help text
--version Show package version
-N --fields List all field names currently used
-F --field=FIELD List all values that a specified field takes
--disk-usage Show total disk usage of all journal files
--vacuum-size=BYTES Reduce disk usage below specified size
--vacuum-files=INT Leave only the specified number of journal files
--vacuum-time=TIME Remove journal files older than specified time
--verify Verify journal file consistency
--sync Synchronize unwritten journal messages to disk
--relinquish-var Stop logging to disk, log to temporary file system
--smart-relinquish-var Similar, but NOP if log directory is on root mount
--flush Flush all journal data from /run into /var
--rotate Request immediate rotation of the journal files
--header Show journal header information
--list-catalog Show all message IDs in the catalog
--dump-catalog Show entries in the message catalog
--update-catalog Update the message catalog database
--setup-keys Generate a new FSS key pair
See the journalctl(1) man page for details.
2.3 版本信息
journalctl 随 systemd 一起发布,版本输出与 systemctl --version 一致:
$ journalctl --version
systemd 249 (249.11-0ubuntu3.23)
+PAM +AUDIT +SELINUX +APPARMOR +IMA +SMACK +SECCOMP +GCRYPT +GNUTLS +OPENSSL +ACL +BLKID +CURL +ELFUTILS +FIDO2 +IDN2 -IDN +IPTC +KMOD +LIBCRYPTSETUP +LIBFDISK +PCRE2 -PWQUALITY -P11KIT -QRENCODE +BZIP2 +LZ4 +XZ +ZLIB +ZSTD -XKBCOMMON +UTMP +SYSVINIT default-hierarchy=unified
第一行是版本号(systemd 249,Ubuntu 修订 249.11-0ubuntu3.23);第二行是编译特性清单,与日志相关的关键项:+PCRE2 表示 --grep 支持完整 PCRE2 正则;+ZSTD/+LZ4/+XZ/+BZIP2 表示 journal 文件支持这些压缩算法;+AUDIT 表示可接收内核审计事件。
三、参数逐项实测
3.1 无参数:全量日志浏览(输出格式逐列解读)
不带任何参数时 journalctl 从最旧到最新输出全部日志。实测全量 2130 行,以下为首部 8 行:
$ journalctl --no-pager | head -8
Oct 15 18:07:39 b8307d66e0c6 kernel: Linux version 6.8.0-138-generic (buildd@lcy02-amd64-008) (x86_64-linux-gnu-gcc-12 (Ubuntu 12.3.0-1ubuntu1~22.04.3) 12.3.0, GNU ld (GNU Binutils for Ubuntu) 2.38) #138~22.04.1-Ubuntu SMP PREEMPT_DYNAMIC Fri Aug 7 13:43:15 UTC (Ubuntu 6.8.0-138.138~22.04.1-generic 6.8.12)
Oct 15 18:07:39 b8307d66e0c6 kernel: Command line: BOOT_IMAGE=/boot/vmlinuz-6.8.0-138-generic root=UUID=43e68207-7df4-4f66-933a-8a595cdde8c8 ro quiet splash
Oct 15 18:07:39 b8307d66e0c6 kernel: KERNEL supported cpus:
Oct 15 18:07:39 b8307d66e0c6 kernel: Intel GenuineIntel
Oct 15 18:07:39 b8307d66e0c6 kernel: AMD AuthenticAMD
Oct 15 18:07:39 b8307d66e0c6 kernel: Hygon HygonGenuine
Oct 15 18:07:39 b8307d66e0c6 kernel: Centaur CentaurHauls
Oct 15 18:07:39 b8307d66e0c6 kernel: zhaoxin Shanghai
$ journalctl --no-pager | wc -l
2130
默认输出格式(short)每行四段,以 Oct 15 21:58:06 b8307d66e0c6 demo-svc[149]: heartbeat 为例:
- 时间:
Oct 15 21:58:06——月 日 时:分:秒,按系统本地时区显示(本机为 UTC;格式精确到秒,需要毫秒用 -o short-precise,见 3.9 节); - 主机名:
b8307d66e0c6——日志产生的主机,集中收集多机日志时可区分来源(可用 --no-hostname 隐藏); - 进程[PID]:
demo-svc[149]——进程标识符与当时的进程号,systemd 自身显示为 systemd[1]; - 消息:冒号后的全部文本。
日志最开头是本次启动的内核消息,最末尾是最新的条目;交互模式下自动进入 less 分页器(G 跳到末尾、q 退出),脚本中必须加 --no-pager。
3.2 -u:按单元过滤
-u 按 systemd 单元名过滤,是按服务查日志的标准方式。demo-svc.service 在 systemctl 全生命周期中的日志(head 截取前 8 行):
$ journalctl -u demo-svc.service --no-pager | head -8
Oct 15 21:58:04 b8307d66e0c6 systemd[1]: Started Demo Service for systemctl practice.
Oct 15 21:58:06 b8307d66e0c6 systemd[1]: Stopping Demo Service for systemctl practice...
Oct 15 21:58:06 b8307d66e0c6 systemd[1]: demo-svc.service: Deactivated successfully.
Oct 15 21:58:06 b8307d66e0c6 systemd[1]: Stopped Demo Service for systemctl practice.
Oct 15 21:58:06 b8307d66e0c6 systemd[1]: Started Demo Service for systemctl practice.
Oct 15 21:58:06 b8307d66e0c6 systemd[1]: Stopping Demo Service for systemctl practice...
Oct 15 21:58:06 b8307d66e0c6 systemd[1]: demo-svc.service: Deactivated successfully.
Oct 15 21:58:06 b8307d66e0c6 systemd[1]: Stopped Demo Service for systemctl practice.
要点:-u 过滤的字段是 _SYSTEMD_UNIT,只要进程由该单元拉起,其 stdout/stderr 与内部 logger 输出都会挂到单元名下;服务名写 demo-svc 与 demo-svc.service 等价。注意单元的生命周期消息(Started/Stopped)由 systemd[1] 产生,也归属该单元——所以 -u 的结果里混有 systemd 主进程的记录,这正是排障时想看的。
3.3 -n:只看最近 N 行
-n 限制输出的条数,默认 10。实测 -n 3 与 -n 10(demo-svc 最近 3 条与最近 10 条):
$ journalctl -u demo-svc.service -n 3 --no-pager
Oct 15 22:04:18 b8307d66e0c6 systemd[1]: demo-svc.service: Deactivated successfully.
Oct 15 22:04:18 b8307d66e0c6 systemd[1]: Stopped Demo Service for systemctl practice.
Oct 15 22:08:18 b8307d66e0c6 systemd[1]: Started Demo Service for systemctl practice.
$ journalctl -u demo-svc.service -n 10 --no-pager
Oct 15 21:58:45 b8307d66e0c6 systemd[1]: Started Demo Service for systemctl practice.
Oct 15 21:58:45 b8307d66e0c6 systemd[1]: demo-svc.service: Current command vanished from the unit file, execution of the command list won't be resumed.
Oct 15 21:58:45 b8307d66e0c6 systemd[1]: Stopping Demo Service for systemctl practice...
Oct 15 21:58:45 b8307d66e0c6 systemd[1]: demo-svc.service: Deactivated successfully.
Oct 15 21:58:45 b8307d66e0c6 systemd[1]: Stopped Demo Service for systemctl practice.
Oct 15 22:04:16 b8307d66e0c6 systemd[1]: Started Demo Service for systemctl practice.
Oct 15 22:04:18 b8307d66e0c6 systemd[1]: Stopping Demo Service for systemctl practice...
Oct 15 22:04:18 b8307d66e0c6 systemd[1]: demo-svc.service: Deactivated successfully.
Oct 15 22:04:18 b8307d66e0c6 systemd[1]: Stopped Demo Service for systemctl practice.
Oct 15 22:08:18 b8307d66e0c6 systemd[1]: Started Demo Service for systemctl practice.
-n 等价于"日志的 tail",先过滤后取尾。-n 1 常用于取最新一条做脚本判断(如确认服务最后的消息)。
3.4 -e:跳到日志末尾
-e 的本意是"在分页器中直接跳到末尾"(--pager-end),交互查看大日志时省去按 G。配合 --no-pager 时其效果是输出尾部内容(systemd 249 中 -e 隐含较大的行数窗口),取尾部 5 行展示:
$ journalctl -e -u demo-svc.service --no-pager | tail -5
Oct 15 22:04:16 b8307d66e0c6 systemd[1]: Started Demo Service for systemctl practice.
Oct 15 22:04:18 b8307d66e0c6 systemd[1]: Stopping Demo Service for systemctl practice...
Oct 15 22:04:18 b8307d66e0c6 systemd[1]: demo-svc.service: Deactivated successfully.
Oct 15 22:04:18 b8307d66e0c6 systemd[1]: Stopped Demo Service for systemctl practice.
Oct 15 22:08:18 b8307d66e0c6 systemd[1]: Started Demo Service for systemctl practice.
交互用法 journalctl -e -u nginx.service 直接定位到该单元最新一条日志,是"刚出了问题赶紧看一眼"的最快路径。
3.5 -f:实时跟随(tail -f 的日志版)
-f 持续等待并显示新到日志,等价于 tail -f。实测:先注入一条标记日志,随后以后台方式延时 3 秒再注入 follow-marker-3,同时用 timeout 8 限时运行 journalctl -f(-t demo-svc 限定标识符,输出自动随 timeout 结束,退出码 124 即 timeout 的标志性返回值):
$ logger -t demo-svc "follow-marker-1"
$ ( sleep 3; logger -t demo-svc "follow-marker-3" ) & timeout 8 journalctl -f -t demo-svc; echo rc=$?
Oct 15 21:58:06 b8307d66e0c6 demo-svc[149]: heartbeat
Oct 15 21:58:06 b8307d66e0c6 demo-svc[208]: heartbeat
Oct 15 21:58:45 b8307d66e0c6 demo-svc[354]: heartbeat
Oct 15 22:04:16 b8307d66e0c6 demo-svc[448]: heartbeat
Oct 15 22:08:18 b8307d66e0c6 demo-svc[565]: heartbeat
Oct 15 22:08:20 b8307d66e0c6 demo-svc[567]: manual inject line A
Oct 15 22:08:20 b8307d66e0c6 demo-svc[568]: manual inject warning line B
Oct 15 22:08:20 b8307d66e0c6 demo-svc[569]: manual inject error line C
Oct 15 22:08:20 b8307d66e0c6 demo-svc[580]: follow-marker-1
Oct 15 22:08:22 b8307d66e0c6 demo-svc[585]: follow-marker-2
Oct 15 22:10:20 b8307d66e0c6 demo-svc[656]: follow-marker-3
rc=124
解读:启动时先显示尾部历史(默认最近 10 条),随后进入跟随;3 秒后注入的 follow-marker-3(22:10:20)被实时显示;8 秒时 timeout 发送终止信号,journalctl 退出,rc=124 证实自动退出。生产上监控某服务用 journalctl -f -u nginx.service,Ctrl+C 结束;脚本化采集用 timeout 包裹,并注意 -f 会一直占用会话。
3.6 -b:按启动过滤(本次启动 / 上次启动 / --list-boots)
-b 只看本次开机以来的日志(等价于老习惯里的 /var/log/boot 之后的日志),输出同样从内核消息开始:
$ journalctl -b --no-pager | head -5
Oct 15 18:07:39 b8307d66e0c6 kernel: Linux version 6.8.0-138-generic (buildd@lcy02-amd64-008) (x86_64-linux-gnu-gcc-12 (Ubuntu 12.3.0-1ubuntu1~22.04.3) 12.3.0, GNU ld (GNU Binutils for Ubuntu) 2.38) #138~22.04.1-Ubuntu SMP PREEMPT_DYNAMIC Fri Aug 7 13:43:15 UTC (Ubuntu 6.8.0-138.138~22.04.1-generic 6.8.12)
Oct 15 18:07:39 b8307d66e0c6 kernel: Command line: BOOT_IMAGE=/boot/vmlinuz-6.8.0-138-generic root=UUID=43e68207-7df4-4f66-933a-8a595cdde8c8 ro quiet splash
Oct 15 18:07:39 b8307d66e0c6 kernel: KERNEL supported cpus:
Oct 15 18:07:39 b8307d66e0c6 kernel: Intel GenuineIntel
Oct 15 18:07:39 b8307d66e0c6 kernel: AMD AuthenticAMD
--list-boots 列出 journal 中记录的全部启动(首列是启动序号,-1 即上一次启动):
$ journalctl --list-boots --no-pager
0 0966f4c59afb485aa77e9a9dca350fe8 Thu 2026-10-15 18:07:39 UTC—Thu 2026-10-15 22:08:22 UTC
本机 journal 只记录了 1 次启动(序号 0),因此查"上次启动"的日志如实报错——这也是新装机器/日志刚启用持久化时的典型形态:
$ journalctl -b -1 --no-pager; echo rc=$?
Data from the specified boot (-1) is not available: No such boot ID in journal
rc=1
要点:-b 0 本次、-b -1 上次、-b -2 上上次;不带编号的 -b 等价 -b 0。排障场景"服务器半夜重启了"直接 journalctl -b -1 -p err 看上次启动中的错误。
3.7 --since / --until:时间窗口过滤
--since 与 --until 接受人类可读的相对时间与绝对时间两种写法。相对写法实测("10 min ago" 起):
$ journalctl --since "10 min ago" --no-pager | head -5
Oct 15 21:58:45 b8307d66e0c6 systemd[1]: Reloading.
Oct 15 21:58:45 b8307d66e0c6 systemd[1]: Started Demo Service for systemctl practice.
Oct 15 21:58:45 b8307d66e0c6 demo-svc[354]: heartbeat
Oct 15 21:58:45 b8307d66e0c6 systemd[1]: Reloading.
Oct 15 21:58:45 b8307d66e0c6 systemd[1]: demo-svc.service: Current command vanished from the unit file, execution of the command list won't be resumed.
绝对时间写法实测(用 date 生成"5 分钟前"到"现在"的窗口,两个边界都是 YYYY-MM-DD HH:MM:SS 格式):
$ journalctl --since "$(date -d '5 min ago' '+%Y-%m-%d %H:%M:%S')" --until "$(date '+%Y-%m-%d %H:%M:%S')" --no-pager | head -5
Oct 15 22:04:16 b8307d66e0c6 systemd[1]: Reloading.
Oct 15 22:04:16 b8307d66e0c6 systemd[1]: Started Demo Service for systemctl practice.
Oct 15 22:04:16 b8307d66e0c6 demo-svc[448]: heartbeat
Oct 15 22:04:18 b8307d66e0c6 systemd[1]: Reloading.
Oct 15 22:04:18 b8307d66e0c6 systemd[1]: Stopping Demo Service for systemctl practice...
要点:相对时间支持 "10 min ago"、"today"、"yesterday"、"-1h" 等;绝对时间除上格式外还接受 "2026-10-15 22:00"(省略秒)与 "22:00"(当天)。注意 --since/--until 按本地时区解释,跨时区核对时加 --utc 或先 timedatectl 确认时区。
3.8 -p:按优先级过滤(配合 logger -p 注入实测)
-p 按日志优先级(emerg=0 到 debug=7)过滤,支持单个级别或范围。先用 logger 注入三种级别的演示日志(见 2.1 节),再实测过滤。-p err(即 3 级及以上:err/alert/emerg)只命中 error line C:
$ journalctl -p err -t demo-svc --no-pager
Oct 15 22:08:20 b8307d66e0c6 demo-svc[569]: manual inject error line C
-p warning(4 级及以上:warning/err/alert/emerg)命中 warning line B 与 error line C 两条,体现"范围"语义:
$ journalctl -p warning -t demo-svc --no-pager
Oct 15 22:08:20 b8307d66e0c6 demo-svc[568]: manual inject warning line B
Oct 15 22:08:20 b8307d66e0c6 demo-svc[569]: manual inject error line C
要点:-p 的值可用名称(err)或数字(3);范围写法如 -p warning..err 表示"warning 到 err 之间";巡检常用组合 journalctl -p err -b(本次启动的所有错误)。
💡 技术拓展:日志优先级沿用 syslog 八级体系,排查时由高到低逐级缩小——
数字 名称 典型场景 0 emerg 系统不可用 1 alert 必须立即处理 2 crit 关键条件(如硬件故障) 3 err 错误,功能受损 4 warning 警告,暂不影响运行 5 notice 正常但值得注意 6 info 一般信息 7 debug 调试细节 生产巡检看 0~3(-p err),变更验证看 4(-p warning..warning)。
3.9 -o:输出格式对照(同一条日志六种形态)
-o 改变输出格式。取同一条日志(demo-svc 最近 1 条 systemd 消息)分别以 short、short-precise、short-iso 输出,可见差异集中在时间戳上:
$ journalctl -u demo-svc.service -n 1 --no-pager -o short
Oct 15 22:08:18 b8307d66e0c6 systemd[1]: Started Demo Service for systemctl practice.
$ journalctl -u demo-svc.service -n 1 --no-pager -o short-precise
Oct 15 22:08:18.930199 b8307d66e0c6 systemd[1]: Started Demo Service for systemctl practice.
$ journalctl -u demo-svc.service -n 1 --no-pager -o short-iso
2026-10-15T22:08:18+0000 b8307d66e0c6 systemd[1]: Started Demo Service for systemctl practice.
-o verbose 展开该条日志的全部结构化字段(节选自实测原文,首行方括号内是日志的唯一游标 cursor):
$ journalctl -u demo-svc.service -n 1 --no-pager -o verbose
Thu 2026-10-15 22:08:18.930199 UTC [s=e3a156fd0bbe489b948eed94f314f1ac;i=84e;b=0966f4c59afb485aa77e9a9dca350fe8;m=36ebb86b5;t=65de849020a27;x=1a2b917048e84df0]
_BOOT_ID=0966f4c59afb485aa77e9a9dca350fe8
_MACHINE_ID=45348b0cff2d47aaadcaa755a14a8987
_HOSTNAME=b8307d66e0c6
PRIORITY=6
SYSLOG_FACILITY=3
SYSLOG_IDENTIFIER=systemd
_UID=0
_GID=0
_SELINUX_CONTEXT=unconfined
_TRANSPORT=journal
TID=1
CODE_FILE=src/core/job.c
JOB_TYPE=start
_PID=1
_COMM=systemd
_EXE=/usr/lib/systemd/systemd
_CMDLINE=/lib/systemd/systemd
_CAP_EFFECTIVE=1ffffffffff
_SYSTEMD_CGROUP=/init.scope
_SYSTEMD_UNIT=init.scope
_SYSTEMD_SLICE=-.slice
CODE_LINE=713
CODE_FUNC=job_emit_done_message
JOB_RESULT=done
MESSAGE_ID=39f53479d3a045ac8e11786248231fbf
MESSAGE=Started Demo Service for systemctl practice.
UNIT=demo-svc.service
JOB_ID=677
INVOCATION_ID=c65439dd4dd94f35a2e7562b5d9c3191
_SOURCE_REALTIME_TIMESTAMP=1792102098930199
(输出节选说明:verbose 为单条日志的完整字段集,以上即实测全部字段。)
-o json 输出单行 JSON(机器可读,供程序解析);-o json-pretty 输出缩进美化的多行 JSON,两者内容一致:
$ journalctl -u demo-svc.service -n 1 --no-pager -o json
{"JOB_TYPE":"start","CODE_FILE":"src/core/job.c","__REALTIME_TIMESTAMP":"1792102098930215","_SYSTEMD_CGROUP":"/init.scope","SYSLOG_IDENTIFIER":"systemd","INVOCATION_ID":"c65439dd4dd94f35a2e7562b5d9c3191","_COMM":"systemd","PRIORITY":"6","_HOSTNAME":"b8307d66e0c6","_SYSTEMD_SLICE":"-.slice","CODE_FUNC":"job_emit_done_message","_MACHINE_ID":"45348b0cff2d47aaadcaa755a14a8987","__CURSOR":"s=e3a156fd0bbe489b948eed94f314f1ac;i=84e;b=0966f4c59afb485aa77e9a9dca350fe8;m=36ebb86b5;t=65de849020a27;x=1a2b917048e84df0","_PID":"1","_SOURCE_REALTIME_TIMESTAMP":"1792102098930199","CODE_LINE":"713","__MONOTONIC_TIMESTAMP":"14742685365","_SELINUX_CONTEXT":"unconfined\n","_EXE":"/usr/lib/systemd/systemd","MESSAGE_ID":"39f53479d3a045ac8e11786248231fbf","UNIT":"demo-svc.service","JOB_ID":"677","_GID":"0","TID":"1","_TRANSPORT":"journal","MESSAGE":"Started Demo Service for systemctl practice.","_SYSTEMD_UNIT":"init.scope","_CMDLINE":"/lib/systemd/systemd","SYSLOG_FACILITY":"3","_BOOT_ID":"0966f4c59afb485aa77e9a9dca350fe8","_CAP_EFFECTIVE":"1ffffffffff","JOB_RESULT":"done","_UID":"0"}
$ journalctl -u demo-svc.service -n 1 --no-pager -o json-pretty
{
"UNIT" : "demo-svc.service",
"JOB_ID" : "677",
"_CMDLINE" : "/lib/systemd/systemd",
"CODE_LINE" : "713",
"CODE_FUNC" : "job_emit_done_message",
"MESSAGE_ID" : "39f53479d3a045ac8e11786248231fbf",
"CODE_FILE" : "src/core/job.c",
"JOB_RESULT" : "done",
"_SYSTEMD_SLICE" : "-.slice",
"_MACHINE_ID" : "45348b0cff2d47aaadcaa755a14a8987",
"_GID" : "0",
"_COMM" : "systemd",
"_EXE" : "/usr/lib/systemd/systemd",
"_CAP_EFFECTIVE" : "1ffffffffff",
"_UID" : "0",
"INVOCATION_ID" : "c65439dd4dd94f35a2e7562b5d9c3191",
"_SOURCE_REALTIME_TIMESTAMP" : "1792102098930199",
"__MONOTONIC_TIMESTAMP" : "14742685365",
"_PID" : "1",
"__CURSOR" : "s=e3a156fd0bbe489b948eed94f314f1ac;i=84e;b=0966f4c59afb485aa77e9a9dca350fe8;m=36ebb86b5;t=65de849020a27;x=1a2b917048e84df0",
"__REALTIME_TIMESTAMP" : "1792102098930215",
"_SYSTEMD_CGROUP" : "/init.scope",
"MESSAGE" : "Started Demo Service for systemctl practice.",
"_HOSTNAME" : "b8307d66e0c6",
"_SELINUX_CONTEXT" : "unconfined\n",
"_BOOT_ID" : "0966f4c59afb485aa77e9a9dca350fe8",
"SYSLOG_IDENTIFIER" : "systemd",
"_TRANSPORT" : "journal",
"TID" : "1",
"SYSLOG_FACILITY" : "3",
"JOB_TYPE" : "start",
"_SYSTEMD_UNIT" : "init.scope",
"PRIORITY" : "6"
}
选型建议:人工阅读用 short(默认)或 verbose;需要毫秒级时序分析用 short-precise;跨机归档、脚本字段提取用 short-iso(时间可排序);对接日志平台/程序解析用 json。verbose/json 里的字段名(如 _PID、SYSLOG_IDENTIFIER)正是 3.14/3.15 节字段过滤的依据。
3.10 -k:只看内核消息
-k(--dmesg)等价于 dmesg 的 journal 版,只显示本次启动的内核日志。实测首部 8 行与总行数:
$ journalctl -k --no-pager | head -8
Oct 15 18:07:39 b8307d66e0c6 kernel: Linux version 6.8.0-138-generic (buildd@lcy02-amd64-008) (x86_64-linux-gnu-gcc-12 (Ubuntu 12.3.0-1ubuntu1~22.04.3) 12.3.0, GNU ld (GNU Binutils for Ubuntu) 2.38) #138~22.04.1-Ubuntu SMP PREEMPT_DYNAMIC Fri Aug 7 13:43:15 UTC (Ubuntu 6.8.0-138.138~22.04.1-generic 6.8.12)
Oct 15 18:07:39 b8307d66e0c6 kernel: Command line: BOOT_IMAGE=/boot/vmlinuz-6.8.0-138-generic root=UUID=43e68207-7df4-4f66-933a-8a595cdde8c8 ro quiet splash
Oct 15 18:07:39 b8307d66e0c6 kernel: KERNEL supported cpus:
Oct 15 18:07:39 b8307d66e0c6 kernel: Intel GenuineIntel
Oct 15 18:07:39 b8307d66e0c6 kernel: AMD AuthenticAMD
Oct 15 18:07:39 b8307d66e0c6 kernel: Hygon HygonGenuine
Oct 15 18:07:39 b8307d66e0c6 kernel: Centaur CentaurHauls
Oct 15 18:07:39 b8307d66e0c6 kernel: zhaoxin Shanghai
$ journalctl -k --no-pager | wc -l
1977
排查硬件/OOM/驱动问题看这里:journalctl -k -p err 只看内核错误,journalctl -k | grep -i oom 找内存不足记录。
3.11 --disk-usage:查看日志占用空间
$ journalctl --disk-usage
Archived and active journals take up 8.0M in the file system.
本机全部 journal 文件(归档 + 活动文件)共占 8.0M。与之配套的三个清理参数(--vacuum-size/--vacuum-files/--vacuum-time)可将占用压回限额,例如 journalctl --vacuum-size=100M 只保留最新 100M;更常规的做法是在 /etc/systemd/journald.conf 里设 SystemMaxUse 永久限额后重启 journald。
3.12 -x:附加目录解释行
-x(--catalog)对有官方"消息目录"条目的日志附加解释(以 ░░ 开头的行)。demo-svc 的 Started 消息实测带出了完整解释块:
$ journalctl -x -u demo-svc.service --no-pager | head -8
Oct 15 21:58:04 b8307d66e0c6 systemd[1]: Started Demo Service for systemctl practice.
░░ Subject: A start job for unit demo-svc.service has finished successfully
░░ Defined-By: systemd
░░ Support: http://www.ubuntu.com/support
░░
░░ A start job for unit demo-svc.service has finished successfully.
░░
░░ The job identifier is 132.
要点:并非所有消息都有目录条目(logger 注入的自定义消息就没有);遇到 systemd 报错(如服务启动失败)时先加 -x 看解释,很多失败场景官方目录里写明了常见原因。
3.13 --grep:按内容正则检索
--grep 用 PCRE2 正则匹配 MESSAGE 字段。实测检索 heartbeat(本服务全部 heartbeat 日志共 9 条,逐字照抄):
$ journalctl --grep=heartbeat --no-pager
Oct 15 21:58:04 b8307d66e0c6 demo-svc[126]: heartbeat
Oct 15 21:58:06 b8307d66e0c6 demo-svc[136]: heartbeat
Oct 15 21:58:06 b8307d66e0c6 demo-svc[140]: heartbeat
Oct 15 21:58:06 b8307d66e0c6 demo-svc[144]: heartbeat
Oct 15 21:58:06 b8307d66e0c6 demo-svc[149]: heartbeat
Oct 15 21:58:06 b8307d66e0c6 demo-svc[208]: heartbeat
Oct 15 21:58:45 b8307d66e0c6 demo-svc[354]: heartbeat
Oct 15 22:04:16 b8307d66e0c6 demo-svc[448]: heartbeat
Oct 15 22:08:18 b8307d66e0c6 demo-svc[565]: heartbeat
含空格的模式用引号包裹,实测匹配三条注入日志:
$ journalctl --grep='manual inject' -t demo-svc --no-pager
Oct 15 22:08:20 b8307d66e0c6 demo-svc[567]: manual inject line A
Oct 15 22:08:20 b8307d66e0c6 demo-svc[568]: manual inject warning line B
Oct 15 22:08:20 b8307d66e0c6 demo-svc[569]: manual inject error line C
两个实测观察需要注意:
其一,大小写控制不是 -i(-i 不是 journalctl 的合法短选项),实测报错:
$ journalctl --grep=HEARTBEAT -i -t demo-svc --no-pager | head -8
journalctl: invalid option -- 'i'
正确做法是 --grep=HEARTBEAT --case-sensitive=no(或写小写正则 heartbeat)。
其二,--grep 与 -u 组合时 systemd 249 的匹配范围可能受限(实测同一数据集下 --grep=heartbeat -u demo-svc.service 仅返回部分条目,而单独 --grep 返回全部 9 条),因此内容检索建议优先单独使用 --grep 或配合 -t 标识符过滤,再用时间参数收窄范围。
3.14 _PID=:精确字段匹配(MATCHES 语法)
MATCHES 是 journal 的字段等式过滤,_PID= 精确匹配进程号。systemd 主进程 PID 恒为 1,实测其日志:
$ journalctl _PID=1 --no-pager | head -4
Oct 15 18:07:39 b8307d66e0c6 systemd[1]: Starting Flush Journal to Persistent Storage...
Oct 15 18:07:39 b8307d66e0c6 systemd[1]: Finished Flush Journal to Persistent Storage.
Oct 15 18:07:39 b8307d66e0c6 systemd[1]: Starting Create Volatile Files and Directories...
Oct 15 18:07:39 b8307d66e0c6 systemd[1]: Finished Create Volatile Files and Directories.
再实测用 show 取出的 demo-svc 主进程 PID 过滤——结果为空,因为该服务的日志全部由其子进程 logger 写出,主进程 bash 本身不产生日志:
$ journalctl _PID=$(systemctl show -p MainPID --value demo-svc.service) --no-pager | head -4
-- No entries --
两个要点:其一,_PID= 匹配的是"日志产生那一刻的进程号",进程重启换号后旧日志不会被新 PID 匹配;其二,多个 MATCHES 并列是"与"关系(如 journalctl _PID=1 -p err 只看 PID 1 的错误),同一字段多次出现是"或"关系(_PID=123 _PID=456 匹配两个 PID)。常用字段还有 _COMM=(进程名)、_EXE=(可执行路径)、_SYSTEMD_UNIT=(单元,即 -u 的底层形式)。
3.15 -t:按 SYSLOG_IDENTIFIER 过滤(与 -u 的区别)
-t 按日志的 SYSLOG_IDENTIFIER(syslog 标识符)过滤。logger -t demo-svc 写出的日志标识符就是 demo-svc,实测前 6 条:
$ journalctl -t demo-svc --no-pager | head -6
Oct 15 21:58:04 b8307d66e0c6 demo-svc[126]: heartbeat
Oct 15 21:58:06 b8307d66e0c6 demo-svc[136]: heartbeat
Oct 15 21:58:06 b8307d66e0c6 demo-svc[140]: heartbeat
Oct 15 21:58:06 b8307d66e0c6 demo-svc[144]: heartbeat
Oct 15 21:58:06 b8307d66e0c6 demo-svc[149]: heartbeat
Oct 15 21:58:06 b8307d66e0c6 demo-svc[208]: heartbeat
对比 -u 的实测结果(3.2 节)可见两者差异:-u demo-svc.service 返回的是单元维度的全部日志(包括 systemd[1] 的 Started/Stopped 生命周期消息),-t demo-svc 只返回应用程序自己写出的业务日志(heartbeat 系列,不含 systemd 消息)。再对照 _COMM=logger(按进程名过滤,展示最近 3 条)可进一步区分:-t 认"声明标识符",_COMM 认"进程名"——本例中标识符是 demo-svc 而进程名是 logger:
$ journalctl _COMM=logger -n 3 --no-pager
Oct 15 22:08:20 b8307d66e0c6 demo-svc[569]: manual inject error line C
Oct 15 22:08:20 b8307d66e0c6 demo-svc[580]: follow-marker-1
Oct 15 22:08:22 b8307d66e0c6 demo-svc[585]: follow-marker-2
经验法则:看"服务整体动态"用 -u,看"应用业务输出"用 -t(前提是应用用 logger -t 或 syslog 接口声明了标识符);systemd 服务的 stdout/stderr 自动归属单元(-u 可见),但不改变 SYSLOG_IDENTIFIER。
3.16 --no-pager:禁用分页器
journalctl 默认把输出送入 less 分页器,脚本与非交互场景必须加 --no-pager(或设置 SYSTEMD_PAGER=cat),否则输出可能被等待输入阻塞。本文所有实测命令均带 --no-pager,效果即输出直接打印、命令立即返回。配合使用的还有 --no-hostname(隐藏主机名列)与 -q(抑制提示信息)。
3.17 日志持久化与存储位置(/var/log/journal 与 Storage=auto)
journal 的存储位置由 journald.conf 的 Storage= 决定,实测当前配置(注释态即默认值 auto):
$ grep -E '^#?Storage' /etc/systemd/journald.conf
#Storage=auto
实测本机 /var/log/journal/ 已存在(含机器 ID 命名的子目录),即处于持久化模式——日志落盘,重启不丢;/run/log/journal 为空目录(易失存储未被使用):
$ ls -la /var/log/journal; echo rc=$?
total 20
drwxr-sr-x+ 1 root systemd-journal 4096 Oct 15 18:07 .
drwxr-xr-x 1 root root 4096 Oct 15 18:06 ..
drwxr-sr-x+ 2 root systemd-journal 4096 Oct 15 18:07 45348b0cff2d47aaadcaa755a14a8987
rc=0
$ ls /run/log/journal
Storage 三种取值机制:auto(默认)——/var/log/journal 存在则持久化、不存在则易失;persistent——强制持久化(目录不存在会自动创建);volatile——强制只写 /run(重启即丢)。开机自启持久化的完整链路:安装时 systemd-journal-flush.service 把 /run 中已有日志刷入 /var。若发现服务器重启后 journalctl -b -1 查不到上次启动,先检查 /var/log/journal 是否存在(本组 3.6 节实测 -b -1 不可用即与 journal 记录范围有关)。
四、实战示例
4.1 场景一:定位服务启动失败的原因
标准链路:-u 锁定单元 → -n 看最近动态 → status 交叉验证。以 demo-svc 的启停周期为例,最近 3 条日志显示服务在 22:04:18 被停止、22:08:18 又被启动:
$ journalctl -u demo-svc.service -n 3 --no-pager
Oct 15 22:04:18 b8307d66e0c6 systemd[1]: demo-svc.service: Deactivated successfully.
Oct 15 22:04:18 b8307d66e0c6 systemd[1]: Stopped Demo Service for systemctl practice.
Oct 15 22:08:18 b8307d66e0c6 systemd[1]: Started Demo Service for systemctl practice.
真实失败场景(如 ExecStart 路径错误)时,同一命令会在 Stopped 之后出现 Failed with result 'exit-code' 与退出码行;再配合 systemctl status <单元> 的 Process 行(见 systemctl 篇 4.2 节实测形态)即可锁定原因。若怀疑配置问题,加 -x 阅读官方解释行(3.12 节实测)。
4.2 场景二:实时监控与限时采集
交互排障直接 journalctl -f -u <单元>;无人值守采集用 timeout 限时(实测 3.5 节:8 秒窗口完整收到历史 10 条 + 实时 1 条,rc=124 退出)。采集指定时间段的增量时,用 --since 指定起点比 -n 更可控:
$ journalctl --since "10 min ago" --no-pager | head -5
Oct 15 21:58:45 b8307d66e0c6 systemd[1]: Reloading.
Oct 15 21:58:45 b8307d66e0c6 systemd[1]: Started Demo Service for systemctl practice.
Oct 15 21:58:45 b8307d66e0c6 demo-svc[354]: heartbeat
Oct 15 21:58:45 b8307d66e0c6 systemd[1]: Reloading.
Oct 15 21:58:45 b8307d66e0c6 systemd[1]: demo-svc.service: Current command vanished from the unit file, execution of the command list won't be resumed.
4.3 场景三:时间窗 + 优先级组合巡检
巡检"最近 10 分钟有没有错误",先 -p err 过滤再由 -t 收窄到目标应用,一次命中 error line C(3.8 节实测组合):
$ journalctl -p err -t demo-svc --no-pager
Oct 15 22:08:20 b8307d66e0c6 demo-svc[569]: manual inject error line C
扩大视角看全系统错误用 journalctl -p err -b --no-pager;报表化导出用 -o short-iso 保证时间可排序。
4.4 场景四:结构化日志供程序消费
把日志转成 JSON 交给脚本解析(-o json 单行 / json-pretty 缩进,完整回显见 3.9 节)。配合 --output-fields 可只输出关心的字段以减小体积:
$ journalctl -u demo-svc.service -n 1 --no-pager -o json --output-fields=MESSAGE,PRIORITY,_PID
输出的 JSON 含 __CURSOR 字段,程序可记录游标并用 --after-cursor 做增量拉取,避免重复消费。
4.5 场景五:磁盘空间紧张时的日志治理
先看占用(实测 8.0M,见 3.11 节),确认由 journal 引起后按需压缩:临时清理 journalctl --vacuum-size=50M 或 --vacuum-time=7d(按时间与体量裁剪归档文件,不影响当前活动文件);长效治理在 /etc/systemd/journald.conf 设 SystemMaxUse=200M 后 systemctl restart systemd-journald。裁剪后再跑 --disk-usage 复核。
五、注意事项与常见问题
Q1:普通用户执行 journalctl 提示无权限或看不到系统日志?
journal 文件属 systemd-journal 组且目录带 + 权限位(3.17 节实测可见 drwxr-sr-x+)。普通用户只能读自己的用户日志,要看系统日志需加入 adm 或 systemd-journal 组:usermod -aG systemd-journal <用户名> 后重新登录。
Q2:服务器重启后日志全没了?
journal 处于易失模式(/var/log/journal 不存在)所致。mkdir -p /var/log/journal && systemctl restart systemd-journald(Storage=auto 下自动切换为持久化),然后 journalctl --flush 把 /run 中现存日志刷入磁盘。判定方法见 3.17 节。
Q3:-f 跟随时刚注入的日志偶尔不显示? 本组实测发现:紧跟在 logger 注入之后启动 -f,初始快照可能恰好取在该条落盘之前,导致该条不出现在历史尾部(3.5 节第一轮实测),加长注入间隔后 follow-marker-3 被正常实时捕获。因此脚本化采集时先启动 -f 再触发业务动作,并留出 1~3 秒余量。
Q4:--grep 加了 -u 后匹配结果变少? systemd 249 实测存在该现象(同一数据集单独 --grep 命中 9 条,与 -u 组合只命中部分,见 3.13 节)。检索时优先单独 --grep 或配 -t,再用 --since/--until 收窄时间窗复核。
Q5:journalctl 显示的时间与预期差 8 小时?
时间列按系统本地时区渲染,本机时区为 UTC 时显示 UTC 时间(本文全部回显即 UTC)。核对时先 timedatectl 确认时区,或加 --utc 强制 UTC 输出;--since/--until 也按本地时区解释。
Q6:日志文件是二进制的,grep 直接搜 /var/log/journal 没结果? journal 是带索引与压缩的二进制格式(编译特性含 LZ4/ZSTD 等),必须经 journalctl 访问:内容检索用 --grep(PCRE2),字段检索用 MATCHES,别对 .journal 文件直接 grep。
六、参考链接
- journalctl(1) 手册(man7.org):https://man7.org/linux/man-pages/man1/journalctl.1.html
- systemd 官方 journalctl 文档:https://www.freedesktop.org/software/systemd/man/journalctl.html
- journald.conf 与存储配置:https://www.freedesktop.org/software/systemd/man/journald.conf.html
- journal 字段说明(_PID、SYSLOG_IDENTIFIER 等):https://www.freedesktop.org/software/systemd/man/systemd.journal-fields.html
- 消息目录(-x 解释行)机制:https://www.freedesktop.org/wiki/Software/systemd/catalog/
- Ubuntu 22.04 systemd 源码包页:https://launchpad.net/ubuntu/+source/systemd