Slide 1

Slide 1 text

‹#› Aaron Mildenstein Logstash Developer Write your own Logstash plugin for fun and profit

Slide 2

Slide 2 text

Logstash A brief history... 2

Slide 3

Slide 3 text

Logstash: Quick Review • Open Source data stream ETL tool with a pipeline • JRuby • Other Java languages coming • Plugin ecosystem 3

Slide 4

Slide 4 text

Logstash Pipeline 4

Slide 5

Slide 5 text

Logstash Configuration Inputs 5 input {
 plugin {
 setting_1 => "value" # comment
 array_2 => [ "value1", "value2" ]
 hash_3 => { key => "value" }
 }
 }

Slide 6

Slide 6 text

Logstash Configuration Filters 6 filter {
 plugin {
 setting_1 => "value" # comment
 array_2 => [ "value1", "value2" ]
 hash_3 => { key => "value" }
 }
 }

Slide 7

Slide 7 text

Logstash Configuration Outputs 7 output {
 plugin {
 setting_1 => "value" # comment
 array_2 => [ "value1", "value2" ]
 hash_3 => { key => "value" }
 }
 }

Slide 8

Slide 8 text

Logstash Configuration Conditionals – Only in filter and output blocks! 8 if EXPRESSION { ... } else if EXPRESSION { ... } else { ... }

Slide 9

Slide 9 text

The Ws of Logstash Plugins • Why? • What? • Who? • Where? • When? • Bonus: How? • That's why you're here, right? 9

Slide 10

Slide 10 text

Why? • Extensibility • Flexibility • Community 10

Slide 11

Slide 11 text

What? • JRuby • Ruby code that can be run by a Java interpreter • Input • Filter • Output • Codec 11

Slide 12

Slide 12 text

Who? • Who owns plugins? • Who can make plugins? • Who determines whether they are included with Logstash? 12

Slide 13

Slide 13 text

Where? • rubygems.org 13

Slide 14

Slide 14 text

When? • Anytime, of course! 14

Slide 15

Slide 15 text

Things to consider 15 Design Phase

Slide 16

Slide 16 text

Before you reinvent the wheel... • Is there an existing plugin? • Can I extend an existing plugin? • Has anyone (outside the Logstash community) solved this problem? • Can I piggy-back on that? • Depends on the license, of course • Is this going to be internal/private or shared/open-source? • Will this be better as a filter or a codec? • Am I reproducing existing functionality? 16

Slide 17

Slide 17 text

Examples • Dedicated plugins vs. codecs for Netflow • Add a grok pattern vs. write a dedicated plugin • TCP/UDP inputs and outputs already exist. • Write a codec if sending/receiving data via TCP/UDP. • http and http_poller input plugins already exist. • Use these if your project sends http, or provides data via http • JDBC input plugin vs. custom plugin 17

Slide 18

Slide 18 text

‹#› To argue at length over a decision that is, in the end, arbitrary. aka "Parkinson's Law of Triviality" 1957 Bikeshedding...

Slide 19

Slide 19 text

Simple things should be simple Not like this Like this

Slide 20

Slide 20 text

Getting started 20 Code basics

Slide 21

Slide 21 text

For a new plugin, start with the examples • One of each kind for you to choose from • https://github.com/logstash-plugins/logstash-input-example/ • https://github.com/logstash-plugins/logstash-filter-example/ • https://github.com/logstash-plugins/logstash-output-example/ • https://github.com/logstash-plugins/logstash-codec-example/ 21

Slide 22

Slide 22 text

Create a GitHub repo for your new plugin Each Logstash plugin lives in its own GitHub repository. To create a new repository for your plugin: 1. Log in to GitHub. 2. Click the Repositories tab. You’ll see a list of other repositories you’ve forked or contributed to. 3. Click the green New button in the upper right. 22

Slide 23

Slide 23 text

Create a GitHub repo for your new plugin 4. Specify the following settings for your new repo: • Repository name — a unique name of the form logstash-TYPE- pluginname. • Public or Private — your choice, but the repository must be Public if you want to submit it as an official plugin. • Initialize this repository with a README — enables you to immediately clone the repository to your computer. 5. Click Create Repository. 23

Slide 24

Slide 24 text

More repository prep • Clone your repository • Clone the example repository that is of the same type • Delete the .git directory from the example repository • Delete everything in your repository, except the .git directory • cp -R everything in the example repository you cloned to your newly wiped plugin directory. • Rename the following files to fit your plugin type and name: • logstash-TYPE-example.gemspec • example.rb • example_spec.rb 24

Slide 25

Slide 25 text

It should look like this Matching your TYPE 25 $ tree logstash-TYPE-mypluginname !"" Gemfile !"" LICENSE !"" README.md !"" Rakefile !"" lib # $"" logstash # $"" TYPEs # $"" mypluginname.rb !"" logstash-TYPE-mypluginname.gemspec $"" spec $"" TYPEs $"" mypluginname_spec.rb

Slide 26

Slide 26 text

