我最近对Logstash感兴趣,在玩了一段时间之后,我决定创建自己的自定义插件以供学习。 我选择从Reddit中提取数据是因为a)我经常使用它并且b)尚无提供此功能的插件。<!-more-→
Elasticsearch站点提供了详尽的文档来创建自己的Logstash插件。 这种努力需要Ruby技术-不仅是语言语法,而且还包括生态系统。 预期该站点假定读者熟悉两者。 不幸的是,这不是我的情况。 我一直在用Java开发很多东西,我在Scala涉猎了一些,我对Kotlin很有兴趣-最后,我只是一个JMV开发人员(这里和那里有一些Javascript)。 长话短说,我从Ruby开始。
在此阶段,有两种可能的方法:
- 阅读有关Ruby,Gems,bundler,整个九码的文档和教程,并在几个月(或更长时间)后回来
- 或通过潜入开发现场学习
鉴于我没有几个月,而且我所学的都足够好,所以我选择了第二个选项。 这篇文章是我经过的步骤的总结,希望它可以使遇到同样情况的其他人受益。
第一步不是最难的
尽管可以从头开始使用新的Logstash插件,但文档建议从模板开始。 在线过程中对此进行了说明。 这一代产生以下结构:
$ tree logstash-input-reddit ├── Gemfile ├── LICENSE ├── README.md ├── Rakefile ├── lib │ └── logstash │ └── inputs │ └── reddit.rb ├── logstash-input-reddit.gemspec └── spec └── inputs └── reddit_spec.rb
对于Ruby新手而言,并不是很明显,该结构是Ruby Gem之一 。 通常,依赖项在关联的Gemfile
中声明:
source 'https://rubygems.org'
gemspec
但是,在这种情况下, gemspec
指令添加了一个附加的间接级别。 在关联的gemspec
文件中不仅声明依赖项,还声明元数据。 这是Bundler实用工具gem的功能。
要安装依赖项,首先需要安装bundler
gem。 是的,有摩擦...
Ruby是极限
尝试安装gem会产生以下结果:
gem install bundler Fetching: bundler-1.13.6.gem (100%) ERROR: While executing gem ... (TypeError) no implicit conversion of nil into String
第一个实现-花费了很多时间(浏览和阅读),是Ruby运行时有不同的风格。 简单的Ruby是不够的,Logstash插件开发:它需要一个专门的运行在JVM上运行又名 JRuby的 。
第二个认识是,虽然在一台机器上安装多个Ruby运行时很容易,但不可能同时运行它们。 尽管Homebrew使jruby
软件包可用,但似乎每个系统只有一个gem仓库,并且它对由不同运行时进行管理的反应非常差。
经过更多浏览后,我找到了解决方案: rbenv 。 通过隔离每个运行时,它不仅管理ruby
本身,而且管理所有关联的可执行文件( gem
, irb
, rake
等)。 这样就可以在最新的2.2.3 Ruby运行时环境下运行我的Jekyll站点,并在我的计算机上使用JRuby构建插件。 rbenv可通过Homebrew获得:
它是这样的:
-
安装rbenv
-
酿造安装rbenv
配置PATH
-
echo'eval“ $(rbenv init-)”'>>〜/ .bash_profile
获取bash配置文件脚本
-
〜/ .bash_profile
列出所有可用的运行时
-
rbenv install -l Available versions: 1.8.5-p113 1.8.5-p114 ... ... ... ree-1.8.7-2012.02 topaz-dev
安装所需的运行时
-
rbenv安装jruby-9.1.6.0
配置项目以使用所需的运行时
-
cd logstash-input-reddit rbenv local jruby-9.1.6.0
检查它的配置
-
ruby --version jruby-9.1.6.0
最后,可以安装捆绑器:
gem install bundler Successfully installed bundler-1.13.6 1 gem installed
从这一点开始,所有必需的gem也可以安装:
bundle install Fetching gem metadata from https://rubygems.org/......... Fetching version metadata from https://rubygems.org/.. Fetching dependency metadata from https://rubygems.org/. Resolving dependencies... Installing rake 12.0.0 Installing public_suffix 2.0.4 ... ... ... Installing rspec-wait 0.0.9 Installing logstash-core-plugin-api 2.1.17 Installing logstash-codec-plain 3.0.2 Installing logstash-devutils 1.1.0 Using logstash-input-reddit 0.1.0 from source at `.` Bundle complete! 2 Gemfile dependencies, 57 gems now installed. Use `bundle show [gemname]` to see where a bundled gem is installed. Post-install message from jar-dependencies: if you want to use the executable lock_jars then install ruby-maven gem before using lock_jars $ gem install ruby-maven -v '~> 3.3.11' or add it as a development dependency to your Gemfile gem 'ruby-maven', '~> 3.3.11'
插件开发适当
最终解决了这些要求之后,便可以开始适当的插件开发。 让我们跳过寻找正确的API来使用Ruby发出HTTP请求或在安装依赖项时解决Bundler警告的问题,最终代码非常简洁:
classLogStash::Inputs::Reddit<LogStash::Inputs::Base
config_name'reddit'
default:codec,'plain'
config:subreddit,:validate=>:string,:default=>'elastic'
config:interval,:validate=>:number,:default=>10
public
defregister
@host=Socket.gethostname
@http=Net::HTTP.new('www.reddit.com',443)
@get=Net::HTTP::Get.new("/r/={@subreddit}/.json")
@http.use_ssl=true
end
defrun(queue)
=wecanaborttheloopifstop?becomestrue
while!stop?
response=@http.request(@get)
json=JSON.parse(response.body)
json['data']['children'].eachdo|child|
event=LogStash::Event.new('message'=>child,'host'=>@host)
decorate(event)
queue<<event
end
Stud.stoppable_sleep(@interval){stop?}
end
end
end
该插件定义了两个配置参数,将解析subrredit
以获得数据以及两次调用之间的interval
(以秒为单位)。
register
方法初始化类属性,而run
方法循环遍历:
- 对Reddit进行HTTP调用
- 将响应主体解析为JSON
- 从JSON制作专用片段,每个帖子一个。 这一点特别重要,因为我们要分别索引每个帖子。
- 将每个片段作为Logstash事件发送以进行索引
当然,它非常粗糙,没有错误处理,它没有保存上次阅读文章的时间戳以防止索引重复等。在当前状态下,该插件有很大的改进空间,但至少可以从MVP的角度来看。
建造和安装
如上所述,该插件是Ruby gem。 它可以像任何其他gem一样构建:
gem build logstash-input-reddit
这将创建一个名为logstash-input-reddit-0.1.0.gem
的二进制文件-名称和版本均来自Bundler的gemspec
。 可以使用标准的Logtstash插件安装过程进行安装:
bin/logstash-plugin install logstash-input-reddit-0.1.0.gem
下游处理
Logstash的一项巨大优势是其处理管道的强大功能。 该插件旨在产生原始数据,但索引应分别处理每个字段。 可以使用mutate
过滤器从另一个字段中提取字段。
这是一个Logstash配置代码段示例,用于填充一些相关字段(并删除message
):
filter{
mutate {
add_field => {
"kind" => "%{message[kind]}"
"subreddit" => "%{message[data][subreddit]}"
"domain" => "%{message[data][domain]}"
"selftext" => "%{message[data][selftext]}"
"url" => "%{message[data][url]}"
"title" => "%{message[data][title]}"
"id" => "%{message[data][id]}"
"author" => "%{message[data][author]}"
"score" => "%{message[data][score]}"
"created_utc" => "%{message[data][created_utc]}"
}
remove_field => [ "message" ]
}
}
一旦构建并安装了插件,就可以使用包含先前代码段的配置文件运行Logstash。 与rubydebug编解码器结合使用时,它应产生类似于以下内容的东西:
{ "selftext" => "", "kind" => "t3", "author" => "nfrankel", "title" => "Structuring data with Logstash", "subreddit" => "elastic", "url" => "https://blog.frankel.ch/structuring-data-with-logstash/", "tags" => [], "score" => "9", "@timestamp" => 2016-12-07T22:32:03.320Z, "domain" => "blog.frankel.ch", "host" => "LSNM33795267A", "@version" => "1", "id" => "5f66bk", "created_utc" => "1.473948927E9" }
结论
从关于Ruby生态系统的几乎为零的知识开始,我对结果非常满意。
我不能做到的唯一的事情是添加第三库(比如rest-client
),Logstash不停地抱怨不能够安装,因为缺少相关的插件。 退回标准HTTP调用即可解决此问题。
另外,请注意,默认模板在安装时会有一些警告,但是可以很容易地解决它们:
- 许可证应读取
Apache-2.0
而不是Apache License (2.0)
- 依赖项版本是开放式的(
'>= 0'
),而应限制更大, 即'~> 2'
- 缺少某些元数据,例如首页
我希望这篇文章对希望开发自己的Logstash插件的其他Java开发人员有用。
翻译自: https://blog.frankel.ch/starting-logstash-plugin-development-for-java-developers/