⏰ 第05章 · journalctl 日志
查看/过滤/服务日志
journalctl 日志¶
服务跑起来了,但它在后台干了什么、报了什么错,你无从知晓。第 3 章我们让服务把 StandardOutput=journal,正是为了让日志统一进 journald。这一章讲怎么把这份日志翻出来、过滤它、跟踪它。
journal 是什么¶
journald 是 systemd 自带的日志守护进程,它把系统里所有服务的 stdout/stderr、内核日志、开机日志统一收进二进制日志库,放在 /var/log/journal/(若配置了持久化)。好处是:
- 统一格式、内置时间戳和来源标签;
- 支持按服务、按时间、按级别、按关键词 索引过滤;
- 自带日志轮转,不会把磁盘吃满;
- 重启后仍是同一份日志(默认已持久化)。
systemctl is-active systemd-journald # 确认 journald 在跑
ls /var/log/journal/ # 持久化日志目录,存在说明存储已开启
易错 日志要跨重启保留,
journald必须配置Storage=persistent(见下文)。临时型容器中可能用Storage=volatile,重启即失——但这台云主机会真实落地/var/log/journal/。
最基本地看:journalctl¶
journalctl # 从最早看到现在,一页页翻(会进 less,q 退出)
journalctl --no-pager # 不分页,一次性输出,适合脚本/复制
journalctl -b # 只看本次开机以来的日志(-b 为 boot)
journalctl -n 50 # 只看最后 50 行
journalctl -x # 附带错误说明,报错时很实用
提示 日志很多时,
journalctl默认进入less分页器。想直接看全部,加--no-pager,或设环境变量SYSTEMD_PAGER=cat。这在粘贴到文档、写脚本时最省事。
按服务过滤:-u¶
-u 是最常用的一条,只看某个 unit 的日志:
journalctl -u bangdream.service # 看 bangdream 的全部日志
journalctl -u bangdream -n 30 # 最后 30 行
journalctl -u bangdream -f # 实时跟随(盯日志)
journalctl -u dsh -u mysql --no-pager # 一次看多个服务
journalctl -u bangdream.service --no-pager -n 8
# Sep 07 20:41:28 srv1952823 systemd[1]: Started bangdream.service - BanG Dream! ...
# Sep 07 20:41:28 srv1952823 node[16643]: ✅ MySQL connected
# Sep 07 20:41:28 srv1952823 node[16643]: 🚀 Server running at http://localhost:3000
按时间过滤¶
journalctl --since "1 hour ago" # 最近一小时
journalctl --since "2026-09-07 20:00" --until "2026-09-07 21:00"
journalctl --since today # 今天
journalctl -u bangdream --since "30 min ago"
journalctl --since "-2h" # 简写:最近 2 小时
--since/--until 接受自然语言或 ISO 时间,排查"昨晚服务是不是崩过"这类问题特别好用。
按级别和关键词过滤¶
journalctl -p err -u bangdream # 只看错误及以上级别(err = error)
journalctl -p warning -n 30 # 警告及以上
journalctl -u bangdream -g "ERROR" # -g 按正则关键词过滤内容
journalctl -u bangdream | grep -i "fail" # 或用 grep
易错
-p err只筛"级别达到 error 的内核/系统日志"。如果服务自己用console.log打印、或 stdout 输出的是文本,它们默认是info级别,-p err会把它们全过滤掉。想看程序正常输出,就别加-p。想区分,靠-u+grep。
实时跟随:-f¶
盯日志不用一遍遍重敲,-f 像 tail -f 一样持续输出新内容:
journalctl -u bangdream -f # Ctrl+C 退出
journalctl -u dsh -f --no-pager
排障时开一个终端 journalctl -f,另一个终端去 restart 服务,能实时看到启动过程每一条输出。
让日志跨重启保留¶
默认 Ubuntu 可能只把日志暂存内存。改成持久化:
# 编辑 /etc/systemd/journald.conf
Storage=persistent
SystemMaxUse=500M # 日志总量上限,防磁盘打满
systemctl restart systemd-journald
提示 生产环境务必限制
SystemMaxUse,否则一个疯了的服务能把/var/log占满导致系统故障。500M~1G 是常见保守值,配合日志轮转一起用。
日志去哪儿了:手把手排一次错¶
场景:你的 node 服务"看起来起来了,但 API 不通"。先别乱重启,看日志:
systemctl status bangdream --no-pager # 1. 看 Active/目录是否正常
journalctl -u bangdream --since "5 min ago" --no-pager # 2. 看最近的启动日志
# 若看到 "Address already in use" —— 端口被占
# 若看到 "Cannot find module ..." —— 依赖缺失
# 若看到 "MySQL connected" 后静默无输出 —— 可能进程仍在,但逻辑没完成
日志能直接告诉你 Exit code 和最后一句报错,比瞎猜快得多。--no-pager + --since 组合是排障黄金搭档。
易错点¶
- 忘记
--no-pager:日志进less,在脚本或非交互环境里会卡住,--no-pager或SYSTEMD_PAGER=cat更稳。 -p err会过滤掉程序正常输出:服务的 stdout 是info级,别用它筛。- 日志可能有敏感信息:登录凭据、webhook URL、token 若打印进了日志,先
grep一遍再决定是否对外分享。 - 日志不持久化:
Storage=volatile或未配置时,重启后日志丢失,排查"昨晚的问题"无从下手,务必Storage=persistent。 /var/log/journal权重:避免用rm直接清日志目录,用journalctl --vacuum-time=1d或修改SystemMaxUse。
相关命令¶
journalctl(全家桶)、journalctl -u/-f/-b/-p/--since、journalctl --vacuum-size/--vacuum-time(清理)、systemctl restart systemd-journald、cat /etc/systemd/journald.conf。