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 查看一条日志的所有字段名。
--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 的问题,就是因为每次重启日志都被清空,根本看不到错误栈。
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 强得多。
4.3 知识体系:日志采集的完整链路
下面这张图展示了从日志产生到查询的完整链路:
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 当然查不到。从那以后,我接手任何服务都会先确认日志输出方式。