Logstash实现自定义插件在filter中读取redis


需求:

埋点的数据,在logstash数据抽取中,需要实时关联mysql的业务数据,最终形成一个数据输出。

实现:

1. 根据官网提示,先生成一个模版
bin/logstash-plugin generate --type filter --name redis  --path vendor/localgems
生成之后的目录是: ./vendor/localgems/logstash-filter-redis 
生成目录的树状结构如下:
[root@node004 localgems]# tree logstash-filter-redis/
logstash-filter-redis/
├── CONTRIBUTORS
├── docs
│   └── index.asciidoc
├── Gemfile
├── lib
│   └── logstash
│       └── filters
│           └── redis.rb
├── LICENSE
├── logstash-filter-redis.gemspec
├── Rakefile
└── spec
    ├── filters
    │   └── test_spec.rb
    └── spec_helper.rb
2. 主要用到的文件及文件夹
lib: 存放插件代码。
logstash-filter-redis.gemspec: 存放插件注册信息。
Gem::Specification.new do |s|
  s.name          = 'logstash-filter-redis'
  s.version       = '0.1.0'
  s.licenses      = ['Apache-2.0']
  s.summary       = '读取redis数据,至filter中 '
  s.description   = '用户可以在logstash中添加该filter,最直观的结果,可以产生一个新的字段,从redis产生 '
  s.homepage      = 'http://www.elastic.co/guide/en/logstash/current/index.html'
  s.authors       = ['rookieuncle']
  s.email         = ['rookieuncle@163.com']
  s.require_paths = ['lib']

  # Files
  s.files = Dir['lib/**/*','spec/**/*','vendor/**/*','*.gemspec','*.md','CONTRIBUTORS','Gemfile','LICENSE','NOTICE.TXT']
   # Tests
  s.test_files = s.files.grep(%r{^(test|spec|features)/})

  # Special flag to let us know this is actually a logstash plugin
  s.metadata = { "logstash_plugin" => "true", "logstash_group" => "filter" }

  # Gem dependencies
  s.add_runtime_dependency "logstash-core-plugin-api", "~> 2.0"
  s.add_development_dependency 'logstash-devutils'
  s.add_development_dependency 'redis'
end

s.name 定义了插件名字
s.version 定义了插件版本(注意:默认是0.1.0开始,但是如果版本小于1.0 会被认为是孵化版本,使用的时候logstash输出日志会记录,但是不影响使用)
s.licenses 协议版本,遵循开源协议,默认是apache-2.0
s.summary 一个关于插件的小简介,如果不想写,可以删除该行,但是不能空着,会报错
s.description 一个详细的简介,如果不想写,可以删除该行,但是不能空着,会报错
s.homepage 插件所属网站,如果不想写,可以删除该行,但是不能空着,会报错
s.authors 作者
s.email 邮箱
s.require_paths 代码所在目录
中间几个一般不需要修改
s.add_runtime_dependency 运行时依赖参数,指定了core的依赖是2.0版本的
s.add_development_dependency 开发依赖,需要的话自己可以配置

我们做修改的地方有一下几个:
s.summary = '读取redis数据,至filter中 ’
s.description = ‘用户可以在logstash中添加该filter,最直观的结果,可以产生一个新的字段,从redis产生’
s.homepage = ‘http://www.elastic.co/guide/en/logstash/current/index.html’
s.authors = [‘rookieuncle’]
s.email = [‘rookieuncle@163.com’]
···
s.add_development_dependency ‘redis’
最后添加的redis,是因为我们需要使用redis,相当于import redis相关依赖

3. 主要代码文件
  1. config 配置规则
    config :variable_name, :validate => :variable_type, :default => “Default value”, :required => boolean, :deprecated => boolean, :obsolete => string

:variable_name:参数名称 (host port db 等 )
:validate:验证参数类型 (:string,:password,:boolean,:number,:array,:hash,:path等)
:required:是否必须配置 (true 或者 false)
:default:默认值
:deprecated:是否废弃 (true 或者 false)
:obsolete:声明该配置不再使用,同时提供用户提供明智的升级方案

  1. reby中
