3. 日志采集:常用采集工具与采集架构设计

3.1 采集层在日志链路中的位置与核心职责

日志分析的价值始于采集。无论后端的存储引擎多么强大、分析算法多么智能,如果采集层丢失数据、重复上报或时间戳错乱,整个链路都会失真。采集层位于应用服务器与消息队列/存储之间,承担三个核心职责:主动拉取或被动接收日志、解析与结构化、可靠传输。一个设计良好的采集层,应当做到“应用无感知、峰值不丢数、重启可续传”。

采集层的关键指标:采集延迟(秒级以内)、吞吐量(单机每秒处理日志条数)、失败重试率、磁盘缓冲占用。这些指标直接决定了后续链路的数据完整性。

应用服务器 Nginx / Java / Python access.log app.log 采集工具层 Filebeat Fluentd Logstash Kafka Topic: app-log Partition: 12 Logstash/ES 消费与索引 多路输入 → 过滤 → 缓冲 → 输出 磁盘缓冲 + 断点续传 至少一次语义

3.2 主流采集工具选型对比

当前生产环境使用最广泛的采集工具集中在 Filebeat、Fluentd 和 Logstash 三者之间。它们并非互斥,很多团队会组合使用。选型时需关注资源占用、插件生态、解析能力和传输可靠性四个维度。

维度 Filebeat Fluentd Logstash
开发语言 Go Ruby / C JRuby
内存占用(典型) 10~30 MB 50~150 MB 300 MB+
日志文件监听 原生支持,自动处理轮转 需 in_tail 插件 需 file 输入插件
内置解析能力 较弱(需配合 ES Ingest) 中等(filter 插件丰富) 强(grok、mutate、date)
传输可靠性 磁盘缓冲 + 背压 内置 buffer 机制 持久化队列
适用场景 轻量采集、大规模部署 容器/微服务、数据路由 复杂解析、集中处理

选型建议:若日志格式简单(如 Nginx 访问日志),优先 Filebeat + Kafka;若涉及多种数据源需要统一路由,Fluentd 的 tag 体系更灵活;若日志格式混乱需要大量 grok 正则解析,Logstash 仍是首选。

3.3 采集架构设计:从单机到分布式

采集架构设计需要回答三个问题:谁采集、采到哪里、如何保证不丢。下面给出两种典型架构。

3.3.1 轻量直连架构(小规模)

适用于日志量小于 50 GB/天、无需复杂路由的场景。Filebeat 直接采集日志并输出到 Elasticsearch 或 Kafka。优点是部署简单、故障点少;缺点是解析能力弱,且如果 Kafka 或 ES 短暂不可用,Filebeat 的磁盘缓冲可能成为瓶颈。

# filebeat.yml 核心配置
filebeat.inputs:
  - type: filestream
    id: nginx-access
    paths:
      - /var/log/nginx/access.log
    parsers:
      - ndjson:
          target: ""

output.kafka:
  hosts: ["kafka1:9092", "kafka2:9092"]
  topic: "nginx-access"
  partition.round_robin:
    reachable_only: true
  required_acks: 1
  compression: gzip
  max_message_bytes: 1000000

3.3.2 分层缓冲架构(中大规模)

当日志量超过 200 GB/天,或需要多团队共享数据时,引入 Kafka 作为缓冲层。采集端只负责“收集+轻解析”,所有复杂处理下沉到消费端。这种架构将采集与消费解耦,Kafka 的分区机制天然支持水平扩展。

架构要点:

  • 每个应用节点部署 Filebeat,只做采集和简单字段提取。
  • Kafka 按日志类型建 Topic,分区数 = max(消费端并发数, 目标吞吐/单分区吞吐)。
  • 消费端(Logstash 或自研 Consumer)负责 grok 解析、GeoIP enrich、单位换算等重逻辑。
  • 若需要实时告警,可从 Kafka 直接接入流处理引擎(如 Flink)。

3.4 实战案例:Nginx 日志采集链路搭建

某电商平台日 PV 约 8000 万,Nginx 日志约 120 GB/天。原先使用 Logstash 直接采集,导致应用服务器 CPU 经常飙到 70%。改造后采用 Filebeat + Kafka + Logstash 的分层架构。

改造步骤:

  1. 在所有 Nginx 节点部署 Filebeat,配置 filestream 输入,关闭 Logstash 采集。
  2. Kafka 创建 nginx-access Topic,分区数设为 24(对应 24 个消费并发)。
  3. Logstash 消费 Kafka,使用 grok 解析 combined 格式,并增加 user_agent 字段拆分。
  4. 输出到 ES 索引 nginx-access-%{+YYYY.MM.dd},每天一个索引便于冷热分离。
# Logstash 消费 Kafka 配置片段
input {
  kafka {
    bootstrap_servers => "kafka1:9092,kafka2:9092"
    topics => ["nginx-access"]
    group_id => "logstash-nginx"
    consumer_threads => 6
    auto_offset_reset => "latest"
  }
}
filter {
  grok {
    match => { "message" => "%{COMBINEDAPACHELOG}" }
  }
  date {
    match => [ "timestamp", "dd/MMM/yyyy:HH:mm:ss Z" ]
    target => "@timestamp"
  }
  useragent {
    source => "agent"
    target => "ua"
  }
}
output {
  elasticsearch {
    hosts => ["es1:9200", "es2:9200"]
    index => "nginx-access-%{+YYYY.MM.dd}"
  }
}

改造后,应用服务器 CPU 降至 15%,日志从产生到可检索的延迟小于 3 秒。高峰期(双11)日志量飙至 400 GB/天,通过增加 Kafka 分区和 Logstash 消费实例即可水平扩展,无需改动采集端。

3.5 常见误区与排障复盘

误区一:采集端做太多解析

在 Filebeat 中写大量正则或 JS 脚本,会导致采集端 CPU 飙升,且 Filebeat 升级时脚本难以维护。正确做法是采集端只做字段提取,复杂解析放到 Logstash 或 ES Ingest Pipeline。

误区二:忽略日志轮转

Linux 下 logrotate 默认会 rename 日志文件。若 Filebeat 未正确配置 ignore_older 和 close_inactive,会导致重复采集或漏采。使用 filestream 类型时,Filebeat 通过 inode 跟踪文件,rename 后会自动处理新文件。

误区三:Kafka 分区数不可变

分区数只能在创建 Topic 时指定或后续增加,不能减少。若前期分区数设置过小(如 6),后期消费端扩容到 12 个实例时,会有 6 个实例空闲。建议初始分区数 = 预估最大消费并发数 × 2。

排障案例:某团队反馈日志延迟从 2 秒涨到 15 分钟。排查步骤:

  1. 检查 Kafka 消费组 Lag,发现 logstash-nginx 组 Lag 持续增长。
  2. 查看 Logstash 监控,发现 heap 使用率 95%,频繁 Full GC。
  3. 进一步定位是 grok 正则存在灾难性回溯(catastrophic backtracking),匹配失败时 CPU 被打满。
  4. 修复:将 grok 表达式中的 .* 改为更精确的字符类,并增加 break_on_match => false 避免重复匹配。

采集层的设计原则可以总结为:轻采集、重缓冲、厚解析。采集端保持轻量,用 Kafka 等消息队列做弹性缓冲,将解析逻辑放在可独立扩展的消费端。这样既能应对流量突刺,又能保证解析逻辑的灵活演进。日志采集不是一锤子买卖,随着业务增长,架构需要具备平滑扩展的能力,而上述分层设计正是为此而生。