4、日志分析平台:ELK/EFK 架构搭建、日志采集与清洗、日志关联分析
日志这东西,平时没人搭理它,一出问题它就是救命稻草。我在一线摸爬滚打这些年,见过太多系统崩了却找不到根因的惨案。说白了,没有一套好用的日志平台,你就是在黑灯瞎火里修机器。
今天咱们聊聊 ELK 和 EFK。这两个东西本质是一回事,只是把 Elasticsearch 和 Kibana 中间的 Logstash 换成了 Fluentd。我个人更偏爱 EFK,因为 Fluentd 的资源占用更轻,在容器化环境里特别香。
4.1 架构选型:ELK 还是 EFK?
先看一张对比表,你心里就有数了:
| 对比项 | ELK (Logstash) | EFK (Fluentd) |
|---|---|---|
| 资源消耗 | 较高,Java 堆内存大户 | 较低,Ruby + C 插件 |
| 插件生态 | 丰富,但配置复杂 | 极丰富,社区活跃 |
| 数据吞吐 | 单机 3000~5000 条/秒 | 单机 10000+ 条/秒 |
| 配置难度 | 中等,需懂 Grok 正则 | 简单,YAML 配置 |
| 容器化友好度 | 一般,镜像较大 | 优秀,官方 DaemonSet 支持 |
我建议:如果你团队里 Java 功底扎实,选 ELK 没问题。但如果你搞微服务、K8s,直接上 EFK,省心太多。
4.2 日志采集与清洗
采集是第一步,也是最容易翻车的一步。我曾经见过一个项目,日志全量采集,结果 ES 集群直接被打挂。嗯,这里要注意:一定要做限流和过滤。
4.2.1 Filebeat 轻量采集
我个人习惯用 Filebeat 做边缘采集,它只负责读文件、发数据,不干别的。配置很简单:
filebeat.inputs:
- type: log
enabled: true
paths:
- /var/log/app/*.log
multiline.pattern: '^\d{4}-\d{2}-\d{2}'
multiline.negate: true
multiline.match: after
output.elasticsearch:
hosts: ["http://es-cluster:9200"]
index: "app-logs-%{+yyyy.MM.dd}"
看到那个 multiline 配置了吗?这是处理 Java 异常堆栈的关键。没有它,一条堆栈会被拆成几十条日志,你根本没法关联分析。
4.2.2 Logstash 数据清洗
数据到了 Logstash,就该做清洗了。我常用的套路是:
- 删除无用字段:比如
beat.hostname、@version,省空间也省性能 - 时间戳标准化:把各种奇葩时间格式统一成 ISO8601
- IP 地理信息:用 GeoIP 插件把 IP 转成经纬度
举个例子,把 Nginx 日志解析成结构化数据:
filter {
grok {
match => { "message" => "%{COMBINEDAPACHELOG}" }
}
date {
match => [ "timestamp", "dd/MMM/yyyy:HH:mm:ss Z" ]
target => "@timestamp"
}
geoip {
source => "clientip"
target => "geo"
}
mutate {
remove_field => ["message", "beat", "tags"]
}
}
4.3 日志关联分析
日志采集清洗完了,怎么用起来?这才是重头戏。我把它分成三个层次:
4.3.1 单维度检索
最简单的用法。比如查某个用户 ID 的所有操作记录:
GET /app-logs-2024.01.15/_search
{
"query": {
"term": {
"user_id.keyword": "u_123456"
}
}
}
这招在排查用户反馈问题时特别管用。我经常在 Kibana 的 Discover 页面直接搜,几秒钟就能定位到问题。
4.3.2 多维度聚合
单条日志没意义,聚合起来才能看到全貌。比如统计每个 API 的 P99 延迟:
GET /app-logs-*/_search
{
"size": 0,
"aggs": {
"by_api": {
"terms": { "field": "api_path.keyword", "size": 50 },
"aggs": {
"p99": {
"percentiles": {
"field": "response_time",
"percents": [99]
}
}
}
}
}
}
核心思路:日志关联分析的本质,就是把散落在不同服务、不同时间点的日志,通过 trace_id、user_id、order_id 等关联字段串起来。没有关联字段,日志就是一堆废纸。
4.3.3 跨服务链路追踪
这是高阶玩法。微服务架构下,一个请求会经过 A→B→C→D 四个服务。如果每个服务各自打日志,你根本不知道问题出在哪一环。
我建议的做法是:
- 在网关层生成全局唯一的
trace_id - 通过 HTTP Header 或 RPC 上下文透传给下游
- 所有服务打印日志时都带上这个 trace_id
- 在 Kibana 里直接搜 trace_id,就能看到完整的调用链
我曾经帮一个电商团队排查下单超时问题。他们日志里没有 trace_id,我花了整整两天才手工拼出调用链。后来加了 trace_id,同样的排查只需要 5 分钟。
4.4 避坑指南
我曾经踩过的坑,你千万别再踩:
- 索引分片数别设太多:每个分片都是 ES 的一个 Lucene 实例,分片太多会导致集群脑裂。我一般一个索引设 3~5 个分片。
- 日志级别别全开 DEBUG:生产环境开 DEBUG 日志,ES 磁盘一天就写满。我建议只保留 WARN 和 ERROR,DEBUG 日志按需开启。
- 别把日志当数据库用:ES 不是关系型数据库,别做复杂的 JOIN 查询。日志就是日志,查完就删,别想着永久保存。
- 冷热数据分离:近 7 天的日志放 SSD 热节点,7 天前的放 HDD 冷节点。我见过有人把所有数据放 SSD,一个月后磁盘 IO 直接打满。
4.5 总结
日志分析平台这事,说难不难,说简单也不简单。核心就三件事:采得到、洗得净、查得快。
采得到靠 Filebeat 或 Fluentd,洗得净靠 Logstash 的 Grok 和 mutate,查得快靠 ES 的索引设计和聚合查询。这三环缺一不可。
最后送你一句话:没有日志的系统,就像没有黑匣子的飞机。出事了只能靠猜。趁现在系统还稳,赶紧把日志平台搭起来吧。