Elastic Stack梳理:Logstash Input插件详解与Codec插件应用指南之文件监控、多行日志处理与Kafka集成

Input插件核心机制与文件监控

Input插件基础功能

Input插件指定数据输入源,单个Pipeline可配置多个插件

Logstash支持丰富的数据源接入(如文件、Kafka、标准输入等)

核心插件包括:

  • stdin:标准输入(无专用配置)
  • file:文件读取(支持日志收集)
  • kafka:消息队列集成

1 ) stdin插件详解

作用:从命令行终端实时读取数据流。

通用配置参数(所有Input插件共享):

  • type:自定义事件类型(字符串),用于后续条件过滤(如 web/mysql)。
  • tags:事件标签(数组),辅助Filter阶段的条件判断。
  • add_field:添加自定义字段(哈希类型)。

配置示例:

ruby 复制代码
input {  
  stdin {  
    codec => "plain"  # 原始文本解码  
    type  => "STD"    # 自定义类型  
    tags  => ["test"]  
    add_field => { "key" => "value" }  
  }  
}  
output { stdout { codec => rubydebug } }  # 调试输出  

执行效果(输入 Hello World):

json 复制代码
{  
  "message" => "Hello World",  
  "type"    => "STD",  
  "tags"    => ["test"],  
  "key"     => "value"  
}  
  • 关键作用:type和tags用于后续Filter条件过滤,add_field扩展元数据

2 ) 文件监控(file插件)的核心问题与解决方案

核心问题解决方案:

  • 断点续传:通过 sincedb 文件记录读取位置(路径由 sincedb_path 指定)
  • 实时监控:定时检查文件更新(间隔由 stat_interval 控制,默认1秒)
  • 新增文件发现:定时扫描匹配文件(间隔由 discover_interval 控制,默认15秒)
  • 文件归档处理:基于inode追踪文件,即使重命名仍可完整读取(依赖 logwatch 库)

关键配置参数:

参数 类型 作用
path 数组 监听文件路径(支持Glob语法)
exclude 数组 排除文件规则
start_position 枚举 beginning(从头读)/end(只读新内容)
ignore_older 数值 忽略超过设定时长(秒)未更新的文件
close_older 数值 关闭超过设定时长(秒)无新内容的文件句柄

Glob匹配语法规则:

  • *:匹配任意非隐藏文件
  • ``:递归匹配子目录
  • ?:匹配单个字符
  • [a-z]:匹配字符范围
  • {a,b,c}:匹配多选项
问题 Logstash解决方案 配置参数
断点续读 sincedb位置记录文件 sincedb_path
实时读取新增内容 定时文件检查机制 stat_interval
动态发现新文件 目录扫描匹配 discover_interval
文件旋转(如logrotate) 基于inode追踪(非文件名) ignore_older
文件句柄资源释放 自动关闭空闲文件 close_older

关键配置详解:

ruby 复制代码
input {
  file {
    path => ["/var/log/*.log", "/var/log//*.log"]  # Glob匹配规则
    exclude => "*.gz"              # 排除压缩文件
    start_position => "beginning"  # 首次读取从文件开头 
    sincedb_path => "/dev/null"    # 调试时禁用sincedb
    ignore_older => 86400          # 忽略24小时未更新文件
    close_older => 3600            # 1小时无新内容关闭句柄
  }
}

注意:start_position 仅在首次读取生效,后续依赖 sincedb 记录。需删除 sincedb 文件才能重新从头读取

3 ) Glob匹配语法规则

ruby 复制代码
path => "/var/log//*.log"  # 递归匹配所有子目录
path => "/var/log/{app1,app2}/*.log"  # 匹配app1/app2目录 
path => "/var/log/??_error.log"       # 匹配两个字符前缀

特殊案例:

  • * 不匹配隐藏文件(需显式指定 .*)
  • `` 跨多级目录递归

4 ) Kafka集成配置

ruby 复制代码
input {
  kafka {
    bootstrap_servers => "kafka1:9092,kafka2:9092"
    group_id => "logstash_consumers"
    topics => ["nginx_logs", "app_logs"]
    consumer_threads => 4  # 并发线程数
  }
}

Codec插件:数据编解码与多行日志处理

1 ) 常用Codec插件对比

插件 作用 使用场景
plain 原始文本输出 基础日志
json 解析JSON字段 API日志
multiline 合并多行事件 Java异常堆栈
rubydebug 友好格式化输出 调试模式
dots 点状进度指示 压力测试

JSON解析示例:

ruby 复制代码
input { stdin { codec => json } }  
output { stdout { codec => rubydebug } }  

输入 {"name":"Alice","age":30} 输出:

