journalctl 命令详解与参数实测(Ubuntu 22.04)

以 Ubuntu 22.04(systemd 249)实测逐参数讲解 journalctl:日志格式逐列解读、-u/-t 过滤差异、-f 跟随与 -n/-e 定位、-b 启动过滤、--since/--until 时间窗、-p 优先级、-o 六种输出格式对照、-k 内核、--grep 检索、_PID 字段过滤与持久化机制,附排障实战与常见问题。

Linux命令Linux系统UbuntuUbuntu 22.04命令行systemd2026/10/16

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 八级体系,排查时由高到低逐级缩小——

数字名称典型场景
0emerg系统不可用
1alert必须立即处理
2crit关键条件(如硬件故障)
3err错误,功能受损
4warning警告,暂不影响运行
5notice正常但值得注意
6info一般信息
7debug调试细节

生产巡检看 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。

六、参考链接