3. 日志数据采集:日志文件格式解析与采集实战

日志采集,说白了就是运维机器人的「耳朵」。耳朵不好使,后面分析得再漂亮也是白搭。今天咱们就聊聊日志文件那点事儿,以及怎么用 Filebeat 和 Logstash 这对黄金搭档把数据稳稳地收上来。

3.1 日志文件格式解析

我刚开始做运维那会儿,最头疼的就是日志格式五花八门。有的系统吐 JSON,有的吐 CSV,还有的能把多行日志挤在一行里。你想想看,如果连格式都搞不清楚,后面怎么玩?

3.1.1 JSON 格式日志

JSON 格式现在最流行,因为它自带结构。每个字段都有名字,解析起来特别方便。我个人习惯在生产环境里优先推荐 JSON 格式,省心。

{
  "timestamp": "2024-01-15T10:30:00.123Z",
  "level": "ERROR",
  "service": "user-service",
  "message": "数据库连接超时",
  "request_id": "req-abc-123",
  "duration_ms": 5000
}

嗯,这里要注意:JSON 日志虽然好,但字段嵌套太深会拖慢解析速度。我建议最多嵌套两层,再深就该考虑扁平化了。

3.1.2 CSV 格式日志

CSV 格式比较老派,但很多遗留系统还在用。它的优点是体积小,缺点是字段顺序不能乱。

timestamp,level,service,message,duration_ms
2024-01-15T10:30:00.123Z,ERROR,user-service,数据库连接超时,5000

我在项目中遇到过一个问题:CSV 里的某个字段值本身带了逗号,结果解析出来全乱了。后来我们统一要求所有字段用双引号包裹,才算解决。

3.1.3 多行日志

多行日志是最坑的。比如 Java 的异常堆栈,一个错误能吐十几行。如果按行采集,一个完整的错误就被拆得七零八落。

2024-01-15 10:30:00.123 ERROR [user-service] 数据库连接超时
java.sql.SQLTimeoutException: 等待超时
    at com.mysql.jdbc.ConnectionImpl.connect(ConnectionImpl.java:123)
    at com.zaxxer.hikari.pool.PoolBase.connect(PoolBase.java:456)
    ... 5 more

我曾经因为没处理好这个,排查问题时对着散落的日志行看了两个小时,最后发现是同一个错误。从那以后,我处理多行日志就格外小心。

我的经验:处理多行日志的关键是找到「行首标识」。比如 Java 异常堆栈,非首行都以空格或 tab 开头,这就是最好的分割点。

3.2 使用 Filebeat 进行日志采集

Filebeat 是 Elastic 家的轻量级采集器。它体积小、资源占用低,适合部署在每台服务器上。说白了,它就是你的「前线侦察兵」。

3.2.1 安装与配置

安装 Filebeat 很简单,一条命令搞定。但配置才是重头戏。

# filebeat.yml 核心配置
filebeat.inputs:
- type: log
  enabled: true
  paths:
    - /var/log/app/*.log
  # 多行日志处理
  multiline:
    pattern: '^\d{4}-\d{2}-\d{2}'
    negate: true
    match: after

output.elasticsearch:
  hosts: ["localhost:9200"]

这里我重点说一下 multiline 配置。pattern 写的是时间戳的正则,意思是「以日期开头的行才是新日志的开始」。negate: true 表示「不符合这个模式的行」,match: after 表示「把这些行合并到上一行后面」。你看,逻辑其实很简单。

3.2.2 实战中的坑

避坑指南:我曾经在生产环境遇到过 Filebeat 把日志重复采集的问题。原因是日志文件被 logrotate 轮转后,Filebeat 没识别出来,又把旧文件读了一遍。解决方案是开启 clean_removed: trueclean_inactive: 72h

3.3 Logstash 数据预处理

Filebeat 负责采集,Logstash 负责「洗数据」。为什么需要洗?因为原始日志往往很糙,字段名不规范、时间格式不统一、有些字段需要拆分或合并。

3.3.1 核心处理流程

Logstash 的处理流程是经典的「输入 → 过滤 → 输出」管道。我画了一张图,帮你理解这个逻辑。

输入 Filebeat / TCP / 文件 过滤(核心) grok / mutate / date geoip / useragent 输出 Elasticsearch / Kafka Logstash 数据处理管道

3.3.2 常用过滤插件

过滤插件是 Logstash 的灵魂。我挑几个最常用的说说。

插件名 作用 我常用的场景
grok 正则解析非结构化文本 解析 Apache/Nginx 访问日志
mutate 字段增删改、类型转换 把字符串数字转成整数
date 时间格式标准化 把各种奇葩时间格式转成 ISO 8601
geoip IP 地址转地理位置 分析用户来源地域

3.3.3 实战配置示例

下面这个配置,是我在一个电商项目里用过的。它处理的是 Nginx 访问日志,同时做了 IP 地理定位和时间标准化。

input {
  beats {
    port => 5044
  }
}

filter {
  # 解析 Nginx 日志
  grok {
    match => { "message" => "%{COMBINEDAPACHELOG}" }
  }
  
  # 时间标准化
  date {
    match => ["timestamp", "dd/MMM/yyyy:HH:mm:ss Z"]
    target => "@timestamp"
  }
  
  # IP 地理定位
  geoip {
    source => "clientip"
    target => "geo"
  }
  
  # 字段清理
  mutate {
    remove_field => ["message", "path"]
    convert => { "response" => "integer" }
  }
}

output {
  elasticsearch {
    hosts => ["localhost:9200"]
    index => "nginx-access-%{+YYYY.MM.dd}"
  }
}
核心要点:grok 的正则匹配很消耗 CPU。我建议在 grok 里只匹配必要的字段,不要贪多。如果日志量特别大(比如每秒几万条),可以考虑在 Filebeat 层面先做一次简单过滤,减轻 Logstash 的压力。

3.4 整体架构串联

最后,我把这三个组件串起来,看看它们是怎么协同工作的。

应用服务器 产生日志文件 Filebeat 采集 + 简单处理 Logstash 解析 + 过滤 + 转换 Elasticsearch 存储 + 索引 日志采集全链路:从文件到搜索引擎

你看,整个链路其实不复杂。Filebeat 负责「搬砖」,Logstash 负责「精加工」,Elasticsearch 负责「存储」。各司其职,配合默契。

我的建议:如果日志量不大(每天几十 GB),可以跳过 Logstash,让 Filebeat 直接写入 Elasticsearch。但如果日志量很大,或者需要复杂的数据清洗,Logstash 还是必不可少的。我在一个日处理 500GB 日志的项目里,就是靠 Logstash 的过滤管道把数据洗得干干净净。

好了,日志采集这块就聊到这儿。记住一句话:采集是基础,格式是门槛,预处理是灵魂。把这三样搞定了,后面的分析工作才能顺风顺水。


专注资料整理