json 复制代码
{ "name": "Alice", "age": 30 }  # 原始JSON被解析为字段  

2 ) 多行日志处理(multiline codec)

场景1:Java异常堆栈、代码换行字符串

核心参数:

  • pattern:正则表达式匹配行特征(如 ^\s+ 匹配空格开头行)
  • what:previous(归属上一事件)/next(归属下一事件)
  • negate:true(反向匹配,即不符合pattern的行触发合并)
ruby 复制代码
input {
  stdin {
    codec => multiline {
      pattern => "^\[%{TIMESTAMP_ISO8601}\]"    # 匹配时间戳行首
      # pattern => "\\$"                        # 以反斜杠结尾的行
      negate => true                            # 对pattern取反 
      what => "previous"                        # 归属到前一行事件
      # what    => "next"                       # 与下一行合并
    }
  }
}

多行匹配模式:

ruby 复制代码
案例1:Java堆栈日志(空格缩进归并)
pattern => "^\s+"  
what => "previous"
 
案例2:代码换行(反斜杠结尾续行)
pattern => "\\$"  
what => "next"

调试技巧:通过negate反转匹配逻辑,what定义行归属方向

场景2:跨行代码字符串合并

ruby 复制代码
codec => multiline {  
  pattern => "\\$"               # 以反斜杠结尾的行  
  what    => "next"              # 与下一行合并  
}  

输入内容:

复制代码
Hello \  
World!  

输出效果(单事件):

json 复制代码
{ "message" => "Hello World!" }  

工程示例:1

1 ) 基础日志采集管道

typescript 复制代码
// nestjs-logger.service.ts
import { Injectable, Logger } from '@nestjs/common';
import { Client } from '@elastic/elasticsearch';
 
@Injectable()
export class LoggerService {
  private readonly esClient: Client;
 
  constructor() {
    this.esClient = new Client({ node: 'http://localhost:9200' });
  }
 
  async logToES(message: string, context: string) {
    await this.esClient.index({
      index: 'app_logs',
      body: { 
        timestamp: new Date().toISOString(),
        message,
        context 
      }
    });
  }
}

Logstash配置同步ES:

ruby 复制代码
logstash.conf
input {
  file {
    path => "/var/log/nest-app/*.log"
    sincedb_path => "/opt/logstash/sincedb"
  }
}
filter {
  grok {
    match => { "message" => "%{TIMESTAMP_ISO8601:timestamp} %{LOGLEVEL:level} %{GREEDYDATA:msg}" }
  }
}
output {
  elasticsearch {
    hosts => ["localhost:9200"]
    index => "nest-logs-%{+YYYY.MM.dd}"
  }
}

2 ) Kafka缓冲日志中台

typescript 复制代码
// kafka-producer.service.ts
import { Kafka } from 'kafkajs';
 
const kafka = new Kafka({ brokers: ['kafka:9092'] });
const producer = kafka.producer();
 
async function sendLog(topic: string, log: object) {
  await producer.send({
    topic,
    messages: [{ value: JSON.stringify(log) }]
  });
}

Logstash消费Kafka写入ES:

ruby 复制代码
input {
  kafka {
    topics => ["nest_logs"]
    bootstrap_servers => "kafka:9092"
    codec => json  # 自动解析JSON
  }
}
output {
  elasticsearch {
    hosts => ["es01:9200"]
    index => "kafka-logs-%{+yyyy.MM}"
  }
}

3 ) 错误告警联动

Elasticsearch Watcher配置:

json 复制代码
PUT _watcher/watch/error_alert
{
  "trigger": { "schedule": { "interval": "1m" } },
  "input": {
    "search": {
      "request": {
        "indices": ["app_logs-*"],
        "body": {
          "query": { "match": { "level": "ERROR" } }
        }
      }
    }
  },
  "actions": {
    "send_email": {
      "email": {
        "to": "admin@example.com",
        "subject": "紧急:发现系统错误日志",
        "body": "检测到{{ctx.payload.hits.total}}条错误日志"
      }
    }
  }
}

工程示例:2

1 )原生Elasticsearch客户端

安装依赖:

bash 复制代码
npm install @elastic/elasticsearch  

服务封装(es.service.ts):

typescript 复制代码
import { Injectable } from '@nestjs/common';  
import { Client, ClientOptions } from '@elastic/elasticsearch';  
 
@Injectable()  
export class EsService {  
  private client: Client;  
 
  constructor() {  
    this.client = new Client({ node: 'http://localhost:9200' } as ClientOptions);  
  }  
 
  async indexDocument(index: string, body: any) {  
    return this.client.index({ index, body });  
  }  
 
  async search(index: string, query: any) {  
    return this.client.search({ index, body: { query } });  
  }  
}  

