3、日志采集与分析:使用Filebeat和Logstash采集搜索引擎日志,通过ELK Stack进行日志分析,定位慢查询和错误日志

搜索引擎跑起来之后,日志就是它的「黑匣子」。

我见过太多团队,线上出了故障,第一反应是「重启试试」。重启完日志一清,啥线索都没了。说实话,这跟销毁犯罪现场没啥区别。

这一章,我们就来聊聊怎么用 Filebeat + Logstash 把搜索引擎的日志乖乖送到 Elasticsearch,再通过 Kibana 把慢查询和错误日志揪出来。

3.1 搜索引擎日志长什么样?

先搞清楚我们要采集什么。搜索引擎的日志,主要分三类:

  • 访问日志:记录了每一次搜索请求,包括查询语句、耗时、返回条数、客户端 IP 等。
  • 慢查询日志:超过预设阈值的查询会被单独记录下来。我一般把阈值设在 500ms。
  • 错误日志:索引损坏、内存溢出、节点宕机……这些信息都在这里。

举个例子,一个典型的搜索引擎访问日志长这样:

2025-03-21 14:23:45.678 [search-thread-12] INFO  SearchHandler - query="深度学习" took=123ms hits=45 shards=6/6

这里面藏着很多信息:查询时间、耗时、命中数、分片状态。嗯,每一段都有价值。

3.2 为什么选 Filebeat + Logstash?

你可能要问:直接用 Logstash 采集不行吗?

行,但没必要。Logstash 是 Java 写的,吃内存比较狠。你想想看,搜索引擎本身就已经很吃资源了,再跑个 Logstash 去 tail 日志,简直是雪上加霜。

Filebeat 就轻量多了。它是 Go 写的,占用资源极小。我习惯在每个搜索引擎节点上部署 Filebeat,只负责采集和转发。Logstash 集中部署,负责解析和过滤。

我的经验:Filebeat 的 CPU 占用通常控制在 1% 以内,内存占用不到 50MB。相比 Logstash,这简直是「蚂蚁和大象」的区别。

3.3 部署 Filebeat:从日志文件到消息队列

先配置 Filebeat。假设搜索引擎日志在 /var/log/search/search.log

filebeat.inputs:
- type: log
  enabled: true
  paths:
    - /var/log/search/*.log
  multiline.pattern: '^\d{4}-\d{2}-\d{2}'
  multiline.negate: true
  multiline.match: after

output.logstash:
  hosts: ["logstash.example.com:5044"]

这里有个坑:搜索引擎的异常堆栈是多行的。如果不做 multiline 配置,一行堆栈会被拆成好几条记录,分析起来非常痛苦。

我曾经因为这个原因,排查一个 OOM 问题花了整整两天。后来才发现,堆栈信息被拆散了,根本连不起来。

注意:multiline 的正则一定要匹配日志行首的时间戳格式。格式不对,合并就会乱。

3.4 Logstash 解析:把日志变成结构化数据

Filebeat 把日志送到 Logstash 后,Logstash 要干三件事:解析、过滤、输出。

先看解析配置:

filter {
  grok {
    match => { "message" => "%{TIMESTAMP_ISO8601:timestamp} \[%{DATA:thread}\] %{LOGLEVEL:loglevel} %{DATA:class} - query=\"%{DATA:query}\" took=%{NUMBER:duration:float}ms hits=%{NUMBER:hits:int} shards=%{NUMBER:shards_success}/%{NUMBER:shards_total}" }
  }

  if [loglevel] == "ERROR" {
    mutate {
      add_tag => ["error_log"]
    }
  }

  if [duration] > 500 {
    mutate {
      add_tag => ["slow_query"]
    }
  }

  date {
    match => ["timestamp", "ISO8601"]
    target => "@timestamp"
  }
}

你看,我用 grok 把一行文本拆成了十几个字段。这样在 Elasticsearch 里就能直接按 duration 排序,按 loglevel 过滤。

说白了,这一步就是把「天书」变成「表格」。

3.5 在 Kibana 里定位慢查询

数据进了 Elasticsearch,接下来就是 Kibana 的表演时间。

我一般会建两个核心视图:

  • 慢查询排行榜:按 duration 降序排列,展示查询语句、耗时、命中数。
  • 错误日志时间线:按 @timestamp 展示 ERROR 级别的日志,看有没有突增。

举个例子,在 Kibana 的 Discover 页面,你可以这样搜:

tags: "slow_query" AND duration > 1000

一秒以上的查询,基本就是「慢查询中的战斗机」了。

我记得有一次,线上搜索突然变慢。打开慢查询排行榜一看,排第一的查询耗时 8 秒。点开一看,查询语句里有个 wildcard 前缀匹配,扫了全索引。嗯,找到元凶了。

3.6 错误日志的告警与聚合

光看还不够,得能告警。Elasticsearch 的 Watcher 或者 ElastAlert 都可以。

我习惯用 ElastAlert,配置简单:

rules:
- name: "搜索引擎错误日志告警"
  type: frequency
  index: search-logs-*
  timeframe:
    minutes: 5
  num_events: 10
  filter:
    - term:
        tags: "error_log"
  alert:
    - "slack"
    - "email"
  slack_webhook_url: "https://hooks.slack.com/xxx"

这个规则的意思是:5 分钟内出现 10 条以上错误日志,就发 Slack 通知。

为什么要设阈值?因为搜索引擎偶尔会有一些可恢复的错误,比如单个分片查询超时重试。如果每条都告警,你会被「告警疲劳」淹没的。

核心思路:日志采集不是「存起来就完事」,而是为了「快速定位问题」。Filebeat 负责轻量采集,Logstash 负责结构化解析,Elasticsearch 负责存储和检索,Kibana 负责可视化和分析。四者缺一不可。

3.7 避坑指南

最后,分享几个我踩过的坑:

  • 时区问题:Filebeat 默认用 UTC,搜索引擎日志可能是本地时间。记得在 Logstash 里用 date 插件统一时区。
  • 日志轮转:搜索引擎日志一般会按天或按大小轮转。Filebeat 的 close_inactive 参数要设得合理,不然轮转后 Filebeat 还占着文件句柄。
  • 索引生命周期:搜索引擎日志量很大,一天几十 GB 很正常。记得配 ILM(索引生命周期管理),超过 30 天的数据自动删除或归档。

嗯,这一章的内容就到这里。日志采集和分析做好了,搜索引擎的「黑匣子」就不再是秘密了。