logstash-filter-logfmt 0.1.12

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 1cb426613c214f5d0316c50a63d9d13abbfac68d
4
+ data.tar.gz: 6e67337f908fb1a51e9e672440825c7b0c40131c
5
+ SHA512:
6
+ metadata.gz: d3d8b52cf14b4df3aefef109237eba3be3c4c9eb14d2a94b5d24557bb3728c0c38518bf229ee0d8412bc1c5fb6592337756f4d1328df9545c5f928bece32b3f1
7
+ data.tar.gz: 266320d3cc1d0ad87da4f0a3720a49bf969f57846bea9701801dabe4fc10e7c97366592ec7a454b0014ac83fd7d7314ea3e2c12c81bd752cf62379e4070aa3b8
@@ -0,0 +1,41 @@
1
+ .DS_Store
2
+ *.gem
3
+
4
+
5
+ *.gem
6
+ *.rbc
7
+ /.config
8
+ /coverage/
9
+ /InstalledFiles
10
+ /pkg/
11
+ /spec/reports/
12
+ /test/tmp/
13
+ /test/version_tmp/
14
+ /tmp/
15
+
16
+ ## Specific to RubyMotion:
17
+ .dat*
18
+ .repl_history
19
+ build/
20
+
21
+ ## Documentation cache and generated files:
22
+ /.yardoc/
23
+ /_yardoc/
24
+ /doc/
25
+ /rdoc/
26
+
27
+ ## Environment normalisation:
28
+ /.bundle/
29
+ /vendor/bundle
30
+ /lib/bundler/man/
31
+
32
+ # for a library or gem, you might want to ignore these files since the code is
33
+ # intended to run in multiple environments; otherwise, check them in:
34
+ # Gemfile.lock
35
+ .ruby-version
36
+ .ruby-gemset
37
+
38
+ # unless supporting rvm < 1.11.0 or doing something fancy, ignore this:
39
+ .rvmrc
40
+
41
+ Gemfile.lock
@@ -0,0 +1,9 @@
1
+ sudo: false
2
+ language: ruby
3
+ jdk:
4
+ - oraclejdk8
5
+ rvm:
6
+ - jruby-1.7.25
7
+ before_install: []
8
+ script:
9
+ - bundle exec rspec spec
File without changes
data/Gemfile ADDED
@@ -0,0 +1,2 @@
1
+ source 'https://rubygems.org'
2
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,13 @@
1
+ Copyright (c) 2015 Tim Buchwaldt / grandcentrinx GmbH <http://grandcentrix.net>
2
+
3
+ Licensed under the Apache License, Version 2.0 (the "License");
4
+ you may not use this file except in compliance with the License.
5
+ You may obtain a copy of the License at
6
+
7
+ http://www.apache.org/licenses/LICENSE-2.0
8
+
9
+ Unless required by applicable law or agreed to in writing, software
10
+ distributed under the License is distributed on an "AS IS" BASIS,
11
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ See the License for the specific language governing permissions and
13
+ limitations under the License.
@@ -0,0 +1,87 @@
1
+ # Logstash Logfmt Parser Plugin
2
+ [![Build Status](https://travis-ci.org/wheely/logstash-filter-logfmt.svg?branch=master)](https://travis-ci.org/wheely/logstash-filter-logfmt)
3
+
4
+
5
+ This is a plugin for [Logstash](https://github.com/elasticsearch/logstash), allowing logstash to parse logfmt-formatted messages.
6
+
7
+ It is fully free and fully open source. The license is Apache 2.0, meaning you are pretty much free to use it however you want in whatever way.
8
+
9
+ ## Documentation
10
+
11
+ Logstash provides infrastructure to automatically generate documentation for this plugin. We use the asciidoc format to write documentation so any comments in the source code will be first converted into asciidoc and then into html. All plugin documentation are placed under one [central location](http://www.elasticsearch.org/guide/en/logstash/current/).
12
+
13
+ - For formatting code or config example, you can use the asciidoc `[source,ruby]` directive
14
+ - For more asciidoc formatting tips, see the excellent reference here https://github.com/elasticsearch/docs#asciidoc-guide
15
+
16
+ ## Need Help?
17
+
18
+ Need help? Try #logstash on freenode IRC or the https://discuss.elastic.co/c/logstash discussion forum.
19
+
20
+ ## Developing
21
+
22
+ ### 1. Plugin Developement and Testing
23
+
24
+ #### Code
25
+ - To get started, you'll need JRuby with the Bundler gem installed.
26
+
27
+ - Create a new plugin or clone and existing from the GitHub [logstash-plugins](https://github.com/logstash-plugins) organization.
28
+
29
+ - Install dependencies
30
+ ```sh
31
+ bundle install
32
+ ```
33
+
34
+ #### Test
35
+
36
+ ```sh
37
+ bundle exec rspec
38
+ ```
39
+
40
+ The Logstash code required to run the tests/specs is specified in the `Gemfile` by the line similar to:
41
+ ```ruby
42
+ gem "logstash", :github => "elasticsearch/logstash", :branch => "1.5"
43
+ ```
44
+ To test against another version or a local Logstash, edit the `Gemfile` to specify an alternative location, for example:
45
+ ```ruby
46
+ gem "logstash", :github => "elasticsearch/logstash", :ref => "master"
47
+ ```
48
+ ```ruby
49
+ gem "logstash", :path => "/your/local/logstash"
50
+ ```
51
+
52
+ Then update your dependencies and run your tests:
53
+
54
+ ```sh
55
+ bundle install
56
+ bundle exec rspec
57
+ ```
58
+
59
+ ### 2. Running your unpublished Plugin in Logstash
60
+
61
+ #### 2.1 Run in a local Logstash clone
62
+
63
+ - Edit Logstash `tools/Gemfile` and add the local plugin path, for example:
64
+ ```ruby
65
+ gem "logstash-filter-logfmt", :path => "/your/local/logstash-filter-logfmt"
66
+ ```
67
+ - Update Logstash dependencies
68
+ ```sh
69
+ rake vendor:gems
70
+ ```
71
+ - Run Logstash with your plugin
72
+ ```sh
73
+ bin/logstash -e 'filter {logfmt { source => "message" }}'
74
+ ```
75
+ At this point any modifications to the plugin code will be applied to this local Logstash setup. After modifying the plugin, simply rerun Logstash.
76
+
77
+ #### 2.2 Run in an installed Logstash
78
+
79
+ - Build your plugin gem
80
+ ```sh
81
+ gem build logstash-filter-logfmt.gemspec
82
+ ```
83
+ - Install the plugin from the Logstash home
84
+ ```sh
85
+ bin/plugin install /your/local/plugin/logstash-filter-logfmt.gem
86
+ ```
87
+ - Start Logstash and proceed to test the plugin
@@ -0,0 +1,82 @@
1
+ # encoding: utf-8
2
+ require 'logstash/filters/base'
3
+ require 'logstash/namespace'
4
+ require 'logfmt'
5
+ require 'json'
6
+
7
+ # Add any asciidoc formatted documentation here
8
+ class LogStash::Filters::Logfmt < LogStash::Filters::Base
9
+ config_name 'logfmt'
10
+
11
+ # The source field to parse
12
+ config :source, validate: :string
13
+
14
+ # The target field to place all the data
15
+ config :target, validate: :string, default: 'logfmt'
16
+
17
+ # Remove source
18
+ config :remove_source, validate: :boolean, default: false
19
+
20
+ # Convert fields to json
21
+ config :convert_to_json, validate: :array, default: []
22
+
23
+ # Convert fields to strings
24
+ config :convert_to_string, validate: :array, default: []
25
+
26
+ def register
27
+ @logger.info 'Logfmt filter registered'
28
+ end
29
+
30
+ def filter(event)
31
+ return if resolve(event).nil?
32
+ filter_matched(event)
33
+ end
34
+
35
+ def resolve(event)
36
+ data = event.get(@source)
37
+ params = Logfmt.parse(data)
38
+
39
+ # log line should at least have level
40
+ return if !params['level'].is_a?(String) || params['level'].empty?
41
+
42
+ if params['stacktrace']
43
+ if params['stacktrace'].start_with?('[')
44
+ params['stacktrace'] = params['stacktrace'][1..-2].split('][')
45
+ else
46
+ params['stacktrace'] = params['stacktrace'].split(',')
47
+ end
48
+ end
49
+ event.set(@target, process_hash(params))
50
+ event.set(@source, nil) if @remove_source
51
+ return true
52
+ rescue => e
53
+ log_exception(e, data)
54
+ nil
55
+ end
56
+
57
+ private
58
+
59
+ def log_exception(e, data)
60
+ @logger.error({
61
+ msg: 'Failed to parse logfmt string',
62
+ error: {
63
+ message: e.message,
64
+ err: e.class.to_s,
65
+ data: data,
66
+ stacktrace: (e.backtrace && e.backtrace.join(','))
67
+ }
68
+ }.to_json)
69
+ end
70
+
71
+ def process_hash(hash)
72
+ hash.each_with_object({}) do |(key,value), all|
73
+ key_parts = key.split('.')
74
+ leaf = key_parts[0...-1].inject(all) { |h, k| h[k] ||= {} }
75
+ leaf[key_parts.last] = value
76
+ end.each_with_object({}) do |(key,value), all|
77
+ next all[key] = value.to_json if @convert_to_json.include?(key)
78
+ next all[key] = value.to_s if @convert_to_string.include?(key)
79
+ all[key] = value
80
+ end
81
+ end
82
+ end # class LogStash::Filters::Logfmt
@@ -0,0 +1,27 @@
1
+ Gem::Specification.new do |s|
2
+ s.name = 'logstash-filter-logfmt'
3
+ s.version = '0.1.12'
4
+ s.licenses = ['Apache License (2.0)']
5
+ s.summary = 'This filter may be used to decode Logfmt messages'
6
+ s.description = 'This gem is a logstash plugin required to be installed on top of the Logstash core pipeline using $LS_HOME/bin/plugin install logstash-filter-logfmt. This gem is not a stand-alone program'
7
+ s.authors = ['Tim Buchwaldt', 'Aleksandr Zykov']
8
+ s.email = 'alexandrz@gmail.com'
9
+ s.homepage = 'https://github.com/alexandrz/logstash-filter-logfmt/'
10
+ s.require_paths = ['lib']
11
+
12
+ # Files
13
+ s.files = `git ls-files`.split($OUTPUT_RECORD_SEPARATOR)
14
+
15
+ # Tests
16
+ s.test_files = s.files.grep(%r{^(test|spec|features)/})
17
+
18
+ # Special flag to let us know this is actually a logstash plugin
19
+ s.metadata = { 'logstash_plugin' => 'true', 'logstash_group' => 'filter' }
20
+
21
+ # Gem dependencies
22
+ s.add_runtime_dependency 'logstash-core-plugin-api', '>= 1.60', '<= 2.99'
23
+ s.add_runtime_dependency 'logfmt', '~> 0.0.8'
24
+
25
+ s.add_development_dependency 'logstash-devutils', ['>= 1.0.0']
26
+ s.add_development_dependency 'rspec'
27
+ end
@@ -0,0 +1,88 @@
1
+ require 'logstash/devutils/rspec/spec_helper'
2
+ require 'logstash/filters/logfmt'
3
+ require 'logstash/event'
4
+ require 'logstash/json'
5
+ require 'insist'
6
+
7
+ describe LogStash::Filters::Logfmt do
8
+ let(:logfmt_filter_options) do
9
+ {
10
+ 'source' => 'message',
11
+ 'convert_to_json' => ['params'],
12
+ 'convert_to_string' => ['status']
13
+ }
14
+ end
15
+ subject { LogStash::Filters::Logfmt.new(logfmt_filter_options) }
16
+
17
+ context '#resolve' do
18
+ let(:data) { %(time=2016-12-27T16:15:00.108+00:00 level=error status=200 response_status=401 response_body.error="Your user ID or license key could not be authenticated." msg="too many connection resets (due to Net::ReadTimeout - Net::ReadTimeout) after 89 requests on 47144016160240, last used 97.457622864 seconds ago" logger=Wheely::App::Pusher::SmsTransport::Devino err=Net::HTTP::Persistent::Error phone=+79263030599 body="4474 - \u0432\u0430\u0448 \u043A\u043E\u0434" thread=47144031566240 stacktrace="[/usr/local/lib/ruby/2.2.0/net/protocol.rb:158:rescue in rbuf_fill][/usr/local/lib/ruby/2.2.0/net/protocol.rb:152:rbuf_fill][/usr/local/lib/ruby/2.2.0/net/protocol.rb:134:readuntil][/usr/local/lib/ruby/2.2.0/net/protocol.rb:144:readline][/usr/local/lib/ruby/2.2.0/net/http/response.rb:39:read_status_line][/usr/local/lib/ruby/2.2.0/net/http/response.rb:28:read_new][/usr/local/bundle/gems/aws-sdk-core-2.3.0/lib/seahorse/client/net_http/patches.rb:29:block in new_transport_request][/usr/local/bundle/gems/aws-sdk-core-2.3.0/lib/seahorse/client/net_http/patches.rb:26:catch][/usr/local/bundle/gems/aws-sdk-core-2.3.0/lib/seahorse/client/net_http/patches.rb:26:new_transport_request][/usr/local/lib/ruby/2.2.0/net/http.rb:1384:request][/usr/local/bundle/gems/net-http-persistent-2.9.4/lib/net/http/persistent.rb:999:request][/app/lib/sms_transport/devino.rb:45:send_message][/app/lib/sms_transport/devino.rb:22:delivery][/app/lib/sms_transport.rb:48:block (2 levels) in delivery_message][/app/lib/common/statsd.rb:63:call][/app/lib/common/statsd.rb:63:block in delivery_time][/usr/local/bundle/gems/dogstatsd-ruby-1.6.0/lib/statsd.rb:199:time][/app/lib/common/statsd.rb:63:delivery_time][/app/lib/sms_transport.rb:48:block in delivery_message][/app/lib/sms_transport.rb:46:each][/app/lib/sms_transport.rb:46:find][/app/lib/sms_transport.rb:46:delivery_message][/app/lib/sms_transport.rb:28:delivery][/app/lib/tasks/delivery_sms_task.rb:22:block in delivery][/usr/local/bundle/gems/logfoo-0.1.4/lib/logfoo/context.rb:33:context][/app/lib/tasks/delivery_sms_task.rb:16:delivery][/app/lib/consumers/notifications_consumer.rb:40:process][/usr/local/bundle/gems/hutch-0.21.0/lib/hutch/tracers/null_tracer.rb:10:handle][/usr/local/bundle/gems/hutch-0.21.0/lib/hutch/worker.rb:119:handle_message][/usr/local/bundle/gems/hutch-0.21.0/lib/hutch/worker.rb:101:block in setup_queue][/usr/local/bundle/gems/bunny-2.3.1/lib/bunny/consumer.rb:56:call][/usr/local/bundle/gems/bunny-2.3.1/lib/bunny/consumer.rb:56:call][/usr/local/bundle/gems/bunny-2.3.1/lib/bunny/channel.rb:1721:block in handle_frameset][/usr/local/bundle/gems/bunny-2.3.1/lib/bunny/consumer_work_pool.rb:97:call][/usr/local/bundle/gems/bunny-2.3.1/lib/bunny/consumer_work_pool.rb:97:block (2 levels) in run_loop][/usr/local/bundle/gems/bunny-2.3.1/lib/bunny/consumer_work_pool.rb:92:loop][/usr/local/bundle/gems/bunny-2.3.1/lib/bunny/consumer_work_pool.rb:92:block in run_loop][/usr/local/bundle/gems/bunny-2.3.1/lib/bunny/consumer_work_pool.rb:91:catch][/usr/local/bundle/gems/bunny-2.3.1/lib/bunny/consumer_work_pool.rb:91:run_loop]") }
19
+ let(:event) { LogStash::Event.new('message' => data) }
20
+ it 'should decode valid logfmt data' do
21
+ insist { subject.resolve(event) } == true
22
+ insist { event.is_a? LogStash::Event }
23
+ insist { event.get('logfmt')['time'] } == '2016-12-27T16:15:00.108+00:00'
24
+ insist { event.get('logfmt')['level'] } == 'error'
25
+ insist { event.get('logfmt')['response_status'] } == 401
26
+ insist { event.get('logfmt')['status'] } == '200'
27
+ insist { event.get('logfmt')['response_body']['error'] } == 'Your user ID or license key could not be authenticated.'
28
+ insist { event.get('logfmt')['stacktrace'].is_a? Array }
29
+ end
30
+ context 'equal in valie' do
31
+ let(:data) { %(time=2016-12-29T12:43:31.029+00:00 level=info method=GET path=/v6/services query=position=55.80450799126121%2C37.58320759981871&selected_service_id=4feb17260c3dba1929000001 status=200 len=0 addr=194.186.99.94 duration=0.050610643 progname=null logger=Rack::Wheely::Logger) }
32
+ it 'should decode valid logfmt data' do
33
+ insist { subject.resolve(event) } == true
34
+ insist { event.is_a? LogStash::Event }
35
+ insist { event.get('logfmt')['time'] } == '2016-12-29T12:43:31.029+00:00'
36
+ insist { event.get('logfmt')['level'] } == 'info'
37
+ insist { event.get('logfmt')['path'] } == '/v6/services'
38
+ insist { event.get('logfmt')['query'] } == 'position=55.80450799126121%2C37.58320759981871&selected_service_id=4feb17260c3dba1929000001'
39
+ end
40
+ end
41
+ context 'escaped quotation in value' do
42
+ let(:data) { 'level=info method="GET \"DOM\""' }
43
+ it 'should decode valid logfmt data' do
44
+ insist { subject.resolve(event) } == true
45
+ insist { event.is_a? LogStash::Event }
46
+ insist { event.get('logfmt')['level'] } == 'info'
47
+ insist { event.get('logfmt')['method'] } == 'GET "DOM"'
48
+ end
49
+ end
50
+ context 'logfmt-ruby issue #9' do
51
+ let(:data) { 'level=error msg="Failed to confirm push notification" logger=Notifications params="{\"token\":\"3c201500-d6e7-4185-a814-9701db7bb0fa\",\"device_id\":\"52C929B2-F5A6-475D-82C5-A54815CBD5BA\"}" thread=47144021941860' }
52
+ it 'should decode valid logfmt data' do
53
+ insist { subject.resolve(event) } == true
54
+ insist { event.is_a? LogStash::Event }
55
+ insist { event.get('logfmt')['thread'] } == 47144021941860
56
+ end
57
+ end
58
+ context 'conver_to_json' do
59
+ let(:data) { 'level=error params.user_id=1 params.foo=bar' }
60
+ it 'should decode valid logfmt data' do
61
+ insist { subject.resolve(event) } == true
62
+ insist { event.is_a? LogStash::Event }
63
+ insist { event.get('logfmt')['params'] } == {user_id: 1, foo: 'bar'}.to_json
64
+ end
65
+ end
66
+ it 'should be fast', performance: true do
67
+ iterations = 5_000
68
+ count = 0
69
+ # Warmup
70
+ 10_000.times { subject.resolve(event) {} }
71
+ start = Time.now
72
+ iterations.times do
73
+ subject.resolve(event) do |_event|
74
+ count += 1
75
+ end
76
+ end
77
+ duration = Time.now - start
78
+ insist { count } == iterations
79
+ puts "codecs/logfmt rate: #{'%02.0f/sec' % (iterations / duration)}, elapsed: #{duration}s"
80
+ end
81
+ context 'processing plain text' do
82
+ let(:data) { "something containing level word that isn't json" }
83
+ it 'falls back to plain text' do
84
+ insist { subject.resolve(event) } == nil
85
+ end
86
+ end
87
+ end
88
+ end
metadata ADDED
@@ -0,0 +1,120 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: logstash-filter-logfmt
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.12
5
+ platform: ruby
6
+ authors:
7
+ - Tim Buchwaldt
8
+ - Aleksandr Zykov
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2017-03-06 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: logstash-core-plugin-api
16
+ requirement: !ruby/object:Gem::Requirement
17
+ requirements:
18
+ - - ">="
19
+ - !ruby/object:Gem::Version
20
+ version: '1.60'
21
+ - - "<="
22
+ - !ruby/object:Gem::Version
23
+ version: '2.99'
24
+ type: :runtime
25
+ prerelease: false
26
+ version_requirements: !ruby/object:Gem::Requirement
27
+ requirements:
28
+ - - ">="
29
+ - !ruby/object:Gem::Version
30
+ version: '1.60'
31
+ - - "<="
32
+ - !ruby/object:Gem::Version
33
+ version: '2.99'
34
+ - !ruby/object:Gem::Dependency
35
+ name: logfmt
36
+ requirement: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: 0.0.8
41
+ type: :runtime
42
+ prerelease: false
43
+ version_requirements: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: 0.0.8
48
+ - !ruby/object:Gem::Dependency
49
+ name: logstash-devutils
50
+ requirement: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: 1.0.0
55
+ type: :development
56
+ prerelease: false
57
+ version_requirements: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: 1.0.0
62
+ - !ruby/object:Gem::Dependency
63
+ name: rspec
64
+ requirement: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ type: :development
70
+ prerelease: false
71
+ version_requirements: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ description: This gem is a logstash plugin required to be installed on top of the
77
+ Logstash core pipeline using $LS_HOME/bin/plugin install logstash-filter-logfmt.
78
+ This gem is not a stand-alone program
79
+ email: alexandrz@gmail.com
80
+ executables: []
81
+ extensions: []
82
+ extra_rdoc_files: []
83
+ files:
84
+ - ".gitignore"
85
+ - ".travis.yml"
86
+ - CHANGELOG.md
87
+ - Gemfile
88
+ - LICENSE
89
+ - README.md
90
+ - lib/logstash/filters/logfmt.rb
91
+ - logstash-filter-logfmt.gemspec
92
+ - spec/filters/logfmt_spec.rb
93
+ homepage: https://github.com/alexandrz/logstash-filter-logfmt/
94
+ licenses:
95
+ - Apache License (2.0)
96
+ metadata:
97
+ logstash_plugin: 'true'
98
+ logstash_group: filter
99
+ post_install_message:
100
+ rdoc_options: []
101
+ require_paths:
102
+ - lib
103
+ required_ruby_version: !ruby/object:Gem::Requirement
104
+ requirements:
105
+ - - ">="
106
+ - !ruby/object:Gem::Version
107
+ version: '0'
108
+ required_rubygems_version: !ruby/object:Gem::Requirement
109
+ requirements:
110
+ - - ">="
111
+ - !ruby/object:Gem::Version
112
+ version: '0'
113
+ requirements: []
114
+ rubyforge_project:
115
+ rubygems_version: 2.4.8
116
+ signing_key:
117
+ specification_version: 4
118
+ summary: This filter may be used to decode Logfmt messages
119
+ test_files:
120
+ - spec/filters/logfmt_spec.rb