To develop a new filter for Logstash, you build a self-contained Ruby gemwhose source code lives in its own GitHub repository. The Ruby gem can then behosted and shared on RubyGems.org. You can use the example filterimplementation as a starting point. (If you’re unfamiliar withRuby, you can find an excellent quickstart guide athttps://www.ruby-lang.org/en/documentation/quickstart/.)
Let’s step through creating a filter plugin using the example filter plugin.
Each Logstash plugin lives in its own GitHub repository. To create a new repository for your plugin:
Specify the following settings for your new repo:
logstash-filter-pluginname
.You can now create your own Logstash plugin in seconds! The generate
subcommand of bin/logstash-plugin
creates the foundationfor a new Logstash plugin with templatized files. It creates the correct directory structure, gemspec files, and dependencies so youcan start adding custom code to process data with Logstash.
For more information, see Generating Plugins
Alternatively, you can use the examples repo we host on github.com
Clone your plugin. Replace GITUSERNAME
with your github username, andMYPLUGINNAME
with your plugin name.
git clone https://github.com/GITUSERNAME/logstash-
filter-MYPLUGINNAME.git
git clone git@github.com:GITUSERNAME/logstash
-filter-MYPLUGINNAME.git
cd logstash-filter-MYPLUGINNAME
You don’t want to include the example .git directory or its contents, so deleteit before you copy the example.
cd /tmp
git clone https://github.com/logstash-plugins/logstash
-filter-example.git
cd logstash-filter-example
rm -rf .git
cp -R * /path/to/logstash-filter-mypluginname/
Rename the following files to match the name of your plugin.
logstash-filter-example.gemspec
example.rb
example_spec.rb
cd /path/to/logstash-filter-mypluginnamemv logstash-filter-example.gemspec logstash-filter-mypluginname.gemspecmv lib/logstash/filters/example.rb lib/logstash/filters/mypluginname.rbmv spec/filters/example_spec.rb spec/filters/mypluginname_spec.rb
Your file structure should look like this:
$ tree logstash-filter-mypluginname├── Gemfile├── LICENSE├── README.md├── Rakefile├── lib│ └── logstash│ └── filters│ └── mypluginname.rb├── logstash-filter-mypluginname.gemspec└── spec └── filters └── mypluginname_spec.rb
For more information about the Ruby gem file structure and an excellentwalkthrough of the Ruby gem creation process, seehttp://timelessrepo.com/making-ruby-gems
Before we dive into the details, open up the plugin file in your favorite text editorand take a look.
# encoding: utf-8require "logstash/filters/base"require "logstash/namespace"# Add any asciidoc formatted documentation here# This example filter will replace the contents of the default# message field with whatever you specify in the configuration.## It is only intended to be used as an example.class LogStash::Filters::Example < LogStash::Filters::Base # 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 :message, :validate => :string, :default => "Hello World!" public def register # Add instance variables end # def register public def filter(event) if @message # Replace the event message with our message as configured in the # config file. event.set("message", @message) end # filter_matched should go in the last line of our successful code filter_matched(event) end # def filterend # class LogStash::Filters::Example
Now let’s take a line-by-line look at the example plugin.
It seems like a small thing, but remember to specify the encoding at thebeginning of your plugin code:
# encoding: utf-8
Logstash depends on things being in UTF-8, so we put this here to tell the Rubyinterpreter that we’re going to be using the UTF-8 encoding.
Logstash filter plugins require parent classes defined inlogstash/filters/base
and logstash/namespace:
require "logstash/filters/base"require "logstash/namespace"
Of course, the plugin you build may depend on other code, or even gems. Just putthem here along with these Logstash dependencies.
Let’s go through the various elements of the plugin itself.
The filter plugin class should be a subclass ofLogStash::Filters::Base
:
class LogStash::Filters::Example < LogStash::Filters::Base
The class name should closely mirror the plugin name, for example:
LogStash::Filters::Example
config :variable_name, :validate => :variable_type, :default => "Default value", :required => boolean, :deprecated => boolean, :obsolete => string
The configuration, or config
section allows you to define as many (or as few)parameters as are needed to enable Logstash to process events.
There are several configuration attributes:
:validate
- allows you to enforce passing a particular data type to Logstashfor this configuration option, such as :string
, :password
, :boolean
,:number
, :array
, :hash
, :path
(a file-system path), uri
, :codec
(since1.2.0), :bytes
. Note that this also works as a coercionin that if I specify "true" for boolean (even though technically a string), itwill become a valid boolean in the config. This coercion works for the:number
type as well where "1.2" becomes a float and "22" is an integer.:default
- lets you specify a default value for a parameter:required
- whether or not this parameter is mandatory (a Boolean true
or:list
- whether or not this value should be a list of values. Will typecheck the list members, and convert scalars to one element lists. Note that this mostly obviates the array type, though if you need lists of complex objects that will be more suitable.false
):deprecated
- informational (also a Boolean true
or false
):obsolete
- used to declare that a given setting has been removed and is no longer functioning. The idea is to provide an informed upgrade path to users who are still using a now-removed setting.Logstash filters must implement the register
and filter
methods.
public def register end # def register
The Logstash register
method is like an initialize
method. It was originallycreated to enforce having super
called, preventing headaches for newbies.(Note: It may go away in favor of initialize
, in conjunction with someenforced testing to ensure super
is called.)
public
means the method can be called anywhere, not just within the class.This is the default behavior for methods in Ruby, but it is specified explicitlyhere anyway.
You can also assign instance variables here (variables prepended by @
).Configuration variables are now in scope as instance variables, like @message
public def filter(event) if @message # Replace the event message with our message as configured in the # config file. event.set("message", @message) end # filter_matched should go in the last line of our successful code filter_matched(event)end # def filter
The plugin’s filter
method is where the actual filtering work takes place!Inside the filter
method you can refer to the event data using the Event
object. Event is the main object that encapsulates data flow internally in Logstashand provides an API for the plugin developers to interact with theevent’s content.
The filter
method should also handle any event dependent configuration byexplicitly calling the sprintf
method available in Event class. For example:
field_foo = event.sprintf(field)
Note that configuration variables are now in scope as instance variables, like@message
filter_matched(event)
Calling the filter_matched
method upon successful execution of the plugin willensure that any fields or tags added through the Logstash configuration for thisfilter will be handled correctly. For example, any add_field
, remove_field
,add_tag
and/or remove_tag
actions will be performed at this time.
Event methods such as event.cancel
are now available to control the workflowof the event being processed.
At this point in the process you have coded your plugin and are ready to builda Ruby Gem from it. The following information will help you complete the process.
A require
statement in Ruby is used to include necessary code. In some casesyour plugin may require additional files. For example, the collectd pluginusesthe types.db
file provided by collectd. In the main directory of your plugin,a file called vendor.json
is where these files are described.
The vendor.json
file contains an array of JSON objects, each describing a filedependency. This example comes from thecollectdcodec plugin:
[{ "sha1": "a90fe6cc53b76b7bdd56dc57950d90787cb9c96e", "url": "http://collectd.org/files/collectd-5.4.0.tar.gz", "files": [ "/src/types.db" ]}]
sha1
is the sha1 signature used to verify the integrity of the filereferenced by url
.url
is the address from where Logstash will download the file.files
is an optional array of files to extract from the downloaded file.Note that while tar archives can use absolute or relative paths, treat them asabsolute in this array. If files
is not present, all files will beuncompressed and extracted into the vendor directory.Another example of the vendor.json
file is thegeoip
filter
The process used to download these dependencies is to call rake vendor
. Thiswill be discussed further in the testing section of this document.
Another kind of external dependency is on jar files. This will be describedin the "Add a gemspec
file" section.
Gemfiles allow Ruby’s Bundler to maintain the dependencies for your plugin.Currently, all we’ll need is the Logstash gem, for testing, but if you requireother gems, you should add them in here.
See Bundler’s Gemfile page for more details.
source 'https://rubygems.org'gemspecgem "logstash", :github => "elastic/logstash", :branch => "7.0"
Gemspecs define the Ruby gem which will be built and contain your plugin.
More information can be found on theRubygems Specification page.
Gem::Specification.new do |s| s.name = 'logstash-filter-example' s.version = '0.1.0' s.licenses = ['Apache License (2.0)'] s.summary = "This filter 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 = 'info@elastic.co' s.homepage = "http://www.elastic.co/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" => "filter" } # Gem dependencies s.add_runtime_dependency "logstash-core-plugin-api", ">= 1.60", "<= 2.99" s.add_development_dependency 'logstash-devutils'end
It is appropriate to change these values to fit your plugin. In particular,s.name
and s.summary
should reflect your plugin’s name and behavior.
s.licenses
and s.version
are also important and will come into play whenyou are ready to publish your plugin.
Logstash and all its plugins are licensed underApache License, version 2 ("ALv2").If you make your plugin publicly available via RubyGems.org,please make sure to have this line in your gemspec:
s.licenses = ['Apache License (2.0)']
The gem version, designated by s.version
, helps track changes to plugins overtime. You should use semver versioning strategy for version numbers.
At the bottom of the gemspec
file is a section with a comment:Gem dependencies
. This is where any other needed gems must be mentioned. Ifa gem is necessary for your plugin to function, it is a runtime dependency. Ifa gem are only used for testing, then it would be a development dependency.
You can also have versioning requirements for your dependencies—including otherLogstash plugins:
# Gem dependencies s.add_runtime_dependency "logstash-core-plugin-api", ">= 1.60", "<= 2.99" s.add_development_dependency 'logstash-devutils'
This gemspec has a runtime dependency on the logstash-core-plugin-api and requires thatit have a version number greater than or equal to version 1.60 and less than or equal to version 2.99.
All plugins have a runtime dependency on the logstash-core-plugin-api
gem, anda development dependency on logstash-devutils
.
In some cases, such as theElasticsearch output plugin,your code may depend on a jar file. In cases such as this, the dependency isadded in the gemspec file in this manner:
# Jar dependencies s.requirements << "jar 'org.elasticsearch:elasticsearch', '5.0.0'" s.add_runtime_dependency 'jar-dependencies'
With these both defined, the install process will search for the required jarfile at http://mvnrepository.com and download the specified version.
Documentation is an important part of your plugin. All plugin documentation isrendered and placed in theLogstash Reference and the Versioned plugin docs.
See Documenting your plugin for tips and guidelines.
Logstash loves tests. Lots of tests. If you’re using your new filterplugin in a production environment, you’ll want to have some tests to ensure youare not breaking any existing functionality.
A full exposition on RSpec is outside the scope of this document. Learnmore about RSpec at http://rspec.info
For help learning about tests and testing, look in thespec/filters/
directory of several other similarplugins.
Now let’s start with a fresh clone of the plugin, build it and run the tests.
Clone your plugin into a temporary location Replace GITUSERNAME
withyour github username, and MYPLUGINNAME
with your plugin name.
git clone https://github.com/GITUSERNAME/logstash-
filter-MYPLUGINNAME.git
git clone git@github.com:GITUSERNAME/logstash-
filter-MYPLUGINNAME.git
cd logstash-filter-MYPLUGINNAME
Then, you’ll need to install your plugins dependencies with bundler:
bundle install
If your plugin has an external file dependency described in vendor.json
, youmust download that dependency before running or testing. You can do this byrunning:
rake vendor
And finally, run the tests:
bundle exec rspec
You should see a success message, which looks something like this:
Finished in 0.034 seconds1 example, 0 failures
Hooray! You’re almost there! (Unless you saw failures… you should fix those first).
Now you’re ready to build your (well-tested) plugin into a Ruby gem.
You already have all the necessary ingredients, so let’s go ahead and run thebuild command:
gem build logstash-filter-example.gemspec
That’s it! Your gem should be built and be in the same path with the name
logstash-filter-mypluginname-0.1.0.gem
The s.version
number from your gemspec file will provide the gem version, inthis case, 0.1.0
.
You should test install your plugin into a clean installation of Logstash.Download the latest version from theLogstash downloads page.
Untar and cd in to the directory:
curl -O https://download.elastic.co/logstash/logstash/logstash-7.0.1.tar.gztar xzvf logstash-7.0.1.tar.gzcd logstash-7.0.1
Using the plugin tool, we can install the gem we just built.
Replace /my/logstash/plugins
with the correct path to the gem for yourenvironment, and 0.1.0
with the correct version number from the gemspec file.
bin/logstash-plugin install /my/logstash/plugins/logstash-filter-example/logstash-filter-example-0.1.0.gem
After running this, you should see feedback from Logstash that it wassuccessfully installed:
validating /my/logstash/plugins/logstash-filter-example/logstash-filter-example-0.1.0.gem >= 0Valid logstash plugin. Continuing...Successfully installed 'logstash-filter-example' with version '0.1.0'
You can also use the Logstash plugin tool to determine which plugins arecurrently available:
bin/logstash-plugin list
Depending on what you have installed, you might see a short or long list ofplugins: inputs, codecs, filters and outputs.
-e
flag.Your results will depend on what your filter plugin is designed to do.
bin/logstash -e 'input { stdin{} } filter { example {} } output {stdout { codec => rubydebug }}'
Test your filter by sending input through stdin
and output (after filtering)through stdout
with the rubydebug
codec, which enhances readability.
In the case of the example filter plugin, any text you send will bereplaced by the contents of the message
configuration parameter, the defaultvalue being "Hello World!":
Testing 1, 2, 3{ "message" => "Hello World!", "@version" => "1", "@timestamp" => "2015-01-27T19:17:18.932Z", "host" => "cadenza"}
Feel free to experiment and test this by changing the message
parameter:
bin/logstash -e 'input { stdin{} } filter { example { message => "This is a new message!"} } output {stdout { codec => rubydebug }}'
Congratulations! You’ve built, deployed and successfully run a Logstashfilter.
Logstash uses RubyGems.org as its repository for all pluginartifacts. Once you have developed your new plugin, you can make it available toLogstash users by simply publishing it to RubyGems.org.
Logstash and all its plugins are licensed underApache License, version 2 ("ALv2").If you make your plugin publicly available via RubyGems.org,please make sure to have this line in your gemspec:
s.licenses = ['Apache License (2.0)']
To begin, you’ll need an account on RubyGems.org
After creating an account,obtain an APIkey from RubyGems.org. By default, RubyGems uses the file ~/.gem/credentials
to store your API key. These credentials will be used to publish the gem.Replace username
and password
with the credentials you created atRubyGems.org:
curl -u username:password https://rubygems.org/api/v1/api_key.yaml > ~/.gem/credentialschmod 0600 ~/.gem/credentials
Before proceeding, make sure you have the right version in your gemspec fileand commit your changes.
s.version = '0.1.0'
To publish version 0.1.0 of your new logstash gem:
bundle installbundle exec rake vendorbundle exec rspecbundle exec rake publish_gem
Executing rake publish_gem
:
s.version = '0.1.0'
)That’s it! Your plugin is published! Logstash users can now install your pluginby running:
bin/logstash-plugin install logstash-filter-mypluginname
It is not required to contribute your source code tologstash-plugins github organization, butwe always welcome new plugins!
Some of the many benefits of having your plugin in the logstash-pluginsrepository are:
To begin migrating your plugin to logstash-plugins, simply create a newissue inthe Logstash repository. When the acceptance guidelines are completed, we willfacilitate the move to the logstash-plugins organization using the recommendedgithub process.