Tavio's blog Tavio's blog
首页
  • JVM底层原理
  • 邪恶多线程
  • MyBatis底层原理
  • Spring底层原理
  • MySQL的优化之路
  • ClickHouse的高性能
  • Redis的快速查询
  • RabbitMQ的生产
  • Kafka的高吞吐量
  • ES的入门到入坑
  • MySQL自增ID主键空洞
  • 前端实现长整型排序
  • MySQL无感换表
  • Redis延时双删
  • 高并发秒杀优惠卷
  • AOP无侵入式告警
  • 长短链接跳转
  • 订单超时取消
关于
  • 分类
  • 标签
  • 归档
GitHub (opens new window)

Tavio Zhang

努力学习的小码喽
首页
  • JVM底层原理
  • 邪恶多线程
  • MyBatis底层原理
  • Spring底层原理
  • MySQL的优化之路
  • ClickHouse的高性能
  • Redis的快速查询
  • RabbitMQ的生产
  • Kafka的高吞吐量
  • ES的入门到入坑
  • MySQL自增ID主键空洞
  • 前端实现长整型排序
  • MySQL无感换表
  • Redis延时双删
  • 高并发秒杀优惠卷
  • AOP无侵入式告警
  • 长短链接跳转
  • 订单超时取消
关于
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
  • ES CRUD
  • ES分布式架构
  • ES ISR机制
  • ES深度分页查询优化
  • ES数据写入到查询
  • ELK的底层逻辑
    • 一、ELK整体架构:分工明确的"日志流水线"
      • 1.1 核心组件定位
      • 1.2 数据流转链路
    • 二、Logstash:日志的"预处理工厂"
      • 2.1 Input:多源日志采集
      • 2.2 Filter:将非结构化日志转为结构化数据
      • 常用Filter插件
      • 2.3 Output:将结构化数据发送到下游
    • 三、Elasticsearch:日志的"分布式搜索引擎"
      • 3.1 核心特性
      • 3.2 日志存储优化
    • 四、Kibana:日志的"可视化仪表盘"
      • 4.1 核心功能
      • 4.2 实战技巧
    • 五、ELK关键特性与最佳实践
      • 5.1 近实时性:为什么不是"实时"?
      • 5.2 数据可靠性:如何避免日志丢失?
      • 5.3 性能优化建议
    • 总结
  • ES ILM
  • 《Elasticsearch》笔记
Tavio
2025-06-30
目录

ELK的底层逻辑

在分布式系统与微服务架构普及的今天,日志已成为排查问题、监控系统状态的核心依据。但海量非结构化日志(如Nginx访问日志、Java堆栈日志)的处理却面临三大挑战:收集难(日志分散在多台服务器)、分析难(格式混乱无规律)、可视化难(无法直观呈现趋势)。

ELK(Elasticsearch + Logstash + Kibana)正是为解决这些问题而生的开源日志处理方案。它通过组件协同,将日志从原始数据转化为可查询、可分析的结构化信息,最终以图表等形式直观展示,成为企业级日志分析的事实标准。

# 一、ELK整体架构:分工明确的"日志流水线"

ELK的强大源于"专业分工+高效协同",三个组件构成一条完整的日志处理链路,每个环节专注于特定任务,共同完成从日志采集到可视化的全流程。

# 1.1 核心组件定位

  • Logstash:日志预处理中枢
    负责从多源采集日志,进行清洗、转换(如提取关键字段、格式化时间),输出结构化数据。相当于"日志加工厂"。

  • Elasticsearch(ES):分布式存储与检索引擎
    接收结构化数据并建立倒排索引,支持毫秒级全文检索与聚合分析。相当于"日志数据库"。

  • Kibana:数据可视化平台
    提供丰富的图表工具(折线图、饼图、热力图等),通过ES的查询接口将数据转化为直观的监控面板。相当于"日志仪表盘"。

# 1.2 数据流转链路

原始日志(文件/网络/消息队列)→ Logstash(采集→清洗→转换)→ Elasticsearch(存储+索引)→ Kibana(查询+可视化)
1

# 二、Logstash:日志的"预处理工厂"

Logstash通过"管道(Pipeline)"机制实现数据处理,核心流程为Input → Filter → Output,每个环节可通过插件灵活扩展。

# 2.1 Input:多源日志采集

Input插件负责从不同来源抓取日志,需根据场景选择合适的采集方式:

输入源 适用场景 核心配置示例
file 单服务器本地日志(如Nginx日志) path => "/var/log/nginx/access.log"
beats 分布式环境轻量采集(推荐) port => 5044(接收Filebeat数据)
kafka 高吞吐场景(日志峰值流量大) bootstrap_servers => "kafka:9092"
tcp/udp 网络设备日志(如路由器、防火墙) port => 5000

