日志采集基础:采集原理、Agent部署、数据流

日志采集,说白了就是三个步骤:发现 -> 读取 -> 发送。机器上先有日志文件,采集器才知道去读它。采集器读到新内容,再打包发给后面的管道。嗯,就这么简单。但为什么实际排查时总有人搞丢数据?

我个人习惯,在讲复杂知识点前,先把最基础的东西讲透。因为丢失排查的根因,十有八九是埋在了这些“你觉得自己很清楚”的原理里。

采集原理:三大模式

我知道市面上有各种采集器,Filebeat、Fluentd、Logstash、咱们自研的Agent等等。但它们的底层逻辑,归纳起来就三种。

模式 核心原理 典型工具 我最怕的情况
文件 tail 模式 像 tail -f 一样,记录 inode 和偏移量 Filebeat、Fluentd tail input 日志轮转时 offset 没跟上,丢了一个文件的内容
网络监听模式 Agent 作为客户端,监听 TCP/UDP 端口 syslog-ng、Logstash TCP input 网络抖动导致数据包丢失,而且没有重传机制
API 轮询模式 定时调用云平台或应用的 API 拉取日志 阿里云SLS、AWS CloudWatch API 限流或返回空结果,但 Agent 以为“没有日志”

最重要的一件事:无论哪种模式,采集器自身都是有状态的。它必须知道自己读到了哪一行。这个状态一旦丢失,必丢数据。

我在项目里见过有人贪方便,把所有日志都写到同一个文件里。结果日志量一大,单个Agent根本赶不上写入的速度。offset不停地被追,最后那个文件一旦被轮转,Agent还没来得及读完,老文件就被删了。嗯,数据就这么蒸发掉了。

为什么会这样?因为文件 tail 模式依赖 inode 来跟踪文件。轮转后,新文件的 inode 变了。如果 Agent 处理不当,它会认为“这是个新文件”,直接从新文件的开头读。旧文件后半段的内容,就再也找不到了。

Agent 部署模式:集中 vs 分散

我手头有好几个运维老项目,日志格式可谓是“各自为政”。有的写在 /var/log/,有的写在 /app/logs/,甚至还有的直接写数据库。所以部署模式,说白了是解决“怎么把日志接进来”的问题。

我习惯把 Agent 分成三大类:轻量级 Shipper、富特性 Collector 和 全功能 Sidecar。

  • 轻量级 Shipper:只负责读和发,不做解析。像 Filebeat,内存占用小,适合每台机器都装。我建议在边缘节点用这种,坏了就重启,不心疼。
  • 富特性 Collector:能解析、过滤、聚合。像 Logstash,但内存吃得多。我一般只在集中处理节点用,不在边缘装。
  • 全功能 Sidecar:跟容器应用绑定,每个 Pod 一个。适合容器化改造后的场景。我曾经遇到一个坑:如果容器重启太频繁,Sidecar 的 checkpoint 没来得及写回硬盘,一重启状态就丢了。

注意:Agent 不是装得越多越好。一个节点上装两份采集不同日志的 Agent,如果它们同时读同一个文件,就会出现 双读竞争。我见过因为竞争导致日志顺序错乱,下游解析器直接报错。

部署时,你还需要考虑 协议。是用 TCP 还是 UDP?我建议:能 TCP 就别用 UDP。UDP 无状态、无重试,网络稍微拥堵,数据包就丢了。你都不知道它什么时候丢的。有一次我排查丢日志,最后发现是采集器和接收方之间的 UDP 端口被防火墙限流了。

下面是一个典型的 Agent 配置示例(我用 Python 伪代码示范):

# agent_config.py
agent = {
    "mode": "tail",
    "paths": ["/var/log/app/*.log"],
    "encoding": "utf-8",
    "offset_file": "/var/lib/agent/offset.db",  # 核心:偏移量持久化
    "output": {
        "type": "kafka",
        "bootstrap_servers": ["10.0.0.1:9092"],
        "topic": "app-logs",
        "compression": "gzip"
    },
    "retry": {
        "max_retries": 5,
        "backoff_sec": 3
    }
}

小技巧:我一直强调 offset 要写 本地持久化存储。不要只依赖内存。否则 Agent 一重启,它就失忆了。你以为它还在追,其实它已经追丢了十几分钟的数据。

数据流:一次完整的采集旅程

把 Agent 装好,配置写好,数据怎么流动?我得画一张流程图,这样回头看排查思路才清晰。

审计日志采集数据流 日志生成层 Agent采集 输出侧 传输链路 目标端 审计服务消费 常见丢失点在这里: 1. Agent 崩溃后 offset 未持久化 2. 网络缓冲区溢出时丢包 必做: - 每条日志在 Agent 侧打上时间戳 - 开启输出端的 ack 确认

你看,数据从应用产生,到 Agent 采集,再到输出、传输、消费,每个环节都可能出问题。我最关注的其实是 输出侧到传输链路 这一段。因为输出的数据量一旦超过链路带宽,就会触发反压或丢包。

我建议你在搭建时就给每个环节留一个 数据流快照。说白了就是在 Agent 和接收方之间,加个简单的计数心跳。Agent 每发一条日志,就记一个 seq number。接收方收到后,能发现号码是不是跳了。跳了?那中间肯定有丢的。

阶段 流转方向 常见问题
日志写入 应用 → 磁盘 日志缓冲区没刷盘,宕机后丢失
Agent 轮询 磁盘 → Agent 内存 Agent 读太快或太慢,追不上写入速度
输出 Agent 内存 → 网络 输出队列积压,导致 Agent 崩溃
缓冲传输 网络 → Kafka/Redis 网络分区、Kafka leader宕机、写入超时
写入目标 Kafka/Redis → 存储 分区数不一致导致数据倾斜,某些分区累积
消费 存储 → 审计服务 消费者 lag 太大,数据被清理策略自动删除

我刚开始做监控时,也想过“文件系统还能丢?”别不信。我遇到过极端情况:磁盘 I/O 被打满,应用在写日志,但写操作被内核延迟了。Agent 读的时候看到的文件内容不完整,等 I/O 恢复,文件已经变了。这就是读到的日志不完整,或者干脆读到了半行。

总结一下这一章的核心:理解采集原理,是为了知道哪里可能丢;了解 Agent 部署,是为了避免部署层面的坑;掌握数据流,是为了在排查时能快速定位出问题的环节。 你想想看,如果连数据从哪到哪都不清楚,排查丢日志岂不是大海捞针?