如何编写 Logstash 编解码器插件

如何编写 Logstash 编解码器插件

要为 Logstash 开发新的编解码器,请构建一个独立的 Ruby gem,其源代码位于其自己的 GitHub 存储库中。然后,可以将 Ruby gem 托管在 RubyGems.org 上并进行共享。你可以使用示例编解码器实现作为起点。(如果你不熟悉 Ruby,可以在 https://www.ruby-lang.org.cn/en/documentation/quickstart/ 找到一份出色的快速入门指南。)

开始入门

让我们逐步了解如何使用 示例编解码器插件 创建编解码器插件。

为你的新插件创建 GitHub 存储库

每个 Logstash 插件都位于其自己的 GitHub 存储库中。要为你的插件创建新的存储库,请执行以下操作

  1. 登录 GitHub。
  2. 单击存储库选项卡。你将看到你已 fork 或贡献的其他存储库列表。
  3. 单击右上角的绿色新建按钮。
  4. 为你的新存储库指定以下设置

    • 存储库名称 — 形式为 logstash-codec-pluginname 的唯一名称。
    • 公共或私有 — 你的选择,但如果你想将其作为官方插件提交,则存储库必须是公共的。
    • 使用 README 初始化此存储库 — 使你能够立即将存储库克隆到你的计算机。
  5. 单击创建存储库

使用插件生成器工具

你可以在几秒钟内创建自己的 Logstash 插件! bin/logstash-plugingenerate 子命令使用模板化文件为新的 Logstash 插件创建基础。它创建正确的目录结构、gemspec 文件和依赖项,以便你可以开始添加自定义代码以使用 Logstash 处理数据。

有关更多信息,请参阅 生成插件

复制编解码器代码

或者,你可以使用我们在 github.com 上托管的示例存储库

  1. 克隆你的插件。GITUSERNAME 替换为你的 github 用户名,并将 MYPLUGINNAME 替换为你的插件名称。

    • git clone https://github.com/GITUSERNAME/logstash-codec-MYPLUGINNAME.git

      • 或者,通过 ssh:git clone [email protected]:GITUSERNAME/logstash-codec-MYPLUGINNAME.git
    • cd logstash-codec-MYPLUGINNAME
  2. 克隆编解码器插件示例,并将其复制到你的插件分支。

    你不想包含示例 .git 目录或其内容,因此请在复制示例之前删除它。

    • cd /tmp
    • git clone https://github.com/logstash-plugins/logstash-codec-example.git
    • cd logstash-codec-example
    • rm -rf .git
    • cp -R * /path/to/logstash-codec-mypluginname/
  3. 重命名以下文件以匹配你的插件名称。

    • logstash-codec-example.gemspec
    • example.rb
    • example_spec.rb

      cd /path/to/logstash-codec-mypluginname
      mv logstash-codec-example.gemspec logstash-codec-mypluginname.gemspec
      mv lib/logstash/codecs/example.rb lib/logstash/codecs/mypluginname.rb
      mv spec/codecs/example_spec.rb spec/codecs/mypluginname_spec.rb

你的文件结构应如下所示

$ tree logstash-codec-mypluginname
├── Gemfile
├── LICENSE
├── README.md
├── Rakefile
├── lib
│   └── logstash
│       └── codecs
│           └── mypluginname.rb
├── logstash-codec-mypluginname.gemspec
└── spec
    └── codecs
        └── mypluginname_spec.rb

有关 Ruby gem 文件结构以及 Ruby gem 创建过程的出色演练的更多信息,请参阅 http://timelessrepo.com/making-ruby-gems

查看你的插件是什么样子

在我们深入了解细节之前,请在你喜欢的文本编辑器中打开插件文件并查看一下。

require "logstash/codecs/base"
require "logstash/codecs/line"

# Add any asciidoc formatted documentation here
class LogStash::Codecs::Example < LogStash::Codecs::Base

  # This example codec will append a string to the message field
  # of an event, either in the decoding or encoding methods
  #
  # This is only intended to be used as an example.
  #
  # input {
  #   stdin { codec => example }
  # }
  #
  # or
  #
  # output {
  #   stdout { codec => example }
  # }
  config_name "example"

  # Append a string to the message
  config :append, :validate => :string, :default => ', Hello World!'

  public
  def register
    @lines = LogStash::Codecs::Line.new
    @lines.charset = "UTF-8"
  end

  public
  def decode(data)
    @lines.decode(data) do |line|
      replace = { "message" => line["message"].to_s + @append }
      yield LogStash::Event.new(replace)
    end
  end # def decode

  public
  def encode(event)
    @on_event.call(event, event.get("message").to_s + @append + NL)
  end # def encode

end # class LogStash::Codecs::Example

编码编解码器插件

现在,让我们逐行查看示例插件。

require 语句

Logstash 编解码器插件需要在 logstash/codecs/base 和 logstash/namespace 中定义的父类

require "logstash/codecs/base"
require "logstash/namespace"

当然,你构建的插件可能依赖于其他代码,甚至 gem。只需将它们与这些 Logstash 依赖项一起放在这里。

插件主体

让我们了解一下插件本身的各个元素。

class 声明

编解码器插件类应该是 LogStash::Codecs::Base 的子类

class LogStash::Codecs::Example < LogStash::Codecs::Base

类名称应与插件名称紧密呼应,例如

LogStash::Codecs::Example

config_name

  config_name "example"

这是你的插件将在编解码器配置块内调用的名称。

如果你在插件代码中设置 config_name "example",则相应的 Logstash 配置块应如下所示

配置参数

  config :variable_name, :validate => :variable_type, :default => "Default value", :required => boolean, :deprecated => boolean, :obsolete => string

配置或 config 部分允许你定义尽可能多(或尽可能少)的参数,以使 Logstash 能够处理事件。

有几个配置属性

  • :validate - 允许你强制 Logstash 为此配置选项传递特定数据类型,例如 :string:password:boolean:number:array:hash:path(文件系统路径)、uri:codec(自 1.2.0 起)、:bytes。请注意,这也用作强制转换,因为如果我为布尔值指定“true”(即使在技术上是一个字符串),它也会在配置中变为有效的布尔值。此强制转换也适用于 :number 类型,其中“1.2”变为浮点数,“22”变为整数。
  • :default - 允许你为参数指定默认值
  • :required - 此参数是否是强制性的(布尔值 true
  • :list - 此值是否应为值列表。将对列表成员进行类型检查,并将标量转换为一个元素的列表。请注意,这在很大程度上避免了数组类型,但如果你需要复杂对象的列表,则更适合使用此类型。false
  • :deprecated - 信息性(也是布尔值 truefalse
  • :obsolete - 用于声明已删除给定设置且不再起作用。其目的是为仍在使用已删除设置的用户提供知情的升级路径。

插件方法

Logstash 编解码器必须实现 register 方法和 decode 方法或 encode 方法(或两者)。

register 方法

  public
  def register
  end # def register

Logstash register 方法类似于 initialize 方法。它最初是为了强制调用 super 而创建的,以防止新手头疼。(注意:为了支持 initialize,它可能会被淘汰,并结合一些强制测试以确保调用 super。)

public 表示该方法可以在任何地方调用,而不仅仅是在类中调用。这是 Ruby 中方法的默认行为,但此处仍明确指定。

你还可以在此处分配实例变量(以 @ 开头的变量)。配置变量现在在作用域中作为实例变量,例如 @message

decode 方法

  public
  def decode(data)
    @lines.decode(data) do |line|
      replace = { "message" => line["message"].to_s + @append }
      yield LogStash::Event.new(replace)
    end
  end # def decode

编解码器的 decode 方法是将来自输入的数据转换为事件的位置。有一些复杂的示例,例如 collectd 编解码器,以及一些简单的示例,例如 spool 编解码器。

必须有一个 yield 语句作为 decode 方法的一部分,该语句会将解码的事件返回到管道。

encode 方法

  public
  def encode(event)
    @on_event.call(event, event.get("message").to_s + @append + NL)
  end # def encode

encode 方法接收一个事件并将其序列化(编码)为另一种格式。 encode 方法的优秀示例包括简单的 plain 编解码器,略微复杂的 msgpack 编解码器,甚至 avro 编解码器。

在大多数情况下,你的 encode 方法应具有 @on_event.call() 语句。此调用将按描述的方式输出每个事件的数据。

构建插件

在此过程的这一点上,你已经编写了你的插件代码,并准备从中构建 Ruby Gem。以下信息将帮助你完成此过程。

外部依赖项

Ruby 中的 require 语句用于包含必要的代码。在某些情况下,你的插件可能需要其他文件。例如,collectd 插件 使用 collectd 提供的 types.db 文件。在你的插件的主目录中,有一个名为 vendor.json 的文件,其中描述了这些文件。

vendor.json 文件包含一个 JSON 对象数组,每个 JSON 对象描述一个文件依赖项。此示例来自 collectd 编解码器插件

[{
        "sha1": "a90fe6cc53b76b7bdd56dc57950d90787cb9c96e",
        "url": "http://collectd.org/files/collectd-5.4.0.tar.gz",
        "files": [ "/src/types.db" ]
}]
  • sha1 是用于验证 url 引用的文件完整性的 sha1 签名。
  • url 是 Logstash 下载文件的地址。
  • files 是一个可选的文件数组,用于从下载的文件中提取文件。请注意,虽然 tar 归档可以使用绝对路径或相对路径,但在该数组中请将它们视为绝对路径。如果 files 不存在,则所有文件将被解压缩并提取到 vendor 目录中。

vendor.json 文件的另一个示例是 geoip 过滤器

下载这些依赖项的过程是调用 rake vendor。这将在本文档的测试部分进一步讨论。

另一种外部依赖项是 jar 文件。这将在“添加 gemspec 文件”部分中描述。

已弃用的功能

随着插件的演变,某个选项或功能可能不再符合预期用途,开发人员可能希望弃用其使用。弃用会警告用户该选项的状态,这样当它在以后的版本中被移除时,他们就不会感到意外。

Logstash 7.6 引入了一个弃用记录器,以使处理这些情况更容易。您可以使用 适配器来确保您的插件可以使用弃用记录器,同时仍然支持旧版本的 Logstash。有关更多信息和使用适配器的说明,请参阅 readme

弃用会在 log 目录中的 logstash-deprecation.log 文件中记录。

添加 Gemfile

Gemfile 允许 Ruby 的 Bundler 为您的插件维护依赖项。目前,我们只需要 Logstash gem 进行测试,但如果您需要其他 gem,您应该在此处添加它们。

有关更多详细信息,请参阅 Bundler 的 Gemfile 页面

source 'https://rubygems.org.cn'
gemspec
gem "logstash", :github => "elastic/logstash", :branch => "8.17"

添加 gemspec 文件

Gemspec 定义将要构建并包含您的插件的 Ruby gem。

有关更多信息,请参阅 Rubygems 规范页面

Gem::Specification.new do |s|
  s.name = 'logstash-codec-example'
  s.version = '0.1.0'
  s.licenses = ['Apache License (2.0)']
  s.summary = "This codec does x, y, z in Logstash"
  s.description = "This gem is a logstash plugin required to be installed on top of the Logstash core pipeline using $LS_HOME/bin/logstash-plugin install gemname. This gem is not a stand-alone program"
  s.authors = ["Elastic"]
  s.email = '[email protected]'
  s.homepage = "https://elastic.ac.cn/guide/en/logstash/current/index.html"
  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" => "codec" }

  # Gem dependencies
  s.add_runtime_dependency "logstash-core-plugin-api", ">= 1.60", "<= 2.99"
  s.add_development_dependency 'logstash-devutils'
end

将这些值更改为适合您的插件是合适的。特别是,s.names.summary 应该反映您插件的名称和行为。

s.licensess.version 也非常重要,并且在您准备发布插件时会发挥作用。

Logstash 及其所有插件均在 Apache 许可证 2.0 版 (“ALv2”) 下获得许可。如果您通过 RubyGems.org 公开发布您的插件,请确保在您的 gemspec 中包含以下行

  • s.licenses = ['Apache License (2.0)']

s.version 指定的 gem 版本有助于跟踪插件随时间的变化。您应该对版本号使用 semver 版本控制策略。

运行时和开发依赖项

gemspec 文件的底部是一个带有注释的部分:Gem dependencies。这是必须提及任何其他所需 gem 的位置。如果一个 gem 是插件运行所必需的,那么它就是一个运行时依赖项。如果一个 gem 仅用于测试,那么它就是一个开发依赖项。

您还可以对依赖项(包括其他 Logstash 插件)具有版本控制要求

  # Gem dependencies
  s.add_runtime_dependency "logstash-core-plugin-api", ">= 1.60", "<= 2.99"
  s.add_development_dependency 'logstash-devutils'

此 gemspec 对 logstash-core-plugin-api 具有运行时依赖项,并要求其版本号大于或等于 1.60 且小于或等于 2.99。

所有插件都对 logstash-core-plugin-api gem 具有运行时依赖项,对 logstash-devutils 具有开发依赖项。

Jar 依赖项

在某些情况下,例如 Elasticsearch 输出插件,您的代码可能依赖于 jar 文件。在这种情况下,依赖项以这种方式添加到 gemspec 文件中

  # Jar dependencies
  s.requirements << "jar 'org.elasticsearch:elasticsearch', '5.0.0'"
  s.add_runtime_dependency 'jar-dependencies'

在这两者都定义之后,安装过程将在 http://mvnrepository.com 上搜索所需的 jar 文件并下载指定的版本。

为您的插件编写文档

文档是您插件的重要组成部分。所有插件文档都将呈现并放置在 Logstash 参考版本化的插件文档中。

有关提示和指南,请参阅 为您的插件编写文档

添加测试

Logstash 非常喜欢测试。大量的测试。如果您在生产环境中使用新的编解码器插件,您需要进行一些测试以确保您没有破坏任何现有功能。

对 RSpec 的全面阐述超出了本文档的范围。有关 RSpec 的更多信息,请访问 https://rspec.ruby-lang.org.cn

如需了解有关测试和测试的帮助,请查看其他几个类似插件的 spec/codecs/ 目录。

克隆和测试!

现在,让我们从插件的全新克隆开始,构建它并运行测试。

  • 将您的插件克隆到临时位置GITUSERNAME 替换为您的 github 用户名,并将 MYPLUGINNAME 替换为您的插件名称。

    • git clone https://github.com/GITUSERNAME/logstash-codec-MYPLUGINNAME.git

      • 或者,通过 ssh:git clone [email protected]:GITUSERNAME/logstash-codec-MYPLUGINNAME.git
    • cd logstash-codec-MYPLUGINNAME

然后,您需要使用 bundler 安装插件的依赖项

bundle install

如果您的插件在 vendor.json 中描述了外部文件依赖项,则必须在运行或测试之前下载该依赖项。您可以通过运行以下命令来执行此操作

rake vendor

最后,运行测试

bundle exec rspec

您应该看到一条成功消息,如下所示

Finished in 0.034 seconds
1 example, 0 failures

太棒了!您快要完成了!(除非您看到失败...您应该首先修复这些失败)。

构建和测试

现在,您已准备好将(经过充分测试的)插件构建为 Ruby gem。

构建

您已经具备了所有必要的要素,因此让我们继续运行构建命令

gem build logstash-codec-example.gemspec

就是这样!您的 gem 应该已构建并位于同一路径中,名称为

logstash-codec-mypluginname-0.1.0.gem

来自您的 gemspec 文件的 s.version 数字将提供 gem 版本,在本例中为 0.1.0

测试安装

您应该将插件测试安装到 Logstash 的全新安装中。从 Logstash 下载页面下载最新版本。

  1. 解压 tar 包并 cd 到该目录

    curl -O https://download.elastic.co/logstash/logstash/logstash-8.17.0.tar.gz
    tar xzvf logstash-8.17.0.tar.gz
    cd logstash-8.17.0
  2. 使用插件工具,我们可以安装刚刚构建的 gem。

    • /my/logstash/plugins 替换为适合您环境的 gem 的正确路径,并将 0.1.0 替换为 gemspec 文件中的正确版本号。

      bin/logstash-plugin install /my/logstash/plugins/logstash-codec-example/logstash-codec-example-0.1.0.gem
    • 运行此命令后,您应该会看到 Logstash 发出的成功安装反馈

      validating /my/logstash/plugins/logstash-codec-example/logstash-codec-example-0.1.0.gem >= 0
      Valid logstash plugin. Continuing...
      Successfully installed 'logstash-codec-example' with version '0.1.0'

      您还可以使用 Logstash 插件工具来确定当前有哪些可用的插件

      bin/logstash-plugin list

      根据您安装的内容,您可能会看到插件的简短或冗长列表:输入、编解码器、过滤器和输出。

  3. 现在,尝试使用通过命令行传递的简单配置运行 Logstash,使用 -e 标志。

    您的结果将取决于您的编解码器插件的设计目的。

bin/logstash -e 'input { stdin{ codec => example{}} } output {stdout { codec => rubydebug }}'

示例编解码器插件将附加 append 的内容(默认情况下附加 “, Hello World!”)

启动 Logstash 后,键入一些内容,例如“Random output string”。生成的输出消息字段内容应为“Random output string, Hello World!”

Random output string
{
       "message" => "Random output string, Hello World!",
      "@version" => "1",
    "@timestamp" => "2015-01-27T19:17:18.932Z",
          "host" => "cadenza"
}

随意尝试并通过更改 append 参数来测试此功能

bin/logstash -e 'input { stdin{ codec => example{ append => ", I am appending this! }} } output {stdout { codec => rubydebug }}'

恭喜!您已构建、部署并成功运行了 Logstash 编解码器。

将您的插件提交到 RubyGems.orglogstash-plugins

Logstash 使用 RubyGems.org 作为所有插件工件的存储库。开发新插件后,您可以通过将其发布到 RubyGems.org 来使其对 Logstash 用户可用。

许可

Logstash 及其所有插件均在 Apache 许可证 2.0 版 (“ALv2”) 下获得许可。如果您通过 RubyGems.org 公开发布您的插件,请确保在您的 gemspec 中包含以下行

  • s.licenses = ['Apache License (2.0)']

发布到 RubyGems.org

首先,您需要在 RubyGems.org 上拥有一个帐户

创建帐户后,从 RubyGems.org 获取 API 密钥。默认情况下,RubyGems 使用文件 ~/.gem/credentials 来存储您的 API 密钥。这些凭据将用于发布 gem。将 usernamepassword 替换为您在 RubyGems.org 上创建的凭据

curl -u username:password https://rubygems.org.cn/api/v1/api_key.yaml > ~/.gem/credentials
chmod 0600 ~/.gem/credentials

在继续之前,请确保您的 gemspec 文件中具有正确的版本并提交您的更改。

  • s.version = '0.1.0'

要发布新 logstash gem 的 0.1.0 版本

bundle install
bundle exec rake vendor
bundle exec rspec
bundle exec rake publish_gem

执行 rake publish_gem

  1. 从 gemspec 文件读取版本 (s.version = '0.1.0')
  2. 在您的本地存储库中检查是否存在该版本的标签。如果该标签已存在,则会中止该过程。否则,它会在您的本地存储库中创建一个新的版本标签。
  3. 构建 gem
  4. 将 gem 发布到 RubyGems.org

就是这样!您的插件已发布!Logstash 用户现在可以通过运行以下命令来安装您的插件

bin/logstash-plugin install logstash-codec-mypluginname

将您的源代码贡献给 logstash-plugins

并非必须将您的源代码贡献给 logstash-plugins github 组织,但我们始终欢迎新插件!

好处

将您的插件放在 logstash-plugins 存储库中的许多好处包括

  • 发现。您的插件将显示在 Logstash 参考中,Logstash 用户首先在此处查找插件和文档。
  • 文档。您的插件文档将自动添加到 Logstash 参考中。
  • 测试。 通过我们的测试基础设施,您的插件将针对 Logstash 的当前和未来版本进行持续测试。 因此,用户可以确信,如果出现不兼容的情况,它们将被快速发现和纠正。

验收指南

  • 代码审查。 您的插件必须由社区成员审查,以确保其连贯性、质量、可读性、稳定性和安全性。
  • 测试。 您的插件必须包含测试才能被接受。 这些测试的范围和完整性也需要进行代码审查。 如果您不知道如何编写测试也没关系,我们会指导您。 我们正在努力发布一个关于为 Logstash 创建测试的指南,这将使其更容易。 同时,您可以参考 http://betterspecs.org/ 获取示例。

要开始将您的插件迁移到 logstash-plugins,只需在 Logstash 存储库中创建一个新的 issue。 当验收指南完成后,我们将使用推荐的 github 流程 来促进迁移到 logstash-plugins 组织。