如何编写自定义 Logstash 插件?(详细教程 + 实战示例)

如何编写自定义 Logstash 插件?(详细教程 + 实战示例)

Logstash 虽然提供了 200+ 官方和社区插件,但在某些特殊场景下(如对接内部系统、私有协议、定制化处理逻辑),仍需要开发 自定义插件

本文将带你从零开始,手把手编写一个 自定义 Filter 插件(也可扩展为 Input/Output),并介绍插件结构、开发环境、调试与打包发布全流程。


一、Logstash 插件类型

类型说明示例
input数据输入源读取自定义 API、消息队列
filter数据处理过滤器解析私有日志格式、调用外部服务
output数据输出目标写入内部数据库、调用 Webhook

本文以 Filter 插件 为例,但结构通用。


二、开发准备

2.1 环境要求

工具版本要求
Ruby≥ 2.7(Logstash 基于 JRuby)
Java≥ 8(Logstash 运行在 JVM 上)
Logstash下载对应版本(如 8.11.3)
gemRuby 包管理器

💡 提示:Logstash 自带 JRuby,推荐使用其内置 Ruby 环境。


2.2 安装 Plugin Generator(官方工具)

Logstash 提供了插件生成器,可快速创建模板:

# 进入 Logstash 目录
cd /usr/share/logstash

# 安装生成器(首次)
bin/logstash-plugin generate --type filter --name my_custom_filter

这会生成一个名为 logstash-filter-my_custom_filter 的插件项目。


三、插件项目结构

执行上述命令后,生成目录:

logstash-filter-my_custom_filter/
├── lib/
│   └── logstash/
│       └── filters/
│           └── my_custom_filter.rb     # 核心逻辑
├── spec/
│   └── logstash/
│       └── filters/
│           └── my_custom_filter_spec.rb # 单元测试
├── logstash-filter-my_custom_filter.gemspec  # Gem 包描述文件
└── CHANGELOG.md

四、实战:编写一个“添加时间戳偏移”的 Filter 插件

需求:

创建一个 filter 插件,将事件中的 @timestamp 增加指定秒数(用于测试或模拟数据)。

例如:offset => 3600 表示时间加 1 小时。


4.1 编辑 my_custom_filter.rb

路径:lib/logstash/filters/my_custom_filter.rb

# encoding: utf-8
require "logstash/filters/base"
require "logstash/namespace"

class LogStash::Filters::MyCustomFilter < LogStash::Filters::Base

  # 插件名称(必须与文件名一致)
  config_name "my_custom_filter"

  # 定义配置参数
  config :offset, :validate => :number, :default => 0
  config :target, :validate => :string, :default => "@timestamp"

  public
  def register
    # 插件初始化时调用
    @logger.info("MyCustomFilter 已注册", :offset => @offset, :target => @target)
  end

  public
  def filter(event)
    # 跳过已标记为“丢弃”的事件
    return unless filter_include?(event)

    begin
      if @target == "@timestamp"
        # 处理时间戳
        new_timestamp = event.timestamp.time + @offset
        event.timestamp = LogStash::Timestamp.new(new_timestamp)
      else
        # 处理普通字段(假设是时间字符串)
        old_value = event.get(@target)
        if old_value.is_a?(String)
          time = Time.parse(old_value)
          new_time = (time + @offset).iso8601
          event.set(@target, new_time)
        end
      end

      # 标记事件已处理
      filter_matched(event)
    rescue => e
      @logger.warn("MyCustomFilter 处理失败", :exception => e.message, :event => event)
      # 可选:丢弃错误事件
      # event.tag("_my_custom_filter_error")
    end
  end

end

4.2 编辑 .gemspec 文件

确保 logstash-filter-my_custom_filter.gemspec 内容正确:

Gem::Specification.new do |s|
  s.name = "logstash-filter-my_custom_filter"
  s.version = "1.0.0"
  s.licenses = ["Apache-2.0"]
  s.summary = "Adds a time offset to @timestamp or a field"
  s.description = "This plugin adds a specified number of seconds to the @timestamp or a custom field."
  s.homepage = "https://github.com/yourname/logstash-filter-my_custom_filter"
  s.authors = ["Your Name"]
  s.email = "you@example.com"

  # 依赖 Logstash 核心
  s.require_paths = ["lib"]

  # 指定 Logstash 版本兼容性
  s.add_runtime_dependency "logstash-core-plugin-api", ">= 1.60", "<= 2.99"
