logstash-filter-elapsed_static 1.0.0

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: 52c215b04b101e34a9f5c2c6c69ac20012b45d71
4
+ data.tar.gz: 73b69bc1a29a64e0842582ce26678b1ae28ab543
5
+ SHA512:
6
+ metadata.gz: d21faf891bf29587be3bb93e2111fc6bcbdca7e8b4ae7ccfb6f005af3c6f94bc3f71afd10d2cf36e156bc566894ec9292c99c40662ebc39a61dcc382df470f89
7
+ data.tar.gz: cf01192691c4e047fc635f35269406b81246f32837a0a68fbe1b278255dc68ee028c870195e4bc3c9e4988dbd665f04171bc7d639d06519520adedd057c0fd05
data/CHANGELOG.md ADDED
@@ -0,0 +1,17 @@
1
+ # 4.0.0
2
+ - Use the new Event Api used in v5.0.0+
3
+
4
+ # 3.0.2
5
+ - Depend on logstash-core-plugin-api instead of logstash-core, removing the need to mass update plugins on major releases of logstash
6
+
7
+ # 3.0.1
8
+ - New dependency requirements for logstash-core for the 5.0 release
9
+
10
+ ## 3.0.0
11
+ - Elasticsearch 2.0 does not allow field names with dots in them. This is a
12
+ breaking change which replaces the `.` with an underscore, `_`
13
+
14
+ ## 2.0.0
15
+ - Plugins were updated to follow the new shutdown semantic, this mainly allows Logstash to instruct input plugins to terminate gracefully,
16
+ instead of using Thread.raise on the plugins' threads. Ref: https://github.com/elastic/logstash/pull/3895
17
+ - Dependency on logstash-core update to 2.0
data/CONTRIBUTORS ADDED
@@ -0,0 +1,17 @@
1
+ The following is a list of people who have contributed ideas, code, bug
2
+ reports, or in general have helped logstash along its way.
3
+
4
+ Contributors:
5
+ * Aaron Mildenstein (untergeek)
6
+ * Andrea Forni (andreaforni)
7
+ * Pier-Hugues Pellerin (ph)
8
+ * Richard Pijnenburg (electrical)
9
+ * Suyog Rao (suyograo)
10
+ * karsaroth
11
+ * Pere Urbón (purbon)
12
+ * Or Asnin (oras)
13
+
14
+ Note: If you've sent us patches, bug reports, or otherwise contributed to
15
+ Logstash, and you aren't on the list above and want to be, please let us know
16
+ and we'll make sure you're here. Contributions from folks like you are what make
17
+ open source awesome.
data/Gemfile ADDED
@@ -0,0 +1,2 @@
1
+ source '--no-verify'
2
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,13 @@
1
+ Copyright (c) 2012–2016 Elasticsearch <http://www.elastic.co>
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.
data/NOTICE.TXT ADDED
@@ -0,0 +1,5 @@
1
+ Elasticsearch
2
+ Copyright 2012-2015 Elasticsearch
3
+
4
+ This product includes software developed by The Apache Software
5
+ Foundation (http://www.apache.org/).
data/README.md ADDED
@@ -0,0 +1,98 @@
1
+ # Logstash Plugin
2
+
3
+ [![Travis Build Status](https://travis-ci.org/logstash-plugins/logstash-filter-elapsed.svg)](https://travis-ci.org/logstash-plugins/logstash-filter-elapsed)
4
+
5
+ This is a plugin for [Logstash](https://github.com/elastic/logstash).
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.elastic.co/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/elastic/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. We also provide [example plugins](https://github.com/logstash-plugins?query=example).
28
+
29
+ - Install dependencies
30
+ ```sh
31
+ bundle install
32
+ ```
33
+
34
+ #### Test
35
+
36
+ - Update your dependencies
37
+
38
+ ```sh
39
+ bundle install
40
+ ```
41
+
42
+ - Run tests
43
+
44
+ ```sh
45
+ bundle exec rspec
46
+ ```
47
+
48
+ ### 2. Running your unpublished Plugin in Logstash
49
+
50
+ #### 2.1 Run in a local Logstash clone
51
+
52
+ - Edit Logstash `Gemfile` and add the local plugin path, for example:
53
+ ```ruby
54
+ gem "logstash-filter-awesome", :path => "/your/local/logstash-filter-awesome"
55
+ ```
56
+ - Install plugin
57
+ ```sh
58
+ # Logstash 2.3 and higher
59
+ bin/logstash-plugin install --no-verify
60
+
61
+ # Prior to Logstash 2.3
62
+ bin/plugin install --no-verify
63
+
64
+ ```
65
+ - Run Logstash with your plugin
66
+ ```sh
67
+ bin/logstash -e 'filter {awesome {}}'
68
+ ```
69
+ At this point any modifications to the plugin code will be applied to this local Logstash setup. After modifying the plugin, simply rerun Logstash.
70
+
71
+ #### 2.2 Run in an installed Logstash
72
+
73
+ 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:
74
+
75
+ - Build your plugin gem
76
+ ```sh
77
+ gem build logstash-filter-awesome.gemspec
78
+ ```
79
+ - Install the plugin from the Logstash home
80
+ ```sh
81
+ # Logstash 2.3 and higher
82
+ bin/logstash-plugin install --no-verify
83
+
84
+ # Prior to Logstash 2.3
85
+ bin/plugin install --no-verify
86
+
87
+ ```
88
+ - Start Logstash and proceed to test the plugin
89
+
90
+ ## Contributing
91
+
92
+ All contributions are welcome: ideas, patches, documentation, bug reports, complaints, and even something you drew up on a napkin.
93
+
94
+ Programming is not a required skill. Whatever you've seen about open source and maintainers or community members saying "send patches or die" - you will not see that here.
95
+
96
+ It is more important to the community that you are able to contribute.
97
+
98
+ For more information about contributing, see the [CONTRIBUTING](https://github.com/elastic/logstash/blob/master/CONTRIBUTING.md) file.
@@ -0,0 +1,263 @@
1
+ # elapsed_static filter
2
+ #
3
+ # This filter tracks a pair of start/end events and calculates the elapsed
4
+ # time between them include hold file analysis.
5
+
6
+ require "logstash/filters/base"
7
+ require "logstash/namespace"
8
+ require 'thread'
9
+ require 'socket'
10
+ require 'time'
11
+
12
+
13
+ # The elapsed filter tracks a pair of start/end events and uses their
14
+ # timestamps to calculate the elapsed time between them.
15
+ #
16
+ # The filter has been developed to track the execution time of processes and
17
+ # other long tasks using ?<timestamp> tag & not the original elapse @timestamp.
18
+ # =========================================================
19
+ #
20
+ #
21
+ # The configuration looks like this:
22
+ # [source,ruby]
23
+ # filter {
24
+ # elapsed {
25
+ # start_tag => "start event tag"
26
+ # end_tag => "end event tag"
27
+ # unique_id_field => "id field name"
28
+ # timeout => seconds
29
+ # new_event_on_match => true/false
30
+ # }
31
+ # }
32
+ #
33
+ # The events managed by this filter must have some particular properties.
34
+ # The event describing the start of the task (the "start event") must contain
35
+ # a tag equal to `start_tag`. On the other side, the event describing the end
36
+ # of the task (the "end event") must contain a tag equal to `end_tag`. Both
37
+ # these two kinds of event need to own an ID field which identify uniquely that
38
+ # particular task. The name of this field is stored in `unique_id_field` and
39
+ # ?<timestmap> tag.
40
+ #
41
+ # You can use a Grok filter to prepare the events for the elapsed filter.
42
+ # An example of configuration can be:
43
+ # [source,ruby]
44
+ # filter {
45
+ # grok {
46
+ # match => { "message" => "(?<timestamp>%{MONTHDAY}/%{MONTHNUM}/%{YEAR} %{TIME}) START id: (?<task_id>.*)" }
47
+ # add_tag => [ "taskStarted" ]
48
+ # }
49
+ #
50
+ # grok {
51
+ # match => { "message" => "(?<timestamp>%{MONTHDAY}/%{MONTHNUM}/%{YEAR} %{TIME}) END id: (?<task_id>.*)" }
52
+ # add_tag => [ "taskTerminated" ]
53
+ # }
54
+ #
55
+ # elapsed {
56
+ # start_tag => "taskStarted"
57
+ # end_tag => "taskTerminated"
58
+ # unique_id_field => "task_id"
59
+ # }
60
+ # }
61
+ #
62
+ # The elapsed filter collects all the "start events". If two, or more, "start
63
+ # events" have the same ID, only the first one is recorded, the others are
64
+ # discarded.
65
+ #
66
+ # When an "end event" matching a previously collected "start event" is
67
+ # received, there is a match. The configuration property `new_event_on_match`
68
+ # tells where to insert the elapsed information: they can be added to the
69
+ # "end event" or a new "match event" can be created. Both events store the
70
+ # following information:
71
+ #
72
+ # * the tags `elapsed_static` and `elapsed_static_match`
73
+ # * the field `elapsed_static_time` with the difference, in seconds, between
74
+ # the two events timestamps
75
+ # * an ID filed with the task ID
76
+ # * the field `elapsed_static_timestamp_start` with the timestamp of the start event
77
+ #
78
+ # If the "end event" does not arrive before "timeout" seconds, the
79
+ # "start event" is discarded and an "expired event" is generated. This event
80
+ # contains:
81
+ #
82
+ # * the tags `elapsed_static` and `elapsed_static_expired_error`
83
+ # * a field called `elapsed_static_time` with the age, in seconds, of the
84
+ # "start event"
85
+ # * an ID filed with the task ID
86
+ # * the field `elapsed_static_timestamp_start` with the timestamp of the "start event"
87
+ #
88
+ class LogStash::Filters::Elapsed_static < LogStash::Filters::Base
89
+ PREFIX = "elapsed_static_"
90
+ ELAPSED_STATIC_FIELD = PREFIX + "time"
91
+ TIMESTAMP_START_EVENT_FIELD = PREFIX + "timestamp_start"
92
+ HOST_FIELD = "host"
93
+
94
+ ELAPSED_STATIC_TAG = "elapsed_static"
95
+ EXPIRED_ERROR_TAG = PREFIX + "expired_error"
96
+ END_WITHOUT_START_TAG = PREFIX + "end_without_start"
97
+ MATCH_TAG = PREFIX + "match"
98
+
99
+ config_name "elapsed_static"
100
+
101
+ # The name of the tag identifying the "start event"
102
+ config :start_tag, :validate => :string, :required => true
103
+
104
+ # The name of the tag identifying the "end event"
105
+ config :end_tag, :validate => :string, :required => true
106
+
107
+ # The name of the field containing the task ID.
108
+ # This value must uniquely identify the task in the system, otherwise
109
+ # it's impossible to match the couple of events.
110
+ config :unique_id_field, :validate => :string, :required => true
111
+
112
+ # The amount of seconds after an "end event" can be considered lost.
113
+ # The corresponding "start event" is discarded and an "expired event"
114
+ # is generated. The default value is 30 minutes (1800 seconds).
115
+ config :timeout, :validate => :number, :required => false, :default => 1800
116
+
117
+ # This property manage what to do when an "end event" matches a "start event".
118
+ # If it's set to `false` (default value), the elapsed information are added
119
+ # to the "end event"; if it's set to `true` a new "match event" is created.
120
+ config :new_event_on_match, :validate => :boolean, :required => false, :default => false
121
+
122
+ public
123
+ def register
124
+ @mutex = Mutex.new
125
+ # This is the state of the filter. The keys are the "unique_id_field",
126
+ # the values are couples of values: <start event, age>
127
+ @start_events = {}
128
+
129
+ @logger.info("elapsed_static, timeout: #{@timeout} seconds")
130
+ end
131
+
132
+ # Getter method used for the tests
133
+ def start_events
134
+ @start_events
135
+ end
136
+
137
+ def filter(event)
138
+
139
+
140
+ unique_id = event.get(@unique_id_field)
141
+ return if unique_id.nil?
142
+
143
+ if(start_event?(event))
144
+ filter_matched(event)
145
+ @logger.info("elapsed_static, 'start event' received", start_tag: @start_tag, unique_id_field: @unique_id_field)
146
+
147
+ @mutex.synchronize do
148
+ unless(@start_events.has_key?(unique_id))
149
+ @start_events[unique_id] = LogStash::Filters::Elapsed_static::Element.new(event)
150
+ end
151
+ end
152
+
153
+ elsif(end_event?(event))
154
+ filter_matched(event)
155
+ @logger.info("elapsed_static, 'end event' received", end_tag: @end_tag, unique_id_field: @unique_id_field)
156
+
157
+ @mutex.lock
158
+ if(@start_events.has_key?(unique_id))
159
+ start_event = @start_events.delete(unique_id).event
160
+ @mutex.unlock
161
+ elapsed_static = Time.parse(event.get("timestamp")) - Time.parse(start_event.get("timestamp"))
162
+ if(@new_event_on_match)
163
+ elapsed_static_event = new_elapsed_static_event(elapsed_static, unique_id, start_event.get("timestamp"))
164
+ filter_matched(elapsed_static_event)
165
+ yield elapsed_static_event if block_given?
166
+ else
167
+ return add_elapsed_static_info(event, elapsed_static, unique_id, start_event.get("timestamp"))
168
+ end
169
+ else
170
+ @mutex.unlock
171
+ # The "start event" did not arrive.
172
+ event.tag(END_WITHOUT_START_TAG)
173
+ end
174
+ end
175
+ end # def filter
176
+
177
+ # The method is invoked by LogStash every 5 seconds.
178
+ def flush(options = {})
179
+ expired_elements = []
180
+
181
+ @mutex.synchronize do
182
+ increment_age_by(5)
183
+ expired_elements = remove_expired_elements()
184
+ end
185
+
186
+ return create_expired_events_from(expired_elements)
187
+ end
188
+
189
+ private
190
+ def increment_age_by(seconds)
191
+ @start_events.each_pair do |key, element|
192
+ element.age += seconds
193
+ end
194
+ end
195
+
196
+ # Remove the expired "start events" from the internal
197
+ # buffer and return them.
198
+ def remove_expired_elements()
199
+ expired = []
200
+ @start_events.delete_if do |key, element|
201
+ if(element.age >= @timeout)
202
+ expired << element
203
+ next true
204
+ end
205
+ next false
206
+ end
207
+
208
+ return expired
209
+ end
210
+
211
+ def create_expired_events_from(expired_elements)
212
+ events = []
213
+ expired_elements.each do |element|
214
+ error_event = LogStash::Event.new
215
+ error_event.tag(ELAPSED_STATIC_TAG)
216
+ error_event.tag(EXPIRED_ERROR_TAG)
217
+
218
+ error_event.set(HOST_FIELD, Socket.gethostname)
219
+ error_event.set(@unique_id_field, element.event.get(@unique_id_field) )
220
+ error_event.set(ELAPSED_STATIC_FIELD, element.age)
221
+ error_event.set(TIMESTAMP_START_EVENT_FIELD, element.event.get("timestamp") )
222
+
223
+ events << error_event
224
+ filter_matched(error_event)
225
+ end
226
+
227
+ return events
228
+ end
229
+
230
+ def start_event?(event)
231
+ return (event.get("tags") != nil && event.get("tags").include?(@start_tag))
232
+ end
233
+
234
+ def end_event?(event)
235
+ return (event.get("tags") != nil && event.get("tags").include?(@end_tag))
236
+ end
237
+
238
+ def new_elapsed_static_event(elapsed_static_time, unique_id, timestamp_start_event)
239
+ new_event = LogStash::Event.new
240
+ new_event.set(HOST_FIELD, Socket.gethostname)
241
+ return add_elapsed_static_info(new_event, elapsed_static_time, unique_id, timestamp_start_event)
242
+ end
243
+
244
+ def add_elapsed_static_info(event, elapsed_static_time, unique_id, timestamp_start_event)
245
+ event.tag(ELAPSED_STATIC_TAG)
246
+ event.tag(MATCH_TAG)
247
+
248
+ event.set(ELAPSED_STATIC_FIELD, elapsed_static_time)
249
+ event.set(@unique_id_field, unique_id)
250
+ event.set(TIMESTAMP_START_EVENT_FIELD, timestamp_start_event)
251
+
252
+ return event
253
+ end
254
+ end # class LogStash::Filters::elapsed_static
255
+
256
+ class LogStash::Filters::Elapsed_static::Element
257
+ attr_accessor :event, :age
258
+
259
+ def initialize(event)
260
+ @event = event
261
+ @age = 0
262
+ end
263
+ end
@@ -0,0 +1,27 @@
1
+ Gem::Specification.new do |s|
2
+
3
+ s.name = 'logstash-filter-elapsed_static'
4
+ s.version = '1.0.0'
5
+ s.licenses = ['Apache License (2.0)']
6
+ s.summary = "For not only real time log writing! This filter tracks a pair of start/end events and calculates the elapsed time between them."
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 = ["Elastic & Intel"]
9
+ s.email = 'info@elastic.co, orrx.asnin@intel.com'
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/**/*','vendor/**/*','*.gemspec','*.md','CONTRIBUTORS','Gemfile','LICENSE','NOTICE.TXT']
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" => "output" }
21
+
22
+ # Gem dependencies
23
+ s.add_runtime_dependency "logstash-core-plugin-api", ">= 1.60", "<= 2.99"
24
+ s.add_development_dependency 'logstash-devutils'
25
+
26
+
27
+ end
@@ -0,0 +1,297 @@
1
+ # encoding: utf-8
2
+ require "logstash/devutils/rspec/spec_helper"
3
+ require "logstash/filters/elapsed_static"
4
+ require "logstash/event"
5
+ require "socket"
6
+
7
+ describe LogStash::Filters::Elapsed_static do
8
+ START_TAG = "startTag"
9
+ END_TAG = "endTag"
10
+ ID_FIELD = "uniqueIdField"
11
+
12
+ def event(data)
13
+ data["message"] ||= "Log message"
14
+ LogStash::Event.new(data)
15
+ end
16
+
17
+ def start_event(data)
18
+ data["tags"] ||= []
19
+ data["tags"] << START_TAG
20
+ event(data)
21
+ end
22
+
23
+ def end_event(data = {})
24
+ data["tags"] ||= []
25
+ data["tags"] << END_TAG
26
+ event(data)
27
+ end
28
+
29
+ before(:each) do
30
+ setup_filter()
31
+ end
32
+
33
+ def setup_filter(config = {})
34
+ @config = {"start_tag" => START_TAG, "end_tag" => END_TAG, "unique_id_field" => ID_FIELD}
35
+ @config.merge!(config)
36
+ @filter = LogStash::Filters::Elapsed_static.new(@config)
37
+ @filter.register
38
+ end
39
+
40
+ context "General validation" do
41
+ describe "receiving an event without start or end tag" do
42
+ it "does not record it" do
43
+ @filter.filter(event("message" => "Log message"))
44
+ insist { @filter.start_events.size } == 0
45
+ end
46
+ end
47
+
48
+ describe "receiving an event with a different start/end tag from the ones specified in the configuration" do
49
+ it "does not record it" do
50
+ @filter.filter(event("tags" => ["tag1", "tag2"]))
51
+ insist { @filter.start_events.size } == 0
52
+ end
53
+ end
54
+ end
55
+
56
+ context "Start event" do
57
+ describe "receiving an event with a valid start tag" do
58
+ describe "but without an unique id field" do
59
+ it "does not record it" do
60
+ @filter.filter(event("tags" => ["tag1", START_TAG]))
61
+ insist { @filter.start_events.size } == 0
62
+ end
63
+ end
64
+
65
+ describe "and a valid id field" do
66
+ it "records it" do
67
+ event = start_event(ID_FIELD => "id123")
68
+ @filter.filter(event)
69
+
70
+ insist { @filter.start_events.size } == 1
71
+ insist { @filter.start_events["id123"].event } == event
72
+ end
73
+ end
74
+ end
75
+
76
+ describe "receiving two 'start events' for the same id field" do
77
+ it "keeps the first one and does not save the second one" do
78
+ args = {"tags" => [START_TAG], ID_FIELD => "id123"}
79
+ first_event = event(args)
80
+ second_event = event(args)
81
+
82
+ @filter.filter(first_event)
83
+ @filter.filter(second_event)
84
+
85
+ insist { @filter.start_events.size } == 1
86
+ insist { @filter.start_events["id123"].event } == first_event
87
+ end
88
+ end
89
+ end
90
+
91
+ context "End event" do
92
+ describe "receiving an event with a valid end tag" do
93
+ describe "and without an id" do
94
+ it "does nothing" do
95
+ insist { @filter.start_events.size } == 0
96
+ @filter.filter(end_event())
97
+ insist { @filter.start_events.size } == 0
98
+ end
99
+ end
100
+
101
+ describe "and with an id" do
102
+ describe "but without a previous 'start event'" do
103
+ it "adds a tag 'elapsedStatic_end_witout_start' to the 'end event'" do
104
+ end_event = end_event(ID_FIELD => "id_123")
105
+
106
+ @filter.filter(end_event)
107
+
108
+ insist { end_event.get("tags").include?("elapsedStatic_end_without_start") } == true
109
+ end
110
+ end
111
+ end
112
+ end
113
+ end
114
+
115
+ context "Start/end events interaction" do
116
+ describe "receiving a 'start event'" do
117
+ before(:each) do
118
+ @id_value = "id_123"
119
+ @start_event = start_event(ID_FIELD => @id_value)
120
+ @filter.filter(@start_event)
121
+ end
122
+
123
+ describe "and receiving an event with a valid end tag" do
124
+ describe "and without an id" do
125
+ it "does nothing" do
126
+ @filter.filter(end_event())
127
+ insist { @filter.start_events.size } == 1
128
+ insist { @filter.start_events[@id_value].event } == @start_event
129
+ end
130
+ end
131
+
132
+ describe "and an id different from the one of the 'start event'" do
133
+ it "does nothing" do
134
+ different_id_value = @id_value + "_different"
135
+ @filter.filter(end_event(ID_FIELD => different_id_value))
136
+
137
+ insist { @filter.start_events.size } == 1
138
+ insist { @filter.start_events[@id_value].event } == @start_event
139
+ end
140
+ end
141
+
142
+ describe "and the same id of the 'start event'" do
143
+ it "deletes the recorded 'start event'" do
144
+ insist { @filter.start_events.size } == 1
145
+
146
+ @filter.filter(end_event(ID_FIELD => @id_value))
147
+
148
+ insist { @filter.start_events.size } == 0
149
+ end
150
+
151
+ shared_examples_for "match event" do
152
+ it "contains the tag 'elapsedStatic'" do
153
+ insist { @match_event.get("tags").include?("elapsedStatic") } == true
154
+ end
155
+
156
+ it "contains the tag tag 'elapsedStatic_match'" do
157
+ insist { @match_event.get("tags").include?("elapsedStatic_match") } == true
158
+ end
159
+
160
+ it "contains an 'elapsedStatic_time field' with the elapsed time" do
161
+ insist { @match_event.get("elapsedStatic_time") } == 10
162
+ end
163
+
164
+ it "contains an 'elapsedStatic_timestamp_start field' with the timestamp of the 'start event'" do
165
+ insist { @match_event.get("elapsedStatic_timestamp_start") } == @start_event.get("timestamp")
166
+ end
167
+
168
+ it "contains an 'id field'" do
169
+ insist { @match_event.get(ID_FIELD) } == @id_value
170
+ end
171
+ end
172
+
173
+ context "if 'new_event_on_match' is set to 'true'" do
174
+ before(:each) do
175
+ # I need to create a new filter because I need to set
176
+ # the config property 'new_event_on_match" to 'true'.
177
+ setup_filter("new_event_on_match" => true)
178
+ @start_event = start_event(ID_FIELD => @id_value)
179
+ @filter.filter(@start_event)
180
+
181
+ end_timestamp = @start_event.get("timestamp") + 10
182
+ end_event = end_event(ID_FIELD => @id_value, "timestamp" => end_timestamp)
183
+ @filter.filter(end_event) do |new_event|
184
+ @match_event = new_event
185
+ end
186
+ end
187
+
188
+ context "creates a new event that" do
189
+ it_behaves_like "match event"
190
+
191
+ it "contains the 'host field'" do
192
+ insist { @match_event.get("host") } == Socket.gethostname
193
+ end
194
+ end
195
+ end
196
+
197
+ context "if 'new_event_on_match' is set to 'false'" do
198
+ before(:each) do
199
+ end_timestamp = @start_event.get("timestamp") + 10
200
+ end_event = end_event(ID_FIELD => @id_value, "timestamp" => end_timestamp)
201
+ @filter.filter(end_event)
202
+
203
+ @match_event = end_event
204
+ end
205
+
206
+ context "modifies the 'end event' that" do
207
+ it_behaves_like "match event"
208
+ end
209
+ end
210
+
211
+ end
212
+ end
213
+ end
214
+ end
215
+
216
+ describe "#flush" do
217
+ def setup(timeout = 1000)
218
+ @config["timeout"] = timeout
219
+ @filter = LogStash::Filters::Elapsed_static.new(@config)
220
+ @filter.register
221
+
222
+ @start_event_1 = start_event(ID_FIELD => "1")
223
+ @start_event_2 = start_event(ID_FIELD => "2")
224
+ @start_event_3 = start_event(ID_FIELD => "3")
225
+
226
+ @filter.filter(@start_event_1)
227
+ @filter.filter(@start_event_2)
228
+ @filter.filter(@start_event_3)
229
+
230
+ # Force recorded events to different ages
231
+ @filter.start_events["2"].age = 25
232
+ @filter.start_events["3"].age = 26
233
+ end
234
+
235
+ it "increments the 'age' of all the recorded 'start events' by 5 seconds" do
236
+ setup()
237
+ old_age = ages()
238
+
239
+ @filter.flush()
240
+
241
+ ages().each_with_index do |new_age, i|
242
+ insist { new_age } == (old_age[i] + 5)
243
+ end
244
+ end
245
+
246
+ def ages()
247
+ @filter.start_events.each_value.map{|element| element.age }
248
+ end
249
+
250
+ context "if the 'timeout interval' is set to 30 seconds" do
251
+ before(:each) do
252
+ setup(30)
253
+
254
+ @expired_events = @filter.flush()
255
+
256
+ insist { @filter.start_events.size } == 1
257
+ insist { @expired_events.size } == 2
258
+ end
259
+
260
+ it "deletes the recorded 'start events' with 'age' greater, or equal to, the timeout" do
261
+ insist { @filter.start_events.key?("1") } == true
262
+ insist { @filter.start_events.key?("2") } == false
263
+ insist { @filter.start_events.key?("3") } == false
264
+ end
265
+
266
+ it "creates a new event with tag 'elapsedStatic_expired_error' for each expired 'start event'" do
267
+ insist { @expired_events[0].get("tags").include?("elapsedStatic_expired_error") } == true
268
+ insist { @expired_events[1].get("tags").include?("elapsedStatic_expired_error") } == true
269
+ end
270
+
271
+ it "creates a new event with tag 'elapsedStatic' for each expired 'start event'" do
272
+ insist { @expired_events[0].get("tags").include?("elapsedStatic") } == true
273
+ insist { @expired_events[1].get("tags").include?("elapsedStatic") } == true
274
+ end
275
+
276
+ it "creates a new event containing the 'id field' of the expired 'start event'" do
277
+ insist { @expired_events[0].get(ID_FIELD) } == "2"
278
+ insist { @expired_events[1].get(ID_FIELD) } == "3"
279
+ end
280
+
281
+ it "creates a new event containing an 'elapsedStatic_time field' with the age of the expired 'start event'" do
282
+ insist { @expired_events[0].get("elapsedStatic_time") } == 30
283
+ insist { @expired_events[1].get("elapsedStatic_time") } == 31
284
+ end
285
+
286
+ it "creates a new event containing an 'elapsedStatic_timestamp_start field' with the timestamp of the expired 'start event'" do
287
+ insist { @expired_events[0].get("elapsedStatic_timestamp_start") } == @start_event_2.get("timestamp")
288
+ insist { @expired_events[1].get("elapsedStatic_timestamp_start") } == @start_event_3.get("timestamp")
289
+ end
290
+
291
+ it "creates a new event containing a 'host field' for each expired 'start event'" do
292
+ insist { @expired_events[0].get("host") } == Socket.gethostname
293
+ insist { @expired_events[1].get("host") } == Socket.gethostname
294
+ end
295
+ end
296
+ end
297
+ end
metadata ADDED
@@ -0,0 +1,92 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: logstash-filter-elapsed_static
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Elastic & Intel
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2017-01-18 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: logstash-core-plugin-api
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '1.60'
20
+ - - "<="
21
+ - !ruby/object:Gem::Version
22
+ version: '2.99'
23
+ type: :runtime
24
+ prerelease: false
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
+ name: logstash-devutils
35
+ requirement: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - ">="
38
+ - !ruby/object:Gem::Version
39
+ version: '0'
40
+ type: :development
41
+ prerelease: false
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
48
+ Logstash core pipeline using $LS_HOME/bin/logstash-plugin install gemname. This
49
+ gem is not a stand-alone program
50
+ email: info@elastic.co, orrx.asnin@intel.com
51
+ executables: []
52
+ extensions: []
53
+ extra_rdoc_files: []
54
+ files:
55
+ - CHANGELOG.md
56
+ - CONTRIBUTORS
57
+ - Gemfile
58
+ - LICENSE
59
+ - NOTICE.TXT
60
+ - README.md
61
+ - lib/logstash/filters/elapsed_static.rb
62
+ - logstash-filter-elapsed_static.gemspec
63
+ - spec/filters/elapsed_spec.rb
64
+ homepage: http://www.elastic.co/guide/en/logstash/current/index.html
65
+ licenses:
66
+ - Apache License (2.0)
67
+ metadata:
68
+ logstash_plugin: 'true'
69
+ logstash_group: output
70
+ post_install_message:
71
+ rdoc_options: []
72
+ require_paths:
73
+ - lib
74
+ required_ruby_version: !ruby/object:Gem::Requirement
75
+ requirements:
76
+ - - ">="
77
+ - !ruby/object:Gem::Version
78
+ version: '0'
79
+ required_rubygems_version: !ruby/object:Gem::Requirement
80
+ requirements:
81
+ - - ">="
82
+ - !ruby/object:Gem::Version
83
+ version: '0'
84
+ requirements: []
85
+ rubyforge_project:
86
+ rubygems_version: 2.6.8
87
+ signing_key:
88
+ specification_version: 4
89
+ summary: For not only real time log writing! This filter tracks a pair of start/end
90
+ events and calculates the elapsed time between them.
91
+ test_files:
92
+ - spec/filters/elapsed_spec.rb