用 journalctl 把 systemd 日志查清楚:从一次服务起不来说起

举报
yd_51737554 发表于 2026/11/05 10:18:01 2026/11/05
【摘要】 昨晚有台机器上的应用服务反复重启,systemctl status 只甩出几行截断的红字。真正能把时间线摊开的,是 journald 的查询入口 journalctl。这篇把我平时排查时真正会用到的几组命令整理下来,不讲手册目录,只讲「出了问题先敲什么」。 先确认 journald 还在记journalctl 读的是 systemd-journald 落在磁盘上的二进制日志,不是 /var/...

昨晚有台机器上的应用服务反复重启,systemctl status 只甩出几行截断的红字。真正能把时间线摊开的,是 journald 的查询入口 journalctl。这篇把我平时排查时真正会用到的几组命令整理下来,不讲手册目录,只讲「出了问题先敲什么」。

先确认 journald 还在记

journalctl 读的是 systemd-journald 落在磁盘上的二进制日志,不是 /var/log/messages 那种纯文本。先看服务本身是否健康、日志到底有没有持久化:

systemctl is-active systemd-journald
journalctl --disk-usage
cat /etc/systemd/journald.conf

如果 Storage= 是 volatile,日志只在 /run/log/journal,重启就没了。需要跨重启保留时,改成 persistent,并保证 /var/log/journal 存在且属主是 systemd-journal。改完执行:

sudo systemctl restart systemd-journald

磁盘被打满之前,用 SystemMaxUse= 给一个上限,比事后删文件稳妥。

一次启动看不清,就按启动次切开

服务「今早起来又挂了」这种描述,先别在全量日志里翻。--list-boots 列出每次启动的序号和起止时间,0 是当前这次,-1 是上一轮:

journalctl --list-boots
journalctl -b -1 -u myapp.service

-b 把范围锁在某一次启动里,-u 再锁到单个 unit。两者一起用,噪音会少一个数量级。

看某个服务时,我固定的四条

journalctl -u myapp.service -n 200 --no-pager
journalctl -u myapp.service -p err..alert --since "1 hour ago"
journalctl -u myapp.service -f
journalctl -u myapp.service -o json-pretty -n 20

含义分别是:最近 200 行、一小时内 error 及以上、持续跟踪、以及把结构化字段打出来。-o json-pretty 在查「这条日志到底是谁写的」时很有用,_PID、_COMM、SYSLOG_IDENTIFIER、CODE_FILE 都在里面。只想看某个进程号:

journalctl _PID=1234 --since "10 min ago"

优先级从低到高是 debug、info、notice、warning、err、crit、alert、emerg。-p warning 表示 warning 及更严重的;写成 -p err..alert 可以卡一个闭区间,避免 emerg 那种全系统广播把画面刷掉。

时间范围比行数更可靠

线上日志量大时,-n 很容易刚好错过故障点。用绝对时间或相对时间把窗口钉死:

journalctl -u myapp.service --since "2026-11-05 09:00:00" --until "2026-11-05 09:20:00"
journalctl -u nginx.service --since "30 min ago" --no-pager

时间按本地时区解释。机器如果是 UTC,而你按北京时间心算,会整整偏八小时。先 timedatectl 看一眼 Time zone 再下 --since。

内核日志和用户态日志不要混在一个窗口里

网卡掉线、OOM、磁盘 I/O 错误通常在内核环里。-k 等价于 -t kernel 这一路:

journalctl -k -b -p err --no-pager
journalctl -k --grep "Out of memory" --since "today"

--grep 是对 MESSAGE 字段做正则,不是对整行文本文件做 grep。它区分大小写,要忽略大小写时用 --grep 配合 -i 并不总是可用,稳妥写法是把模式写成明确的大小写,或者先 -o cat 导出再在管道里过滤。导出时记得 --no-pager,否则会进 less。

把「服务挂了」和「被人重启」分开

同样是 unit 进入 failed,原因可能是进程自己退出、可能是 OOM killer、也可能是有人 systemctl restart。看 unit 自己的状态变迁:

journalctl -u myapp.service --since "today" | grep -E "Started|Stopped|Failed|Main process exited|killed"
systemctl show myapp.service -p NRestarts -p ExecMainStatus -p Result -p ActiveState

NRestarts 在涨、Result=oom-kill,就别再改应用配置了,先看内存限制:MemoryMax=、cgroup 当前用量,以及同一时刻的 journalctl -k --grep "Killed process"。

权限和常见空结果

普通用户默认只能看自己产生的日志。空输出不一定是「没日志」,经常是没进 systemd-journal 或 adm 组:

id
sudo journalctl -u myapp.service -n 50 --no-pager

另外,-u 后面要写 unit 名而不是二进制名。服务叫 myapp.service,不要写成进程名 myappd,否则结果是空的,而且不报错。不确定名字时:

systemctl list-units --type=service --state=running

日志太多时怎么收

查完问题不代表要一直留着全量。除了 SystemMaxUse=,还可以:

sudo journalctl --vacuum-time=14d
sudo journalctl --vacuum-size=500M

vacuum 只删已归档的旧 journal 文件,不会动当前正在写的那一个。在生产上我更倾向先设 SystemMaxUse=,把 vacuum 留作一次性清理。

一个可复用的排查顺序

  1. timedatectl 确认时区,systemctl is-active systemd-journald 确认日志服务。
  2. journalctl -u <unit> -b -p warning --no-pager 看本次启动里的告警。
  3. 还是看不懂,就 --since / --until 把窗口缩到故障前后十分钟,加 -o json-pretty 看 _PID 和退出码。
  4. 怀疑是系统而不是应用,切到 journalctl -k -b -p err。
  5. 需要留档时,journalctl -u <unit> --since today --no-pager > /tmp/<unit>-$(date +%F).log,不要把含令牌或连接串的行原样贴出去。

journalctl 的价值不在参数多,而在于它和 unit、启动次、优先级、时间窗口是对齐的。先把范围收窄,再读内容,比在几万行里用眼睛搜索快得多。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0)

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。