外观
journalctl 日志
systemd 把服务日志收进 journald,好处是统一时间轴、结构化字段、按服务过滤。坏处是默认只在内存里,重启就没了。
场景
服务起不来、行为异常、想按时间段查日志。journalctl 比在 /var/log 里翻文件快得多。
一、基本用法
bash
journalctl -u order-api -n 100 --no-pager
journalctl -u order-api -f
journalctl -u order-api --since '10 min ago'text
Oct 27 22:11:02 kafka-1 systemd[1]: Started Order API.
Oct 27 22:11:03 kafka-1 java[33120]: 2026-10-27 22:11:03.114 INFO Started in 8.2s
Oct 27 22:14:31 kafka-1 java[33120]: 2026-10-27 22:14:31.882 ERROR Failed to fetch order 88213三个高频组合:
bash
# 跟随 + 只看错误
journalctl -u order-api -f -p err
# 指定时间段
journalctl -u order-api --since '2026-10-27 21:00:00' --until '2026-10-27 22:00:00'
# 本次启动以来
journalctl -u order-api -b二、过滤参数
| 参数 | 作用 |
|---|---|
-u <unit> | 指定服务 |
-f | 跟随 |
-n N | 最近 N 行 |
--since / --until | 时间范围 |
-p <级别> | 优先级过滤 |
-b / -b -1 | 本次 / 上次启动 |
-k | 只看内核日志(等价 dmesg) |
-t <标识> | 按 syslog identifier |
_PID=<pid> | 按进程号 |
-o <格式> | 输出格式 |
优先级从高到低:
text
emerg(0) alert(1) crit(2) err(3) warning(4) notice(5) info(6) debug(7)-p err 表示 err 及以上(0~3)。
输出格式
bash
journalctl -u order-api -o cat -n 5text
2026-10-27 22:11:03.114 INFO Started in 8.2s
2026-10-27 22:14:31.882 ERROR Failed to fetch order 88213| 格式 | 用途 |
|---|---|
short(默认) | 时间 + 主机 + 标识 + 消息 |
cat | 只有消息(脚本里用) |
json / json-pretty | 结构化,含所有字段 |
verbose | 所有字段平铺 |
with-unit | 带 unit 前缀 |
bash
journalctl -u order-api -o json-pretty -n 1json
{
"__CURSOR": "s=8f2c1e9a...",
"__REALTIME_TIMESTAMP": "1761570290314000",
"_PID": "33120",
"_UID": "10001",
"_SYSTEMD_UNIT": "order-api.service",
"PRIORITY": "3",
"MESSAGE": "Failed to fetch order 88213",
"_BOOT_ID": "3d41b7c0..."
}PRIORITY 是数字优先级,_SYSTEMD_UNIT 是所属服务。
三、时间表达式的时区坑
bash
date
journalctl --since '10 min ago'--since 用的是本机时区。如果日志来自容器(容器内是 UTC),你会看到时间戳差 8 小时。
两种情况要分清:
- journald 记录的时间始终带时区,用
--since按本机时区过滤是对的。 - 应用自己打印的时间可能是 UTC(容器没设 TZ),看起来"晚 8 小时"。
排查时加 -o short-iso 看带时区的完整时间:
bash
journalctl -u order-api -o short-iso -n 3text
2026-10-27T22:14:31+0800 kafka-1 java[33120]: 2026-10-27 14:14:31.882 ERROR ...一眼就能看出应用打的是 UTC。
四、持久化:重启后还在吗
默认情况下(/var/log/journal 不存在),journald 只写到 /run/log/journal,重启就清空。
bash
ls -d /var/log/journal 2>/dev/null || echo "未持久化"
journalctl --disk-usagetext
Archived and active journals take up 248.0M in the file system.开启持久化:
bash
mkdir -p /var/log/journal
systemd-tmpfiles --create --prefix /var/log/journal
systemctl restart systemd-journaldini
# /etc/systemd/journald.conf
Storage=persistent
SystemMaxUse=4G
SystemMaxFileSize=200M
MaxRetentionSec=30daybash
systemctl restart systemd-journald
journalctl --disk-usage日志量大的机器要限容
SystemMaxUse 不设时默认是文件系统的 10%。500G 盘就是 50G 日志。生产上显式设成 2~4G,并配 MaxRetentionSec。
清理:
bash
journalctl --vacuum-size=1G
journalctl --vacuum-time=7dtext
Deleted archived journal /var/log/journal/.../system@0005f2c1.journal (200.0M).
Vacuuming done, freed 1.2G of archived journals from /var/log/journal.--vacuum-* 只删已归档的文件,当前正在写的那个不会删。
五、和 rsyslog 的关系
很多发行版同时装了 systemd-journald 和 rsyslog:
- journald 收 stdout/stderr 和内核日志。
- rsyslog 从 journald 转发到
/var/log/messages、/var/log/secure。
所以同一个事件在两个地方都有。查传统日志文件:
bash
tail -f /var/log/messages
grep -i 'failed' /var/log/secure两者都装时,journald.conf 里的 ForwardToSyslog=yes 决定是否转发。
六、结构化查询
bash
# 按可执行文件名
journalctl /usr/local/jdk-17/bin/java -n 20
# 按 PID
journalctl _PID=33120 -n 20
# 列出所有字段
journalctl -u order-api -o verbose -n 1 | head -30
# 按字段值过滤
journalctl _SYSTEMD_UNIT=order-api.service PRIORITY=3 -n 20查内核层面的 OOM:
bash
journalctl -k --since '1 hour ago' | grep -i -E 'oom|killed process'text
Oct 27 21:31:02 kafka-1 kernel: Memory cgroup out of memory: Killed process 31882 (java)七、配合 grep 使用
bash
journalctl -u order-api --since today -o cat | grep -i -E 'error|exception' | tail -30注意 journalctl ... | grep 是先取全部再过滤,日志量大时会慢。能用 -p err 或字段过滤就别用 grep。
bash
# 好:让 journald 过滤
journalctl -u order-api -p err --since today -o cat
# 差:全量输出再 grep
journalctl -u order-api --since today | grep ERROR八、常见场景
服务启动失败
bash
systemctl status order-api --no-pager
journalctl -u order-api -b -n 80 --no-pager看 Failed to start 前后的几行,通常是 ExecStart 路径、权限、端口占用。
查某次重启前发生了什么
bash
journalctl -b -1 -p warning -n 100-b -1 是上一次启动(即崩溃那次)。
追踪某个请求
bash
journalctl -u order-api -f -o cat | grep '88213'九、踩过的坑
| 现象 | 根因 | 处置 |
|---|---|---|
| 重启后日志没了 | 没开持久化 | 建 /var/log/journal + Storage=persistent |
journalctl 输出为空 | 应用写文件不写 stdout | 看应用日志路径 |
| 时间差 8 小时 | 容器 UTC / 应用打 UTC | 设 TZ,用 -o short-iso 看 |
| 磁盘被日志占满 | SystemMaxUse 太大或没设 | journalctl --vacuum-size |
--since '10 min ago' 查不到 | 时区或格式 | 用绝对时间 --since '2026-10-27 21:00:00' |
-p err 查不到 ERROR | 应用日志没带优先级,都算 info | 用 grep ERROR 或改应用日志级别映射 |
-u 找不到服务 | unit 名不对 | systemctl list-units | grep xxx |
| 输出被分页卡住 | 脚本里没 --no-pager | 加 --no-pager |