2 ) 方案2:NestJS官方Elastic模块

安装依赖:

bash 复制代码
npm install @nestjs/elasticsearch  

模块注册(app.module.ts):

typescript 复制代码
import { Module } from '@nestjs/common';  
import { ElasticsearchModule } from '@nestjs/elasticsearch';  
 
@Module({  
  imports: [  
    ElasticsearchModule.register({ node: 'http://localhost:9200' }),  
  ],  
})  
export class AppModule {}  

服务调用(log.service.ts):

typescript 复制代码
import { Injectable } from '@nestjs/common';  
import { ElasticsearchService } from '@nestjs/elasticsearch';  
 
@Injectable()  
export class LogService {  
  constructor(private readonly esService: ElasticsearchService) {}  
 
  async createLog(index: string, log: any) {  
    return this.esService.index({ index, body: log });  
  }  
}  

ELK全链路集成(Logstash+Kafka+ES)

架构说明:

  1. Logstash:通过 kafka Input插件消费日志,Filter阶段解析字段
  2. Kafka:作为缓冲队列,解耦日志生产与消费
  3. Elasticsearch:存储结构化日志,NestJS服务通过客户端查询

Logstash配置:

ruby 复制代码
input {  
  kafka {  
    topics => ["nginx_logs"]  
    bootstrap_servers => "kafka:9092"  
  }  
}  
filter {  
  grok { match => { "message" => "%{COMBINEDAPACHELOG}" } } # 解析Nginx日志  
}  
output {  
  elasticsearch {  
    hosts => ["es:9200"]  
    index => "nginx-%{+YYYY.MM.dd}"  
  }  
}  

ES索引生命周期管理(ILM):

json 复制代码
PUT _ilm/policy/logs_policy  
{  
  "policy": {  
    "phases": {  
      "hot": { "actions": { "rollover": { "max_size": "50GB" } } },  
      "delete": { "min_age": "30d", "actions": { "delete": {} } }  
    }  
  }  
}  
 
PUT _index_template/logs_template
{
  "index_patterns": ["*-logs*"],
  "template": {
    "settings": {
      "number_of_shards": 3,
      "number_of_replicas": 1,
      "index.lifecycle.name": "logs_policy"
    }
  }
}

性能调优参数

yaml 复制代码
# elasticsearch.yml
thread_pool.write.queue_size: 1000  # 增大写入队列
indices.memory.index_buffer_size: 30%  # 内存缓冲区
cluster.routing.allocation.disk.watermark.low: 85%  # 磁盘警戒线

最佳实践:结合_bulk API批量写入,单批次5-15MB数据量

常见问题解决方案

  1. Sincedb失效

    • 删除sincedb_path文件强制从头读取
    • 开发环境可用sincedb_path => "/dev/null"
  2. 多行日志截断

    ruby 复制代码
    codec => multiline {
      pattern => "^%{TIMESTAMP_ISO8601} "
      negate => true
      what => "previous"
      auto_flush_interval => 5  # 5秒无新行强制结束事件
    }
  3. 文件句柄泄漏

    ruby 复制代码
    file {
      close_older => 300   # 5分钟关闭空闲文件
      file_completed_action => "delete"  # 处理完删除归档文件
    }

技术总结:

  • Logstash的核心价值在于通过插件化架构实现数据流的灵活编排。Input插件解决数据源适配问题,Codec插件处理数据格式转换,尤其multiline对堆栈日志的归并至关重要
  • 生产环境中需结合Kafka缓冲、ES索引生命周期管理、批量写入策略构建稳健日志管道
相关推荐
m0_646429977 小时前
RabbitMQ 核心知识总结(生产实战版)
分布式·rabbitmq
DBA_G9 小时前
GBase 8c分布式数据库客户端工具 gsql
数据库·分布式
chunyublog9 小时前
Spark 词频统计练习
大数据·分布式·spark
ClouGence11 小时前
开源数据库管理工具 CloudDM 4.4.0 发布,新增 Kafka、SAP HANA 数据源
数据库·kafka
acrel711 小时前
高速公路分布式光伏并网电能质量与安全保护方案应用
分布式·安全
RobinXYuan11 小时前
【Kafka 源码手记】01 从消息队列到 Event Streaming
kafka·源码阅读
IT小小猪13 小时前
# Hadoop 3.3.6 完全分布式集群部署知识库
hadoop·分布式·eclipse
此时不提桶,更待何时13 小时前
06-24-B-RabbitMQ面试与生产事故实战
分布式·面试·rabbitmq
来福猿14 小时前
分布式锁 Redis 实现源码分析:SETNX、过期时间与可重入设计
数据库·redis·分布式
Pniubi1 天前
分布式锁分类讲解
分布式