# encoding: utf-8
# 第一行的utf-8设置,是reby的格式,类似与shell中的.sh脚本,不要动!
require "logstash/filters/base"
require "logstash/namespace"
require "redis" # 添加redis依赖

class LogStash::Filters::Redis < LogStash::Filters::Base
  # 定义name 需要与上边配置文件保持一致
  config_name "redis"

  default :codec, "json" # 设置配置文件默认的格式

  config :host, :validate => :string, :default => "127.0.0.1" # 设置 redis host 参数,默认 127.0.0.1
  config :port, :validate => :number, :default => 6379 # 设置 port 参数,默认 6379
  config :db, :validate => :number, :default => 0 # 设置 db 参数,默认 0
  config :password, :validate => :string # 设置 password 参数,不默认
  config :timeout, :validate => :number, :default => 5 # 设置 timeout 参数,不默认
  config :key, :validate => :string # 设置 查询的rediskey 参数,不默认
  config :target, :validate => :string, :default => "target" # 设置 返回 参数,不设置默认为 "target"


  public
  # register 是初始化代码,和init类似 必须实现两个方法的第一个
  def register
    # 做redis初始化工作
    @logger = self.logger
    begin
      $redis = Redis.new(:host => @host,:port => @port, :db => @db, :password => @password)
    rescue => e
       @logger.error("Field to init redis:", :exception => e) # 如果redis初始化失败,则报错
    end
    @logger.info("redis filter is ok, the plugin param: host is #{@host}; port is #{@port}; db is #{@db}; password is #{@password};") # 打印info日志,将配置文件内容打印输出
  end 

  public
  # filter 是具体功能实现。 必须实现两个方法的第二个 如果你编写的是input或者output 则根据自动生成的看
  def filter(event) 
    _key=event.get(@key)
    # @logger.info("_key_1: #{_key}\t")
    if !_key.nil?
      # @logger.info("_key: #{_key}\t")
      _value=$redis.get(_key)
      # @logger.info("_key: #{_key}, _value: #{_value}")
    if (!_value.nil?)&&(!_value.empty?)
      if @target
        @logger.debug? && @logger.debug("Overwriting existing target field", :target => @target)
        event.set(@target, _value)
      end
    else
        _action=event.get("action")
        @logger.info("The value was not found in Redis! _key: #{_key}, _action: #{_action}")
    if @message
      event.set("message", @message)
    end # @message
    end # _value.nil?
    end # _key.nil?
    filter_matched(event) # 该方法会把事件传入下一个过滤器
  end # def filter
  # 为了在logstash关闭时候不丢失数据,建议实现这个方法,logstash会自动执行(在output插件中实现,filter不必实现)
  # public 
  # def teardown
  # end
end # Redis
在上述filter方法中可以使用几个默认的方法:
  1. filter_matched() 该方法会把事件传入下一个过滤器
  2. add_field、remove_field 添加或者移除一个字段
  3. add_tags、remove_tags 添加或者移除一个标签
  4. event.cancel 取消当前的事件,即不在继续向下走。(在split插件中有典型的使用场景)
4. 配置完成将插件配置到logstash配置文件中
  1. 这里使用之前 是需要对安装一下redis的依赖库,默认logstash7.1.0是不带redis依赖的

安装方式:切换到logstash的根目录的 vendor/jruby/bin 目录下,然后执行./gem install redis 安装完成后 在 vendor/jruby/lib/ruby/gems/shared/gems目录下查看有没有redis相关文件夹

  1. 然后代码中引用的redis依赖才会生效,要保证
  1. logstash-filter-redis.gemspec 中最后添加 s.add_development_dependency 'redis'
  2. 保证 ruby 开头 require "redis" # 添加redis依赖
  1. 在logstash根目录下的Gemfile文件中添加依赖(path是文件生成位置)

gem "logstash-filter-test", :path => "vendor/localgems/logstash-filter-test"

5. logstash中使用自定义的redis插件
input {
  stdin { } 
}
filter{
  redis {
    host => "127.0.0.1"
    password => "root"
    key => "find_key"
    target => "out_value"
  }
}
output {
  stdout { }
}

测试输出正常是可以有结果的!


到这里就差不多了,有什么问题,欢迎留言讨论