logstash-codec-loginsight 0.1.48

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: ff605c480d5ebf65f598c5012b05cce22be807ab
4
+ data.tar.gz: 5d49e4b6ab13c92be8e91cc94300ecca8773a55f
5
+ SHA512:
6
+ metadata.gz: 94eb279833bd8387916f0e10f7639948870db278a28e4d0108de9f4ee90231be03dc1a8c64974e3fc122e0f89c4e712b4c8f849e70a06c0c6268593b4640bfcb
7
+ data.tar.gz: c961ed872c861ceab49d18dfdde26b0809856a413f7d37358eb6fe69f4e8ee2266a73204fb2cd7b41d99951922f9707770d23676bc98c781d76f533c9826cde0
data/CONTRIBUTORS ADDED
@@ -0,0 +1,14 @@
1
+ Contributors:
2
+ * Alan J Castonguay (alanjcastonguay)
3
+
4
+ Inspired by logstash-codec-json, from these fine people:
5
+
6
+ Contributors:
7
+ * Colin Surprenant (colinsurprenant)
8
+ * Jordan Sissel (jordansissel)
9
+ * João Duarte (jsvd)
10
+ * Kurt Hurtado (kurtado)
11
+ * Nick Ethier (nickethier)
12
+ * Pier-Hugues Pellerin (ph)
13
+ * Richard Pijnenburg (electrical)
14
+ * Tal Levy (talevy)
data/Gemfile ADDED
@@ -0,0 +1,2 @@
1
+ source 'https://rubygems.org'
2
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,11 @@
1
+ Licensed under the Apache License, Version 2.0 (the "License");
2
+ you may not use this file except in compliance with the License.
3
+ You may obtain a copy of the License at
4
+
5
+ http://www.apache.org/licenses/LICENSE-2.0
6
+
7
+ Unless required by applicable law or agreed to in writing, software
8
+ distributed under the License is distributed on an "AS IS" BASIS,
9
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10
+ See the License for the specific language governing permissions and
11
+ limitations under the License.
data/README.md ADDED
@@ -0,0 +1,123 @@
1
+ # logstash-codec-loginsight
2
+
3
+ This is a plugin for [Logstash](https://github.com/elastic/logstash), converting events received from [VMware vRealize Log Insight](https://www.vmware.com/support/pubs/log-insight-pubs.html) via [logstash-input-http](https://www.elastic.co/guide/en/logstash/current/plugins-inputs-http.html).
4
+
5
+ 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.
6
+
7
+ ## Installation from rubygems
8
+
9
+ [logstash-codec-loginsight](http://rubygems.org/gems/logstash-codec-loginsight) is hosted on rubygems.org. [Download and install the latest gem](https://www.elastic.co/guide/en/logstash/current/working-with-plugins.html) in your Logstash deployment:
10
+
11
+ ```sh
12
+ bin/logstash-plugin install logstash-codec-loginsight
13
+ ```
14
+
15
+ Verify installed version:
16
+ ```sh
17
+ bin/logstash-plugin list --verbose logstash-codec-loginsight
18
+ logstash-codec-loginsight (x.y.z)
19
+ ```
20
+
21
+ ## Usage
22
+
23
+ The codec is designed to be chained with the `logstash-input-http` plugin. Log events are forwarded from Log Insight with CFAPI on port 9000 (non-ssl) or 9543 (ssl), targetting and instance of `logstash-input-http` configured with additional codec.
24
+
25
+ ```
26
+ input {
27
+ http {
28
+ port=>9000
29
+ additional_codecs=>{ "application/json" => "loginsight"}
30
+ }
31
+ }
32
+ ```
33
+
34
+ | option | default | notes |
35
+ | --- | --- | --- |
36
+ | `charset` | `UTF-8` | The character encoding used in this codec.
37
+
38
+ ## AsciiDocs
39
+
40
+ 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.elastic.co/guide/en/logstash/current/).
41
+
42
+ - For formatting code or config example, you can use the asciidoc `[source,ruby]` directive
43
+ - For more asciidoc formatting tips, see the excellent reference here https://github.com/elastic/docs#asciidoc-guide
44
+
45
+ ## Need Help?
46
+
47
+ Need help? Try #logstash on freenode IRC or the https://discuss.elastic.co/c/logstash discussion forum.
48
+
49
+ ## Developing
50
+
51
+ ### 1. Plugin Developement and Testing
52
+
53
+ #### Code
54
+ - To get started, you'll need JRuby with the Bundler gem installed.
55
+
56
+ - Create a new plugin or clone and existing from the GitHub [logstash-plugins](https://github.com/logstash-plugins) organization. We also provide [example plugins](https://github.com/logstash-plugins?query=example).
57
+
58
+ - Install dependencies
59
+ ```sh
60
+ bundle install
61
+ ```
62
+
63
+ #### Test
64
+
65
+ - Update your dependencies
66
+
67
+ ```sh
68
+ bundle install
69
+ ```
70
+
71
+ - Run tests
72
+
73
+ ```sh
74
+ bundle exec rspec
75
+ ```
76
+
77
+ ### 2. Running the local, unpublished plugin in Logstash
78
+
79
+ #### 2.1 Run in a local Logstash clone
80
+
81
+ - Edit Logstash `Gemfile` and add the local plugin path, for example:
82
+ ```ruby
83
+ gem "logstash-codec-loginsight", :path => "/your/local/logstash-codec-loginsight"
84
+ ```
85
+ - Install plugin
86
+ ```sh
87
+ # Logstash 2.3 and higher
88
+ bin/logstash-plugin install --no-verify
89
+
90
+ # Prior to Logstash 2.3
91
+ bin/plugin install --no-verify
92
+
93
+ ```
94
+ - Run Logstash with your plugin
95
+ ```sh
96
+ bin/logstash --debug --log.level=debug -e 'input { http {port=>9000 additional_codecs=>{ "application/json" => "loginsight"} } } output { stdout {codec=>rubydebug} }'
97
+ ```
98
+ At this point any modifications to the plugin code will be applied to this local Logstash setup. After modifying the plugin, simply rerun Logstash.
99
+
100
+ #### 2.2 Run in an installed Logstash
101
+
102
+ You can use the same **2.1** method to run your plugin in an installed Logstash by editing its `Gemfile` and pointing the `:path` to your local plugin development directory or you can build the gem and install it using:
103
+
104
+ - Build your plugin gem
105
+ ```sh
106
+ gem build logstash-filter-awesome.gemspec
107
+ ```
108
+ - Install the plugin from the Logstash home
109
+ ```sh
110
+ # Logstash 2.3 and higher
111
+ bin/logstash-plugin install --no-verify
112
+
113
+ # Prior to Logstash 2.3
114
+ bin/plugin install --no-verify
115
+
116
+ ```
117
+ - Start Logstash and proceed to test the plugin
118
+
119
+ ## Contributing
120
+
121
+ All contributions are welcome: ideas, patches, documentation, bug reports, complaints, and even something you drew up on a napkin.
122
+
123
+ For more information about contributing, see the [CONTRIBUTING](https://github.com/elastic/logstash/blob/master/CONTRIBUTING.md) file.
@@ -0,0 +1,106 @@
1
+ # encoding: utf-8
2
+ # Copyright © 2017 VMware, Inc. All Rights Reserved.
3
+ # SPDX-License-Identifier: Apache-2.0
4
+
5
+ require "logstash/codecs/base"
6
+ require "logstash/util/charset"
7
+ require "logstash/json"
8
+ require "logstash/event"
9
+ require "logstash/timestamp"
10
+
11
+ # This codec may be used to decode (via inputs) JSON-encoded messages,
12
+ # with timestamp and key=value fields, from VMware vRealize Log Insight via CFAPI.
13
+ #
14
+ # If this codec recieves a payload from an input that is not valid JSON,
15
+ # then it it will add a tag `_jsonparsefailure` with the payload stored
16
+ # in the `message` field.
17
+ #
18
+ # If this codec receives a payload from an input that is valid JSON,
19
+ # but which is not structured as Log Insight is expected to produce,
20
+ # then it will add a tag `_eventparsefailure` with the payload stored
21
+ # in the `message` field.
22
+ #
23
+ # input { http { port=>9000 additional_codecs=>{ "application/json" => "loginsight"} } }
24
+
25
+ class LogStash::Codecs::LogInsight < LogStash::Codecs::Base
26
+ config_name "loginsight"
27
+
28
+ # The character encoding used in this codec. Examples include "UTF-8" and
29
+ # "CP1252".
30
+ config :charset, :validate => ::Encoding.name_list, :default => "UTF-8"
31
+
32
+ def register
33
+ @logger.info("Log Insight Codec Registered")
34
+ @converter = LogStash::Util::Charset.new(@charset)
35
+ @converter.logger = @logger
36
+ end
37
+
38
+ def decode(data, &block)
39
+ parse(@converter.convert(data), &block)
40
+ end
41
+
42
+ private
43
+
44
+ def map_loginsight_messages(item, &block)
45
+ # map the fields and values to a hash, preserving an existing `message`
46
+ begin
47
+ event_hash = Hash[item["fields"].map { |f| [f["name"], f["content"]] }]
48
+ if event_hash.has_key?("message")
49
+ event_hash["_message"] = event_hash["message"]
50
+ end
51
+ rescue
52
+ event_hash = {}
53
+ end
54
+
55
+ # Add the message field, so it overwrites the above
56
+ event = LogStash::Event.new(event_hash.merge("message" => item["text"]))
57
+
58
+ # Make a Timestamp object from the source message (UTC Epoch in Milliseconds).
59
+ begin
60
+ newtime = LogStash::Timestamp.at( item["timestamp"].to_i / 1000.0 )
61
+ event.set( LogStash::Event::TIMESTAMP, newtime )
62
+ rescue
63
+ event.set( LogStash::Event::TIMESTAMP_FAILURE_FIELD, item["timestamp"])
64
+ end
65
+
66
+ yield event
67
+ end
68
+
69
+ def parse(json, &block)
70
+ decoded = LogStash::Json.load(json)
71
+
72
+ case decoded
73
+ when Hash
74
+ @logger.debug("parse handling hash", :decoded => decoded)
75
+
76
+ if decoded.has_key?("messages") and decoded["messages"].kind_of?(Array)
77
+ decoded["messages"].each { |item|
78
+ begin
79
+ map_loginsight_messages(item, &block)
80
+ rescue StandardError => e
81
+ @logger.error("Event parse failure.", :error => e, :item => item)
82
+ yield LogStash::Event.new("message" => item, "tags" => ["_eventparsefailure"])
83
+ end
84
+ } # end decoded["message"].each
85
+ else
86
+ @logger.error("Log Insight codec expects 'messages' to contain an array", :data => json)
87
+ yield LogStash::Event.new("message" => decoded, "tags" => ["_eventparsefailure"])
88
+ end
89
+ else
90
+ @logger.error("Log Insight expeects a hash containing 'messages'", :data => json)
91
+ yield LogStash::Event.new("message" => decoded, "tags" => ["_eventparsefailure"])
92
+ end
93
+ rescue LogStash::Json::ParserError => e
94
+ @logger.error("JSON parse failure.", :error => e, :data => json)
95
+ yield LogStash::Event.new("message" => json, "tags" => ["_jsonparsefailure"])
96
+ rescue StandardError => e
97
+ # This should NEVER happen.
98
+ @logger.warn(
99
+ "An unexpected error occurred parsing JSON data",
100
+ :data => json,
101
+ :message => e.message,
102
+ :class => e.class.name,
103
+ :backtrace => e.backtrace
104
+ )
105
+ end
106
+ end
@@ -0,0 +1,26 @@
1
+ Gem::Specification.new do |s|
2
+
3
+ s.name = 'logstash-codec-loginsight'
4
+ s.version = '0.1.48'
5
+ s.licenses = ['Apache-2.0']
6
+ s.summary = "This codec may be used to decode (via inputs) and encode (via outputs) full JSON messages"
7
+ 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"
8
+ s.authors = ["Alan J Castonguay"]
9
+ s.email = 'info@elastic.co'
10
+ s.homepage = "http://www.elastic.co/guide/en/logstash/current/index.html"
11
+ s.require_paths = ["lib"]
12
+
13
+ # Files
14
+ s.files = Dir["lib/**/*","spec/**/*","*.gemspec","*.md","CONTRIBUTORS","Gemfile","LICENSE","NOTICE.TXT", "vendor/jar-dependencies/**/*.jar", "vendor/jar-dependencies/**/*.rb", "VERSION", "docs/**/*"]
15
+
16
+ # Tests
17
+ s.test_files = s.files.grep(%r{^(test|spec|features)/})
18
+
19
+ # Special flag to let us know this is actually a logstash plugin
20
+ s.metadata = { "logstash_plugin" => "true", "logstash_group" => "codec" }
21
+
22
+ # Gem dependencies
23
+ s.add_runtime_dependency "logstash-core-plugin-api", ">= 1.60", "<= 2.99"
24
+
25
+ s.add_development_dependency 'logstash-devutils'
26
+ end
@@ -0,0 +1,166 @@
1
+ # encoding: utf-8
2
+ # Copyright © 2017 VMware, Inc. All Rights Reserved.
3
+ # SPDX-License-Identifier: Apache-2.0
4
+
5
+ require "logstash/devutils/rspec/spec_helper"
6
+ require "logstash/codecs/loginsight"
7
+ require "logstash/event"
8
+ require "logstash/json"
9
+ require "logstash/timestamp"
10
+
11
+ describe LogStash::Codecs::LogInsight do
12
+ subject do
13
+ LogStash::Codecs::LogInsight.new
14
+ end
15
+
16
+ context "#map_loginsight_messages" do
17
+ it "works" do
18
+ message = {"text" => "message body"}
19
+
20
+ expect { |b|
21
+ subject.send(:map_loginsight_messages, message, &b)
22
+ }.to yield_control.exactly(1).times
23
+
24
+ subject.send(:map_loginsight_messages, message) do |event|
25
+ expect(event).to be_a(LogStash::Event)
26
+ end
27
+ end
28
+
29
+ it "honors existing milliseconds-since-epoch timestamp" do
30
+ message = {"timestamp" => "5000"}
31
+ subject.send(:map_loginsight_messages, message) do |event|
32
+ expect(event).to be_a(LogStash::Event)
33
+ expect(event.get("@timestamp")).to be_a(LogStash::Timestamp)
34
+ expect(event.get("@timestamp").to_i).to eql(5) # 5000 ms == 5 seconds
35
+ end
36
+ end
37
+
38
+ it "adds missing timestamp" do
39
+ message = {"text" => "message body"}
40
+ subject.send(:map_loginsight_messages, message) do |event|
41
+ expect(event).to be_a(LogStash::Event)
42
+ expect(event.get("@timestamp")).to be_a(LogStash::Timestamp)
43
+ expect(event.get("message")).to eql("message body")
44
+ end
45
+ end
46
+
47
+ it "maps arbitrary fields" do
48
+ message = {"text"=>"message body", "fields"=>[{"name"=>"hostname", "content"=>"172.16.44.1", "startPosition"=>-1, "length"=>-2147483648}, {"name"=>"extratag", "content"=>"5", "startPosition"=>-1, "length"=>-2147483648}, {"name"=>"__li_source_path", "content"=>"172.16.44.1", "startPosition"=>-1, "length"=>-2147483648}]}
49
+ subject.send(:map_loginsight_messages, message) do |event|
50
+ expect(event).to be_a(LogStash::Event)
51
+ expect(event.get("@timestamp")).to be_a(LogStash::Timestamp)
52
+ expect(event.get("message")).to eql("message body")
53
+ expect(event.get("hostname")).to eql("172.16.44.1")
54
+ expect(event.get("__li_source_path")).to eql("172.16.44.1")
55
+ expect(event.get("extratag")).to eql("5")
56
+ end
57
+ end
58
+
59
+ it "preserves an existing 'message' field" do
60
+ message = {"text"=>"message body", "fields"=>[{"name"=>"message", "content"=>"message tag"}]}
61
+ subject.send(:map_loginsight_messages, message) do |event|
62
+ expect(event).to be_a(LogStash::Event)
63
+ expect(event.get("message")).to eql(message["text"])
64
+ expect(event.get("_message")).to eql(message["fields"][0]["content"]["message tag"])
65
+ end
66
+ end
67
+
68
+ it "handles missing 'message' field" do
69
+ message = {"fields"=>[{"name"=>"foo", "content"=>"bar"}]}
70
+ subject.send(:map_loginsight_messages, message) do |event|
71
+ expect(event).to be_a(LogStash::Event)
72
+ expect(event.get("message")).to eql(nil)
73
+ end
74
+ end
75
+
76
+ it "ignores other fields" do
77
+ message = {"foo"=>"bar"}
78
+ subject.send(:map_loginsight_messages, message) do |event|
79
+ expect(event).to be_a(LogStash::Event)
80
+ expect(event.get("message")).to eql(nil)
81
+ expect(event).not_to include("foo")
82
+ end
83
+ end
84
+
85
+ end
86
+
87
+ context "#decode" do
88
+
89
+ it "silently yields no events for an empty input messages list" do
90
+ data = {"messages" => []}
91
+ expect { |b|
92
+ subject.decode(LogStash::Json.dump(data), &b)
93
+ }.to yield_control.exactly(0).times
94
+ end
95
+
96
+ describe "passes through unknown, but valid json" do
97
+ shared_examples "given" do |value_arg|
98
+ context "where input is '#{value_arg}'" do
99
+ let(:value) { value_arg }
100
+ let(:event) do
101
+ e = nil
102
+ subject.decode(LogStash::Json.dump(value)) do |decoded|
103
+ e = decoded
104
+ end
105
+ e
106
+ end
107
+
108
+ it "stores the value in 'message'" do
109
+ expect(event.get("message")).to eql(value)
110
+ end
111
+
112
+ it "adds the _eventparsefailure tag" do
113
+ expect(event.get("tags")).to include("_eventparsefailure")
114
+ end
115
+ end
116
+ end
117
+
118
+ include_examples "given", 123
119
+ include_examples "given", "scalar"
120
+ include_examples "given", "-1"
121
+ include_examples "given", " "
122
+ include_examples "given", {"foo" => "bar"}
123
+ include_examples "given", [{"foo" => "bar"}]
124
+ include_examples "given", {"foo" => "bar", "baz" => {"quux" => ["a","b","c"]}}
125
+
126
+ end
127
+
128
+ describe "cannot parse json" do
129
+ shared_examples "json" do |value_arg|
130
+ context "where input is '#{value_arg}'" do
131
+ let(:value) { value_arg }
132
+ let(:event) do
133
+ e = nil
134
+ subject.decode(value) do |decoded|
135
+ e = decoded
136
+ end
137
+ e
138
+ end
139
+
140
+ it "stores the value in 'message'" do
141
+ expect(event.get("message")).to eql(value)
142
+ end
143
+
144
+ it "adds the failure tag" do
145
+ expect(event).to include "tags"
146
+ end
147
+
148
+ it "uses an array to store the tags" do
149
+ expect(event.get("tags")).to be_a Array
150
+ end
151
+
152
+ it "adds the _jsonparsefailure tag" do
153
+ expect(event.get("tags")).to include("_jsonparsefailure")
154
+ end
155
+ end
156
+ end
157
+
158
+ include_examples "json", "scalar"
159
+ include_examples "json", 'random_{message'
160
+ include_examples "json", "{"
161
+ include_examples "json", "[{]"
162
+ end
163
+
164
+ end
165
+
166
+ end
metadata ADDED
@@ -0,0 +1,87 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: logstash-codec-loginsight
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.48
5
+ platform: ruby
6
+ authors:
7
+ - Alan J Castonguay
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2017-05-16 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - ">="
17
+ - !ruby/object:Gem::Version
18
+ version: '1.60'
19
+ - - "<="
20
+ - !ruby/object:Gem::Version
21
+ version: '2.99'
22
+ name: logstash-core-plugin-api
23
+ prerelease: false
24
+ type: :runtime
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ requirements:
27
+ - - ">="
28
+ - !ruby/object:Gem::Version
29
+ version: '1.60'
30
+ - - "<="
31
+ - !ruby/object:Gem::Version
32
+ version: '2.99'
33
+ - !ruby/object:Gem::Dependency
34
+ requirement: !ruby/object:Gem::Requirement
35
+ requirements:
36
+ - - ">="
37
+ - !ruby/object:Gem::Version
38
+ version: '0'
39
+ name: logstash-devutils
40
+ prerelease: false
41
+ type: :development
42
+ version_requirements: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - ">="
45
+ - !ruby/object:Gem::Version
46
+ version: '0'
47
+ 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
48
+ email: info@elastic.co
49
+ executables: []
50
+ extensions: []
51
+ extra_rdoc_files: []
52
+ files:
53
+ - CONTRIBUTORS
54
+ - Gemfile
55
+ - LICENSE
56
+ - README.md
57
+ - lib/logstash/codecs/loginsight.rb
58
+ - logstash-codec-loginsight.gemspec
59
+ - spec/codecs/loginsight_spec.rb
60
+ homepage: http://www.elastic.co/guide/en/logstash/current/index.html
61
+ licenses:
62
+ - Apache-2.0
63
+ metadata:
64
+ logstash_plugin: 'true'
65
+ logstash_group: codec
66
+ post_install_message:
67
+ rdoc_options: []
68
+ require_paths:
69
+ - lib
70
+ required_ruby_version: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - ">="
73
+ - !ruby/object:Gem::Version
74
+ version: '0'
75
+ required_rubygems_version: !ruby/object:Gem::Requirement
76
+ requirements:
77
+ - - ">="
78
+ - !ruby/object:Gem::Version
79
+ version: '0'
80
+ requirements: []
81
+ rubyforge_project:
82
+ rubygems_version: 2.6.8
83
+ signing_key:
84
+ specification_version: 4
85
+ summary: This codec may be used to decode (via inputs) and encode (via outputs) full JSON messages
86
+ test_files:
87
+ - spec/codecs/loginsight_spec.rb