Coming Soon: generate This will be much more simple... 26 $ bin/logstash-plugin generate --help Usage: bin/logstash-plugin generate [OPTIONS] Options: --type TYPE Type of the plugin {input, filter, codec, output}s --name PLUGIN Name of the new plugin --path PATH Location where the plugin skeleton will be created (default: "${PWD}") -h, --help print help

Slide 27

Slide 27 text

Coming Soon: generate See? 27 $ bin/logstash-plugin generate --type input --name fooplugin \ --path /tmp/fooplugin Creating /tmp/fooplugin/logstash-input-fooplugin create logstash-input-fooplugin/CHANGELOG.md create logstash-input-fooplugin/CONTRIBUTORS create logstash-input-fooplugin/DEVELOPER.md create logstash-input-fooplugin/Gemfile create logstash-input-fooplugin/lib/logstash/inputs/fooplugin.rb create logstash-input-fooplugin/LICENSE create logstash-input-fooplugin/logstash-input-fooplugin.gemspec create logstash-input-fooplugin/Rakefile create logstash-input-fooplugin/README.md create logstash-input-fooplugin/spec/inputs/fooplugin_spec.rb

Slide 28

Slide 28 text

Plugin Anatomy The "bones" 28 # encoding: utf-8 require "logstash/inputs/base" require "logstash/namespace" require "stud/interval" require "socket" # for Socket.gethostname # Add any asciidoc formatted documentation here # Generate a repeating message. # # This plugin is intended only as an example. class LogStash::Inputs::Example < LogStash::Inputs::Base

Slide 29

Slide 29 text

Plugin Anatomy Configuration & Auto-documentation 29 # This is where your plugin name goes config_name "example" # If undefined, Logstash will complain, even if codec is unused. default :codec, "plain" # The message string to use in the event. config :message, :validate => :string, :default => "Hello World!" # Set how frequently messages should be sent. # # The default, `1`, means send a message every second. config :interval, :validate => :number, :default => 1

Slide 30

Slide 30 text

Plugin Anatomy Register & set instance variables 30 public def register @host = Socket.gethostname end # def register

Slide 31

Slide 31 text

Plugin Anatomy Input "run" block 31 def run(queue) Stud.interval(@interval) do event = LogStash::Event.new("message" => @message, "host" => @host) decorate(event) queue << event end # loop end # def run end # class LogStash::Inputs::Example

Slide 32

Slide 32 text

Plugin Anatomy Filter config example 32 # Setting the config_name here is required. This is how you # configure this filter from your Logstash config. # # filter { # example { message => "My message..." } # } config_name "example" # Replace the message with this value. config :new_message, :validate => :string, :default => "Hello World!"

Slide 33

Slide 33 text

Plugin Anatomy Filter method example 33 public def filter(event) if @message # Replace the event message with our message as configured # in the config file. # OLD WAY: event["message"] = @new_message # New WAY below: event.set('message') = @new_message end # filter_matched should go in the last line of our code filter_matched(event) end # def filter end # class LogStash::Filters::Example

Slide 34

Slide 34 text

Plugin Anatomy Output method example 34 public # Takes an array of events def multi_receive(events) # code goes here. # Be sure to loop through all events in the array end # def multi_receive public # Needed for logstash < 2.2 compatibility # Takes events one at a time def receive(event) # code goes here. end # def receive

Slide 35

Slide 35 text

Plugin Anatomy Output threading consideration 35 # If declared logstash will only allow a single instance of this plugin # to exist, regardless of how many CPU cores logstash detects. This is best # used in cases like the File output, where separate threads writing to a # single file would only cause problems. # # respond_to? check needed for backwards compatibility with < 2.2 Logstashes declare_workers_not_supported! if self.respond_to? (:declare_workers_not_supported!)

Slide 36

Slide 36 text

Plugin Anatomy Output threading consideration 36 # If declared threadsafe logstash will only ever create one # instance of this plugin per pipeline. # That instance will be shared across all workers # It is up to the plugin author to correctly write concurrent code! # # respond_to? check needed for backwards compatibility with < 2.2 Logstashes declare_threadsafe! if self.respond_to?(:declare_threadsafe!)

Slide 37

Slide 37 text

Plugin Anatomy Codec register example 37 public def register @lines = LogStash::Codecs::Line.new @lines.charset = "UTF-8" end

Slide 38

Slide 38 text

Plugin Anatomy Codec decode example 38 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 # New way: # replace = { "message" => line.get('message').to_s + @append }

Slide 39

Slide 39 text

Plugin Anatomy Codec encode example 39 public def encode(event) @on_event.call(event, event["message"].to_s + @append + NL) end # def encode # New way: # @on_event.call(event, event.get('message').to_s + @append + NL)

Slide 40

Slide 40 text

GitHub time! 40 Examples (time permitting)

Slide 41

Slide 41 text

Publish your plugin • https://www.elastic.co/guide/en/logstash/current/submitting-plugin.html • Go solo (self-publish to RubyGems), or • Submit to the logstash-plugins repository on GitHub • Acceptance Guidelines • Code Review • Must include tests • Submit an issue at https://github.com/elastic/logstash 41

Slide 42

Slide 42 text

‹#› Questions? I'll be here all night…