3. 日志采集:常用采集工具与采集架构设计
3.1 采集层在日志链路中的位置与核心职责
日志分析的价值始于采集。无论后端的存储引擎多么强大、分析算法多么智能,如果采集层丢失数据、重复上报或时间戳错乱,整个链路都会失真。采集层位于应用服务器与消息队列/存储之间,承担三个核心职责:主动拉取或被动接收日志、解析与结构化、可靠传输。一个设计良好的采集层,应当做到“应用无感知、峰值不丢数、重启可续传”。
采集层的关键指标:采集延迟(秒级以内)、吞吐量(单机每秒处理日志条数)、失败重试率、磁盘缓冲占用。这些指标直接决定了后续链路的数据完整性。
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 的分层架构。
改造步骤:
- 在所有 Nginx 节点部署 Filebeat,配置
filestream输入,关闭 Logstash 采集。 - Kafka 创建
nginx-accessTopic,分区数设为 24(对应 24 个消费并发)。 - Logstash 消费 Kafka,使用 grok 解析 combined 格式,并增加
user_agent字段拆分。 - 输出到 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 分钟。排查步骤:
- 检查 Kafka 消费组 Lag,发现
logstash-nginx组 Lag 持续增长。 - 查看 Logstash 监控,发现 heap 使用率 95%,频繁 Full GC。
- 进一步定位是 grok 正则存在灾难性回溯(catastrophic backtracking),匹配失败时 CPU 被打满。
- 修复:将 grok 表达式中的
.*改为更精确的字符类,并增加break_on_match => false避免重复匹配。
采集层的设计原则可以总结为:轻采集、重缓冲、厚解析。采集端保持轻量,用 Kafka 等消息队列做弹性缓冲,将解析逻辑放在可独立扩展的消费端。这样既能应对流量突刺,又能保证解析逻辑的灵活演进。日志采集不是一锤子买卖,随着业务增长,架构需要具备平滑扩展的能力,而上述分层设计正是为此而生。