日志产生源与采集:从哪里来,怎么收
做日志管理的第一件事,就是搞清楚日志到底在哪儿。你想想看,一个中型公司的 IT 环境,少说有几十台服务器、几台网络设备,现在再加上容器集群,日志源五花八门。我最早接手过一个项目,光找日志文件就花了三天——各部门放的路径都不一样,有的在 /var/log/app/,有的在 D:\Logs\,还有的直接写到数据库里。所以这一章,咱们先把“从哪里来”和“怎么收”理清楚。
常见的日志源
日志源按设备类型可以分成三大类。每类的格式、频率、采集方式都不一样,别指望一把抓。
| 设备类型 | 典型设备/实例 | 常见日志内容 | 默认格式 | 采集难度 |
|---|---|---|---|---|
| 服务器 | Linux/Windows 物理机或虚机 | 系统日志(syslog、EventLog)、应用日志(Web、数据库、中间件) | 文本行、JSON、CSV、二进制 | 低 |
| 网络设备 | 路由器、交换机、防火墙、负载均衡 | Syslog(接口状态、流量、安全事件) | RFC 3164/5424 syslog | 中 |
| 容器 | Kubernetes Pod、Docker 容器 | stdout/stderr 日志、容器事件、Node 日志 | JSON(docker log driver) | 较高 |
注意:网络设备日志常以 UDP 发送(丢包不重传),我曾经遇到过防火墙在高峰期丢 40% 的日志,后来改成 TCP 和本地缓存才解决。如果你对完整性要求高,别迷信 UDP。
服务器日志——最传统也最杂
服务器日志我见得最多。Linux 下 /var/log/ 里的东西,Windows 下事件查看器里的记录,还有各种中间件自己写的日志文件。个人习惯是先把所有应用日志标准化——都写成 JSON 格式,至少带上时间戳、级别、服务名、请求 ID。否则采集上来也是一堆乱麻。
这里有个坑:很多开发者喜欢把日志打到 console(标准输出)然后重定向到文件。这在容器时代很常见,但如果你直接采集文件,磁盘满了日志丢了都不知道。嗯,我在一次大促时就吃了这个亏——Java 应用日志只写 stdout,采集器只监控 /app/logs/*.log,结果两套没对上,故障排查晚了半小时。
网络设备日志——UDP 是常态,可靠实现靠方案
网络设备大多支持 RFC 3164 或 5424 的 syslog 协议。默认走 UDP 514 端口,发送即忘,不关心你是否收到。我建议:
- 如果不差带宽,改成 TCP 或 RELP(Reliable Event Logging Protocol)
- 或者用 syslog-ng/rsyslog 的前端做本地缓冲 + 重传
- 防火墙日志量极大(每秒几万条),采集时别忘了做聚合过滤,只保留安全事件和异常流量
我的经验:网络设备的日志时间戳经常不准(设备时钟不同步)。采集后务必在管道里加一步时间戳归一化,我习惯统一转为 UTC 毫秒时间戳,否则跨设备关联时你会疯掉。
容器日志——让采集器适配动态环境
容器环境下,Pod 随时创建销毁,日志文件也随容器生命周期走。Kubernetes 默认把容器 stdout/stderr 写入宿主机的 /var/log/containers/,文件名为 <pod名>_<命名空间>_<容器名>-xxx.log。这种命名虽然长,但包含了足够信息。
我的建议是:
- 如果集群规模小于 50 节点,用 DaemonSet 部署 Filebeat 或 Fluentd,每个节点一个采集器
- 如果节点数多(几百上千),先让容器日志通过 Fluentd 写入 Kafka 缓冲,再通过 Logstash 消费——别让采集器直接打后端存储,你扛不住
- 容器日志的行格式要统一,最好 JSON,用
--log-opt max-size=10m max-file=3控制滚卷,防止单个文件撑爆磁盘
核心逻辑:容器日志采集最关键的是“动态感知”和“标签注入”。采集器自动识别新 Pod,并注入 Pod 名、命名空间、标签等元数据。否则日志到了后端你根本不知道是哪来的。
采集工具三驾马车:Filebeat、Fluentd、Logstash
市面上主流工具就这三家。我用过的优先级是:轻量优先 → Filebeat,复杂管道 → Logstash,统一生态 → Fluentd。下面逐个说。
Filebeat
Elastic 公司出品,Go 语言写,内存占用极低(几十 MB)。我习惯用它做日志的“最后一公里”采集——在每台服务器上只部署 Filebeat,它负责读文件、压缩、加密发送到 Kafka 或 Logstash。
配置很简单,比如 collect 一个 JSON 日志文件:
filebeat.inputs:
- type: log
enabled: true
paths:
- /var/log/app/*.log
json.keys_under_root: true
json.message_key: message
output.kafka:
hosts: ["kafka01:9092", "kafka02:9092"]
topic: "app-log"
Filebeat 有个自带的 harvester 机制,不管文件怎么滚卷(rotation),它都能跟踪 inode 继续读。我曾用它连续跑过一年零故障。
Fluentd
用 Ruby 写的,插件生态超级丰富(官方 1000+ 个)。最大的优势是“统一日志层”——不管你从哪来(syslog、HTTP、tail),都能通过插件转成统一结构,再输出到几十种后端。我见过很多公司用 Fluentd 做 Kubernetes 的默认日志采集器,配合 fluent-plugin-kubernetes 可以自动注入 Pod 标签。
不过说实话,Fluentd 的内存和 CPU 开销比 Filebeat 大不少。如果你的服务器只有 1C2G,我建议别上 Fluentd,选 Filebeat 更合适。
Logstash
ELK 全家桶里的“管道处理”角色。Java 写的,性能一般但功能最强。能做复杂的过滤、转换、富化——正则替换、GeoIP 地址解析、多行合并、时间戳解析等等。我的用法:把 Logstash 放在 Kafka 后面,专门做流式处理,然后输出给 Elasticsearch。
Logstash 有个大问题:吃内存。你想想看,每启动一个 pipeline 就要一个线程,内存轻松上 GB。我一般建议只有处理逻辑复杂时才用 Logstash,简单场景放 Filebeat 手里就行。
| 特性 | Filebeat | Fluentd | Logstash |
|---|---|---|---|
| 语言 | Go | Ruby / C | Java |
| 资源占用 | 极低 | 中等 | 较高 |
| 处理能力 | 弱(仅采集+简单过滤) | 中(丰富插件) | 强(完整 pipeline) |
| 输出目的地 | Elasticsearch、Kafka、Logstash | 几乎所有(插件) | ELK 生态为主 |
| 容器生态 | 有 Helm chart | 官方 Kubernetes 插件 | 有但重 |
| 典型场景 | 轻量采集器 | 统一日志层 | 复杂数据管道 |
采集架构的选择思路
我个人习惯遵循“分层采集”原则:
- 边缘层:每台机器/容器节点部署轻量采集器(Filebeat/Fluentd),只做文件读取、简单字段提取、压缩发送
- 缓冲层:使用 Kafka (或 Redis)做消息队列,削峰填谷,保证日志不丢失
- 处理层:Logstash 或 Fluentd 聚合节点从缓冲层消费,进行过滤、转换、富化
- 存储层:发送到 Elasticsearch、S3、HDFS 等存储系统
下面这张图能帮你快速理解采集链路:
避坑指南与个人习惯
我曾经在一个云原生项目里犯过这样的错:同时用 Filebeat + Logstash 双向传输日志,结果没有做重试和 ack 机制,磁盘故障导致日志永久丢失。从那以后我养成了两个习惯:
- 所有生产环境的采集配置必须加
close_inactive和clean_removed(Filebeat)或flush_interval(Fluentd),防止文件句柄泄漏 - 采集链路中至少有一个持久化缓冲(Kafka 或 Redis),哪怕只是单点测试,也先把消息队列搭好再调后续
另外别忽视日志文件的滚动(rotation)。很多系统默认的 logrotate 配置会把滚动后的日志文件改名、压缩,采集器如果未及时重新打开新文件,会漏数据。Filebeat 的 close_removed 和 harvester_limit 参数就是干这个用的。记得根据日志滚卷频率调整采集器的扫描间隔。
一个小技巧:在采集侧不管用哪个工具,都先做“结构化”处理——把原始日志转成 JSON,然后加一个 @timestamp 字段(采集时间)。这样后面压缩归档时,时间线非常清晰,压缩比也更高(因为可以按天分区)