⏰ 第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。