4. 日志采集进阶:journalctl 与 docker logs 实战

日志采集这块,很多同学一开始都只盯着日志文件。但说实话,在真正的生产环境里,系统日志和容器日志才是第一手情报。我见过太多人出了问题先去翻 /var/log/messages,结果发现日志被轮转切割了,关键信息早没了。

这一讲,我们就来啃两块硬骨头:systemd 的 journalctl 和 Docker 的 docker logs。掌握了它们,你排查问题的效率至少翻一倍。

4.1 systemd-journald:系统日志的中央枢纽

先问个问题:你的 Linux 服务器上,谁在统一管理所有系统日志?

答案是 systemd-journald。它从内核、服务、用户进程收集日志,存成二进制格式。好处是结构化、带元数据、查询快。坏处是——嗯,二进制文件一旦损坏,恢复起来很麻烦。我曾经在排查一次内核 panic 时,就因为 journal 文件损坏,不得不从串口日志里手动拼线索。

4.1.1 基本查询:从入门到熟练

最基础的用法,就是直接看所有日志:

journalctl

但这样输出太多,你根本看不完。我建议你养成加参数的习惯:

场景 命令 说明
只看最近 10 条 journalctl -n 10 类似 tail,适合快速扫一眼
实时跟踪 journalctl -f 跟 tail -f 一样,持续输出新日志
按时间范围 journalctl --since "10 min ago" 支持 "yesterday"、"today" 等自然语言
按服务名 journalctl -u nginx.service 只看某个服务的日志,最常用
按优先级 journalctl -p err 只显示 error 及以上级别
我的习惯:每次排查问题,我都会先跑 journalctl -u <服务名> --since "1 hour ago" -p err。先过滤出错误,再根据时间戳去翻上下文。这样不会一开始就被海量信息淹没。

4.1.2 高级过滤:精准定位问题

你以为 journalctl 只能按服务查?它的过滤能力远不止这些。

按优先级过滤:

# 只显示 error 及以上(0-3)
journalctl -p 0..3

# 只显示 warning 及以上(0-4)
journalctl -p warning

优先级从 0(emerg)到 7(debug)。我一般用 -p err 快速定位故障,再用 -p warning 看潜在风险。

按字段过滤:

# 按 PID 查
journalctl _PID=1234

# 按用户查
journalctl _UID=1000

# 按可执行文件路径查
journalctl _COMM=sshd

这些字段来自 journal 的元数据。你可以用 journalctl --output verbose 查看一条日志的所有字段名。

注意:journalctl 默认只显示当前启动周期的日志。想看历史启动的日志,需要加 --boot=-1(上一次启动)或 --list-boots 列出所有启动记录。我踩过这个坑——有一次服务器重启后,我查了半天找不到之前的错误,就是因为忘了指定启动序号。

4.1.3 输出格式:让日志更可读

默认的输出格式是短格式,适合人看。但如果你要导出分析,可以用其他格式:

# JSON 格式,适合程序解析
journalctl -o json

# 详细格式,显示所有字段
journalctl -o verbose

# 只显示消息内容,不显示时间戳等
journalctl -o cat

我个人最常用的是 -o json-pretty,格式化后的 JSON 可读性很好,方便 grep 或 jq 处理。

4.2 docker logs:容器日志的标准化输出

容器日志和系统日志最大的区别是什么?容器日志走的是 标准输出(stdout/stderr),而不是文件。docker logs 就是从这个标准输出流里读取日志的。

4.2.1 基础用法:快速查看容器日志

# 查看最近 50 条
docker logs --tail 50 my_container

# 实时跟踪
docker logs -f my_container

# 带时间戳
docker logs -t my_container

# 只看最近 5 分钟的
docker logs --since 5m my_container

这里有个坑:docker logs 只能看到容器启动后的日志。如果容器频繁重启,之前的日志会被清掉。我曾经排查一个容器不断 CrashLoopBackOff 的问题,就是因为每次重启日志都被清空,根本看不到错误栈。

解决方案:配置 Docker 的日志驱动为 json-file 并设置 max-size 和 max-file,或者使用 journald 驱动把容器日志统一交给 systemd-journald 管理。这样即使容器重启,日志也不会丢。

4.2.2 日志驱动:不止 json-file

Docker 支持多种日志驱动,每种都有适用场景:

驱动 特点 适用场景
json-file 默认驱动,每个容器一个 JSON 文件 单机开发测试
journald 写入 systemd-journald 与系统日志统一管理
syslog 发送到 syslog 服务 已有 syslog 基础设施
fluentd 发送到 fluentd 收集器 集中式日志平台
gelf Graylog Extended Log Format Graylog 用户

我建议生产环境至少用 journald 或 fluentd 驱动。json-file 虽然简单,但日志文件容易撑爆磁盘,而且轮转策略不够灵活。

4.2.3 实战:结合 journalctl 查容器日志

如果你把 Docker 日志驱动设为 journald,那么所有容器日志都会进入 systemd-journald。这时候,你可以用 journalctl 统一查询:

# 查看某个容器的日志
journalctl CONTAINER_NAME=my_container

# 查看某个镜像的日志
journalctl CONTAINER_IMAGE=nginx:latest

# 结合时间过滤
journalctl CONTAINER_NAME=my_container --since "30 min ago" -p err

这样做的好处是:你可以在一个地方查系统日志和容器日志,不用来回切换工具。而且 journalctl 的过滤能力比 docker logs 强得多。

核心思路:把容器日志纳入 systemd-journald 的统一管理,然后用 journalctl 作为唯一的日志查询入口。这样你只需要掌握一个工具,就能搞定系统日志和容器日志。

4.3 知识体系:日志采集的完整链路

下面这张图展示了从日志产生到查询的完整链路:

日志采集链路图 日志采集链路图 系统服务日志 容器标准输出 内核日志 systemd-journald 采集 二进制 Journal 文件 /var/log/journal/ 查询工具:journalctl / docker logs / 第三方平台 说明:容器日志可通过 journald 驱动直接进入 systemd-journald,实现统一采集和查询

4.4 避坑指南:我踩过的那些坑

最后分享几个实战中容易踩的坑:

  • 日志轮转配置:journald 默认日志上限是所在文件系统的 10%。我曾经有一台服务器日志量特别大,结果 journal 文件撑满了 /var 分区,导致系统服务异常。记得配置 /etc/systemd/journald.conf 里的 SystemMaxUse 和 MaxFileSec。
  • 容器日志不持久化:docker logs 默认只保留当前容器的日志。容器删除后日志就没了。我建议要么用 journald 驱动,要么把日志挂载到宿主机目录。
  • 时间同步问题:journalctl 按时间过滤时,如果服务器时间不准,查到的日志可能对不上。确保 NTP 服务正常运行。
  • 权限问题:普通用户可能看不到所有 journal 日志。需要把用户加入 systemd-journal 组,或者用 sudo 执行。
我曾经踩过的坑:有一次排查线上问题,用 journalctl -u nginx.service --since "1 hour ago" 查不到任何日志。折腾了半天才发现,nginx 的日志配置里把 access_log 和 error_log 写到了文件,而不是标准输出。systemd-journald 只采集 stdout/stderr,所以 journalctl 当然查不到。从那以后,我接手任何服务都会先确认日志输出方式。