关键配置解析:
以file插件为例,需关注日志读取位置的管理:

input {
  file {
    path => "/var/log/java/app.log"  # 日志文件路径(容器内需映射宿主机路径)
    start_position => "end"          # 从文件末尾开始读取(类似tail -f)
    sincedb_path => "/usr/share/logstash/data/sincedb"  # 记录读取偏移量
    type => "java-log"               # 标记日志类型(用于后续filter区分处理)
    # 处理Java堆栈日志(多行合并)
    codec => multiline {
      pattern => "^%{YEAR:year}-%{MONTHNUM2:month}-%{MONTHDAY:day} %{HOUR:hour}:%{MINUTE:minute}:%{SECOND:second}\.%{INT:millisecond}"
      negate => true                 # 不匹配pattern的行合并到上一行
      what => "previous"
      auto_flush_interval => 3       # 3秒内无新行则强制输出,避免数据滞留
    }
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
  • sincedb作用:记录文件inode、设备号、偏移量,确保Logstash重启后从断点继续读取,避免重复采集。若需每次启动从头读取,可配置sincedb_path => "/dev/null"。
  • multiline codec:解决Java异常堆栈(多行日志)的合并问题,确保一条完整日志被视为一个事件。

# 2.2 Filter:将非结构化日志转为结构化数据

Filter是Logstash的核心,通过插件提取关键信息(如时间戳、日志级别、线程名),将杂乱的日志字符串转化为JSON格式的结构化数据。

# 常用Filter插件

插件 作用 典型场景
grok 正则匹配提取字段 从日志中提取时间、级别、类名等
date 时间格式转换 将日志时间映射为ES的@timestamp
mutate 字段修改(增删改、类型转换) 精简冗余字段、截取类名短名
geoip IP地址解析为地理位置 分析Nginx访问日志的来源地区

实战配置示例(处理Java日志):

filter {
  # 仅处理type为java-log的日志
  if [type] == "java-log" {
    # 1. 提取关键字段(时间戳、线程、级别、类名、日志内容)
    grok {
      match => {
        "message" => [
          # 匹配格式:2025-11-16 10:22:59.032 [main] INFO com.example.ELKApplicationTests - Hello World
          "^%{TIMESTAMP_ISO8601:datetime} \[%{DATA:thread}\] %{LOGLEVEL:level} %{DATA:class} - %{GREEDYDATA:logger}"
        ]
      }
      tag_on_failure => ["_java_parse_failure"]  # 解析失败打标签(便于后续排查)
    }

    # 2. 时间格式转换(映射为ES的@timestamp,指定时区)
    date {
      match => ["datetime", "yyyy-MM-dd HH:mm:ss.SSS"]
      target => "@timestamp"  # ES默认时间字段,用于排序和时间范围查询
      timezone => "Asia/Shanghai"  # 确保时间为北京时间(避免时区偏移)
      remove_field => ["datetime"]  # 转换后删除原始字段,精简数据
    }

    # 3. 字段清洗与优化
    mutate {
      gsub => [ "class", ".*\.", "" ]  # 全类名→短类名(如com.example.Test→Test)
      convert => { "level" => "string" }  # 确保日志级别为字符串类型
      remove_field => ["path", "host", "@version"]  # 删除冗余字段,减少存储
    }
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30

处理效果对比:

  • 原始日志(非结构化):
    2025-11-16 10:22:59.032 [main] INFO com.example.elk.ELKApplicationTests - Hello World

  • 处理后(结构化JSON):

{
  "@timestamp": "2025-11-16T02:22:59.032Z",  // 转换为UTC时间(ES默认存储格式)
  "level": "INFO",
  "class": "ELKApplicationTests",
  "thread": "main",
  "logger": "Hello World",
  "type": "java-log"
}
1
2
3
4
5
6
7
8

# 2.3 Output:将结构化数据发送到下游

Output插件负责将处理后的结构化数据输出到目标存储或中间件,最常见的目标是Elasticsearch,也可输出到Kafka(缓冲)或控制台(调试)。

核心配置示例:

output {
  # 输出到Elasticsearch,按日期拆分索引(便于管理)
  elasticsearch {
    hosts => ["http://elasticsearch:9200"]  # ES集群地址(多个用逗号分隔)
    index => "java-logs-%{+YYYY.MM.dd}"     # 索引名格式:java-logs-2025.11.16
  }
  
  # 调试用:同时输出到控制台(生产环境建议关闭)
  stdout { codec => rubydebug }
}
1
2
3
4
5
6
7
8
9
10

索引命名技巧:按日期拆分索引(如java-logs-YYYY.MM.dd),便于后续按时间范围清理旧数据,减轻ES存储压力。

# 三、Elasticsearch:日志的"分布式搜索引擎"

ES是ELK的存储与检索核心,其分布式架构与倒排索引机制确保了海量日志的高效存储与查询。

# 3.1 核心特性

  • 倒排索引:将日志字段(如level、class)与文档ID映射,支持快速全文检索(如查询"ERROR级别且类名为Test的日志")。
  • 近实时搜索:数据写入后默认1秒可查询(可通过index.refresh_interval调整,牺牲实时性换性能)。
  • 水平扩展:通过分片(Shard)实现数据分布式存储,支持集群动态扩容。

# 3.2 日志存储优化

  • 索引生命周期管理(ILM):自动管理索引从创建到删除的全生命周期,避免存储膨胀。
    示例:7天内为"热索引"(高优先级,支持写入和查询),7天后转为"温索引"(收缩分片、合并段以节省空间),180天后自动删除。
  # 创建ILM策略
  PUT _ilm/policy/java-logs-logstash-policy
  {
    "policy": {
      "phases": {
        "hot": {  // 热阶段:0ms起,高优先级
          "actions": { "set_priority": { "priority": 100 } }
        },
        "warm": {  // 温阶段:7天后,优化存储
          "min_age": "7d",
          "actions": {
            "shrink": { "number_of_shards": 1 },  // 收缩分片(减少资源占用)
            "forcemerge": { "max_num_segments": 1 }  // 合并段(减少文件数)
          }
        },
        "delete": {  // 删除阶段:180天后,自动清理
          "min_age": "180d",
          "actions": { "delete": {} }
        }
      }
    }
  }
  
  # 创建索引模板,绑定ILM策略
  PUT _index_template/java-logs-logstash-template
  {
    "index_patterns": ["java-logs-*"],  // 匹配所有java-logs前缀的索引
    "template": {
      "settings": {
        "number_of_shards": 1,  // 单分片(日志场景通常不需要多分片)
        "number_of_replicas": 0,  // 无副本(非高可用场景节省资源)
        "index.lifecycle.name": "java-logs-logstash-policy"  // 绑定ILM策略
      }
    }
  }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35

# 四、Kibana:日志的"可视化仪表盘"

Kibana是ELK的"门面",通过直观的界面将ES中的日志数据转化为可交互的图表,支持实时监控与问题排查。

# 4.1 核心功能

  • Discover:实时查询日志,支持按字段筛选、时间范围过滤(如查询近1小时的ERROR日志)。
  • Visualize:生成折线图(趋势)、饼图(级别分布)、表格(明细)等可视化图表。
  • Dashboard:组合多个图表为仪表盘(如"系统健康监控面板"),支持自动刷新。

# 4.2 实战技巧

  • 利用筛选器快速定位问题:在Discover页面通过level:ERROR筛选错误日志,结合class字段定位异常类。
  • 保存常用查询:将高频查询(如"登录失败日志")保存为搜索,避免重复配置。
  • 设置告警:当ERROR日志数量超过阈值时,通过Kibana Alerting发送邮件或短信通知。

# 五、ELK关键特性与最佳实践

# 5.1 近实时性:为什么不是"实时"?

ELK日志存在默认1秒延迟,原因是ES的写入机制:数据先写入内存缓冲区,每隔1秒刷新到文件系统缓存(形成可查询的分段),最终异步写入磁盘。若需降低延迟,可减小index.refresh_interval(如500ms),但会增加IO压力。

# 5.2 数据可靠性:如何避免日志丢失?

  • Logstash持久化队列:启用queue.type: persisted,将未处理的事件暂存到磁盘,重启后不丢失。
  # logstash.yml配置
  queue.type: persisted      # 启用磁盘队列(默认内存队列)
  queue.max_bytes: 10gb      # 队列最大容量(避免磁盘占满)
  queue.checkpoint.writes: 1 # 每处理1个事件就 checkpoint(确保不重复)
1
2
3
4
  • Filebeat前置采集:在分布式场景,用Filebeat(轻量、低资源)替代Logstash直接读文件,Filebeat自带断点续传,避免Logstash宕机导致的采集中断。

# 5.3 性能优化建议

  • Logstash:减少复杂filter(如嵌套正则),增加pipeline.workers(工作线程数,建议等于CPU核心数)。
  • Elasticsearch:关闭副本(非高可用场景),定期执行forcemerge减少分段数量。
  • Kibana:避免查询过大时间范围(如超过30天),使用聚合缓存(aggs.cache.enabled: true)。

# 总结

ELK 通过"采集-处理-存储-可视化"的全链路设计,完美解决了分布式系统日志处理的痛点。

编辑 (opens new window)
#ES ELK
上次更新: 2026/01/21, 19:29:14
ES数据写入到查询
ES ILM

← ES数据写入到查询 ES ILM→

最近更新
01
订单超时取消
01-21
02
双 Token 登录
01-21
03
长短链接跳转
01-21
更多文章>
Theme by Vdoing
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式