end

五、测试插件

5.1 本地安装插件

cd logstash-filter-my_custom_filter

# 打包
gem build logstash-filter-my_custom_filter.gemspec

# 安装到 Logstash
/usr/share/logstash/bin/logstash-plugin install ./logstash-filter-my_custom_filter-1.0.0.gem

✅ 安装成功后,Logstash 启动时会加载该插件。


5.2 编写测试配置文件 test.conf

input {
  generator {
    message => "test event"
    count => 1
  }
}

filter {
  my_custom_filter {
    offset => 3600
    target => "@timestamp"
  }
}

output {
  stdout {
    codec => rubydebug
  }
}

5.3 运行测试

/usr/share/logstash/bin/logstash -f test.conf

你应该看到输出的 @timestamp 比当前时间 快了 1 小时


六、编写单元测试(可选但推荐)

编辑 spec/logstash/filters/my_custom_filter_spec.rb

require "logstash/devutils/rspec/spec_helper"
require "logstash/filters/my_custom_filter"

describe LogStash::Filters::MyCustomFilter do
  let(:config) { { "offset" => 3600 } }

  context "当 offset 为 3600" do
    sample({}) do
      expect(subject.get("@timestamp").time.to_i).to be > (Time.now.to_i + 3500)
    end
  end
end

运行测试:

rake test

七、支持 Input 或 Output 插件

Input 示例骨架

class LogStash::Inputs::MyInput < LogStash::Inputs::Base
  config_name "my_input"

  def register; end

  def run(queue)
    while !stop?
      event = LogStash::Event.new("message" => "hello from custom input")
      queue << event
      sleep 5
    end
  end

  def stop; end
end

Output 示例骨架

class LogStash::Outputs::MyOutput < LogStash::Outputs::Base
  config_name "my_output"

  def register; end

  def receive(event)
    puts "Output: #{event.to_json}"
  end

  def close; end
end

生成命令:

bin/logstash-plugin generate --type input --name my_input
bin/logstash-plugin generate --type output --name my_output

八、插件发布与管理

8.1 发布到 RubyGems(可选)

gem push logstash-filter-my_custom_filter-1.0.0.gem

8.2 内部私有仓库(企业推荐)

使用 Gem in a Box、Artifactory 或 Nexus 搭建私有 gem 仓库。


九、调试技巧

技巧方法
查看日志--log.level debug
打印调试信息@logger.info("debug", :var => value)
使用 stdout { codec => rubydebug }查看事件结构
启用 --config.test_and_exit验证配置语法
bin/logstash -f test.conf --config.test_and_exit

十、最佳实践

  1. 命名规范
    • Filter 插件:logstash-filter-<name>
    • 使用小写字母+下划线
  2. 错误处理
    • 捕获异常,记录日志,避免崩溃
    • 使用 event.tag("_error") 标记失败事件
  3. 性能优化
    • 避免在 filter 中做耗时网络请求(可异步)
    • 缓存外部资源(如 lookup 表)
  4. 文档化
    • 在 README 中说明配置参数和用途
  5. 版本管理
    • 遵循语义化版本(SemVer)

十一、常见问题

❓ 插件未加载?

  • 检查插件名称是否与 config_name 一致
  • 查看 Logstash 启动日志是否有 Load custom plugin 信息
  • 确保 .gem 安装成功:bin/logstash-plugin list | grep my_custom_filter

❓ 报错 undefined method

  • 检查类继承是否正确:< LogStash::Filters::Base
  • 确保文件路径和命名规范

❓ 如何调用外部 API?

require "net/http"
uri = URI("https://api.example.com/lookup")
response = Net::HTTP.get(uri)

注意:建议加缓存,避免性能瓶颈。


十二、参考资源


结语

编写自定义 Logstash 插件,让你能够:

  • 对接内部系统
  • 实现复杂业务逻辑
  • 扩展 Logstash 能力边界

虽然需要一定的 Ruby 基础,但模板化开发 + 丰富的 API,使得开发过程非常高效。


🎁 附:完整插件模板下载

你可以克隆这个仓库作为起点:

git clone https://github.com/logstash-plugins/logstash-filter-example.git

替换内容即可快速开发。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值