Input插件核心机制与文件监控
Input插件基础功能
Input插件指定数据输入源,单个Pipeline可配置多个插件
Logstash支持丰富的数据源接入(如文件、Kafka、标准输入等)
核心插件包括:
- stdin:标准输入(无专用配置)
- file:文件读取(支持日志收集)
- kafka:消息队列集成
1 ) stdin插件详解
作用:从命令行终端实时读取数据流。
通用配置参数(所有Input插件共享):
type:自定义事件类型(字符串),用于后续条件过滤(如web/mysql)。tags:事件标签(数组),辅助Filter阶段的条件判断。add_field:添加自定义字段(哈希类型)。
配置示例:
input {
stdin {
codec => "plain" # 原始文本解码
type => "STD" # 自定义类型
tags => ["test"]
add_field => { "key" => "value" }
}
}
output { stdout { codec => rubydebug } } # 调试输出
执行效果(输入 Hello World):
{
"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 |
关键配置详解:
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匹配语法规则
path => "/var/log//*.log" # 递归匹配所有子目录
path => "/var/log/{app1,app2}/*.log" # 匹配app1/app2目录
path => "/var/log/??_error.log" # 匹配两个字符前缀
特殊案例:
*不匹配隐藏文件(需显式指定.*)- `` 跨多级目录递归
4 ) Kafka集成配置
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解析示例:
input { stdin { codec => json } }
output { stdout { codec => rubydebug } }
输入 {"name":"Alice","age":30} 输出:
{ "name": "Alice", "age": 30 } # 原始JSON被解析为字段
2 ) 多行日志处理(multiline codec)
场景1:Java异常堆栈、代码换行字符串
核心参数:
pattern:正则表达式匹配行特征(如^\s+匹配空格开头行)what:previous(归属上一事件)/next(归属下一事件)negate:true(反向匹配,即不符合pattern的行触发合并)
input {
stdin {
codec => multiline {
pattern => "^\[%{TIMESTAMP_ISO8601}\]" # 匹配时间戳行首
# pattern => "\\$" # 以反斜杠结尾的行
negate => true # 对pattern取反
what => "previous" # 归属到前一行事件
# what => "next" # 与下一行合并
}
}
}
多行匹配模式:
案例1:Java堆栈日志(空格缩进归并)
pattern => "^\s+"
what => "previous"
案例2:代码换行(反斜杠结尾续行)
pattern => "\\$"
what => "next"
调试技巧:通过negate反转匹配逻辑,what定义行归属方向
场景2:跨行代码字符串合并
codec => multiline {
pattern => "\\$" # 以反斜杠结尾的行
what => "next" # 与下一行合并
}
输入内容:
Hello \
World!
输出效果(单事件):
{ "message" => "Hello World!" }
工程示例:1
1 ) 基础日志采集管道
// 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:
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缓冲日志中台
// 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:
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配置:
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客户端
安装依赖:
npm install @elastic/elasticsearch
服务封装(es.service.ts):
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模块
安装依赖:
npm install @nestjs/elasticsearch
模块注册(app.module.ts):
import { Module } from '@nestjs/common';
import { ElasticsearchModule } from '@nestjs/elasticsearch';
@Module({
imports: [
ElasticsearchModule.register({ node: 'http://localhost:9200' }),
],
})
export class AppModule {}
服务调用(log.service.ts):
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)
架构说明:
- Logstash:通过
kafkaInput插件消费日志,Filter阶段解析字段 - Kafka:作为缓冲队列,解耦日志生产与消费
- Elasticsearch:存储结构化日志,NestJS服务通过客户端查询
Logstash配置:
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):
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"
}
}
}
性能调优参数
# 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数据量
常见问题解决方案
-
Sincedb失效
- 删除
sincedb_path文件强制从头读取 - 开发环境可用
sincedb_path => "/dev/null"
- 删除
-
多行日志截断
codec => multiline { pattern => "^%{TIMESTAMP_ISO8601} " negate => true what => "previous" auto_flush_interval => 5 # 5秒无新行强制结束事件 } -
文件句柄泄漏
file { close_older => 300 # 5分钟关闭空闲文件 file_completed_action => "delete" # 处理完删除归档文件 }
技术总结:
- Logstash的核心价值在于通过插件化架构实现数据流的灵活编排。Input插件解决数据源适配问题,Codec插件处理数据格式转换,尤其
multiline对堆栈日志的归并至关重要 - 生产环境中需结合Kafka缓冲、ES索引生命周期管理、批量写入策略构建稳健日志管道
641

被折叠的 条评论
为什么被折叠?



