日志采集基础

日志采集基础架构 标准输出 / 错误流 stdout / stderr 文件落盘 日志轮转 Filebeat Fluentd 中央日志存储 日志从产生到存储的完整链路:应用输出 → 文件管理 → 采集传输 → 集中存储

日志采集是整个 pipeline 的第一步,也是坑最多的一步。我见过太多因为采集配不好,导致线上故障定位慢了半拍的案例。说白了,日志没采好,后面分析再强也没用。这一章我就把采集的四个核心环节拆开来讲,每个环节都附上我实际踩过的坑。

标准输出与标准错误流

每个应用都会产生标准输出(stdout)和标准错误(stderr)。很多人觉得这俩差不多,其实区别大了。stdout 是正常日志,stderr 用来输出异常或错误信息。你想想看,如果所有日志都往 stderr 里写,那监控系统会以为你的应用一直在报错。

我在项目中遇到过一件事:有个团队把业务日志全打到了 stderr,结果监控告警一天响了上百次。等真正出故障的时候,大家反而麻木了,没人去看告警。后来我帮他们把正常日志切到 stdout,错误日志保留在 stderr,告警才恢复正常。

关键点:在容器环境(Docker / Kubernetes)中,kubectl logs 默认合并显示 stdout 和 stderr。但如果你用 --previous 或对接采集工具,两者可以分开处理,千万别混为一谈。

生产环境中我建议这样区分:

  • stdout — 业务逻辑日志、访问日志、常规信息
  • stderr — 异常堆栈、错误码、严重告警

这样做的好处是,采集时可以给 stderr 设置更高的优先级,实时告警,而 stdout 走批量分析。

文件落盘

除了标准流,很多应用还会直接把日志写到文件里。像 Java 的 Logback、Python 的 logging、Nginx 的 access log,都属于文件落盘。文件落盘的优点是持久化强,缺点是管理成本高——你要管路径、权限、磁盘空间。

我曾经因为日志文件权限没配好,Filebeat 读不了日志,排查了整整两个小时。最后发现是 /var/log/app 目录只有 root 能读,而 Filebeat 用 filebeat 用户启动,直接 Permission denied。嗯,从那以后我每次部署都会加一条检查:

# 确保采集用户有读权限
sudo -u filebeat test -r /var/log/app/*.log && echo "OK" || echo "permission issue"

文件落盘时要规划好三件事:

  1. 路径规范 — 统一用 /var/log/<app-name>/,别散落在各处
  2. 命名规则 — 包含应用名 + 日期 + 序号,比如 api-2025-04-01-001.log
  3. 权限管控 — 采集用户至少要能读,建议用同一用户组
注意:千万别把日志写到根目录或系统分区。我曾经见过有人把日志打到 / 下面,磁盘满了直接导致服务器宕机。独立挂载一个日志分区,是最基础的防护。

日志轮转

日志如果一直写不清理,磁盘迟早会爆。轮转(log rotation)就是解决这个问题的——按时间或大小切分旧日志,压缩或删除。Linux 上最常用的工具是 logrotate,我也会在 CI 流水线里配一套统一的轮转策略。

轮转的核心参数其实就这几个:

参数 作用 推荐值
daily / size 轮转触发条件(时间或大小) 每天 或 100MB
rotate 保留旧文件的数量 7 ~ 30
compress 是否压缩旧日志 开启(gzip)
copytruncate 适用于持续写入的文件 建议开启

一个典型的 logrotate 配置长这样:

/var/log/myapp/*.log {
    daily
    rotate 14
    compress
    copytruncate
    missingok
    notifempty
}

我曾经因为把 rotate 设成了 90 天,又没开压缩,结果日志分区被撑爆,生产环境宕了 40 分钟。你知道吗,那一刻所有人盯着我,真是刻骨铭心。从那以后,我强制要求每个项目必须配轮转,并且压缩必须开。

经验之谈:轮转频率要根据日志量动态调整。如果白天流量大、晚上流量小,可以设 size 200M + daily 双重触发。另外,轮转后记得通知采集工具重新打开文件(postrotate 里跑 kill -USR1)。

采集工具:Filebeat 与 Fluentd

日志产生了、落盘了、轮转了,下一步就是把它采集到中央存储。市面上主流的两款工具是 Filebeat 和 Fluentd。我两个都用过,简单说下各自的脾气。

先看对比表格:

特性 Filebeat Fluentd
资源占用 极低(Go 编写) 中等(Ruby/C)
插件生态 较少,但够用 丰富(1000+)
配置难度 简单,YAML 格式 较复杂,config 语法
数据可靠性 内置背压 & 重试 支持 buffer & 故障转移
适用场景 轻量采集,直连 ES / Logstash 复杂路由,多目标输出

我个人习惯这样选:如果只是把日志送到 Elasticsearch,用 Filebeat 就够了,轻量、稳定、几乎零运维。但如果你需要做复杂的数据处理、格式转换,或者同时发往 Kafka、S3 等多个目的地,那 Fluentd 更合适。

Filebeat 的基础配置:

filebeat.inputs:
  - type: log
    enabled: true
    paths:
      - /var/log/myapp/*.log
    fields:
      app: myapp
      env: production

output.elasticsearch:
  hosts: ["http://elasticsearch:9200"]
  index: "myapp-%{+yyyy.MM.dd}"

Fluentd 的基础配置:

<source>
  @type tail
  path /var/log/myapp/*.log
  tag myapp.log
  <parse>
    @type json
  </parse>
</source>

<match myapp.log>
  @type elasticsearch
  host elasticsearch
  port 9200
  logstash_format true
  logstash_prefix myapp
</match>
一个小建议:如果你刚开始搭日志体系,从 Filebeat 入手最稳妥。我早期在项目里直接上 Fluentd,配置调了一周才稳定。后来换 Filebeat,半小时就跑通了。先把链路走通,再考虑要不要换更重的工具。

最后说一句,不管用哪个工具,一定要加「健康检查」和「采集延迟告警」。我曾经有一次 Fluentd 进程挂了三天没人发现,三天日志全丢了。嗯,从那以后我的每个采集节点都配了存活检测 + 延迟告警,超过 5 分钟没新日志就拉群。

核心要点回顾:
• stdout/stderr 分开用途,千万别混用
• 文件落盘统一路径、规范命名、注意权限
• 日志轮转必须配,压缩必须开,保留期视磁盘而定
• Filebeat 轻量简单,Fluentd 灵活强大,按需选择