logstash-filter-aggregate 2.3.1 → 2.4.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: eaa83c85ffda851fdae41182c4ec2d010483dbbe
4
- data.tar.gz: bfbb53a989e67654304b4e4c9cf89f2b91411c6a
3
+ metadata.gz: 86bf41e2f4183eb1bca1053dcdd54bba16ce715d
4
+ data.tar.gz: edd15d9f860d6becda2d0faabf246de87cb404bd
5
5
  SHA512:
6
- metadata.gz: 0adfe2d3916570f84230a5823ad6b038f661a5837bb78600e66c3a58d4b066630cf31cf974f939a8071f2ab2549698756a24cb768e6b15df983a2546f0dc6490
7
- data.tar.gz: 7720f2d38e7757145294e05903376874dcd88a600bcd9174c5da1ac2ef0ca3ba4bd2545c01a107317cf037f99d8be8005cd3830aba62af32d85c6e8e5f57f1c7
6
+ metadata.gz: adf8addee7fcfd1efeddf980a85933b9ccb35e89f1d3e1e784fb302902a0308ec31209decb5c3d3da83151860d212e2d7c79f41349bc5e2cc4a16acdc2352d5c
7
+ data.tar.gz: 320a2b40266aa4cc506ef01ec6e61e6b0f92985f2ccd4914a621b725fc9ace12f50e8d3314c36afedff97c5e0bb886a2460c2bc87549ebf41ff92389754b6842
data/CHANGELOG.md CHANGED
@@ -1,3 +1,9 @@
1
+ ## 2.4.0
2
+ - new feature: You can now define timeout options per task_id pattern (#42)
3
+ timeout options are : `timeout, timeout_code, push_map_as_event_on_timeout, push_previous_map_as_event, timeout_task_id_field, timeout_tags`
4
+ - validation: a configuration error is thrown at startup if you define any timeout option on several aggregate filters for the same task_id pattern
5
+ - breaking: if you use `aggregate_maps_path` option, storage format has changed. So you have to delete `aggregate_maps_path` file before starting Logstash
6
+
1
7
  ## 2.3.1
2
8
  - new feature: Add new option "timeout_tags" so that you can add tags to generated timeout events
3
9
 
data/README.md CHANGED
@@ -204,7 +204,9 @@ In that case, you don't want to wait task timeout to flush aggregation map.
204
204
  - after the final event, the map attached to task is deleted
205
205
  - in one filter configuration, it is recommanded to define a timeout option to protect the filter against unterminated tasks. It tells the filter to delete expired maps
206
206
  - if no timeout is defined, by default, all maps older than 1800 seconds are automatically deleted
207
- - finally, if `code` execution raises an exception, the error is logged and event is tagged '_aggregateexception'
207
+ - all timeout options have to be defined in only one aggregate filter per task_id pattern.
208
+ Timeout options are : `timeout, timeout_code, push_map_as_event_on_timeout, push_previous_map_as_event, timeout_task_id_field, timeout_tags`
209
+ - if `code` execution raises an exception, the error is logged and event is tagged '_aggregateexception'
208
210
 
209
211
  ## Use Cases
210
212
  - extract some cool metrics from task logs and push them into task final log event (like in example #1 and #2)
@@ -236,29 +238,20 @@ Tell the filter what to do with aggregate map (default : "create_or_update").
236
238
  Default value: `create_or_update`
237
239
 
238
240
  - **end_of_task:**
239
- Tell the filter that task is ended, and therefore, to delete map after code execution.
241
+ Tell the filter that task is ended, and therefore, to delete aggregate map after code execution.
240
242
  Default value: `false`
241
243
 
242
- - **timeout:**
243
- The amount of seconds after a task "end event" can be considered lost.
244
- When timeout occurs for a task, The task "map" is evicted.
245
- If no timeout is defined, default timeout will be applied : 1800 seconds.
246
-
247
244
  - **aggregate_maps_path:**
248
245
  The path to file where aggregate maps are stored when logstash stops and are loaded from when logstash starts.
249
246
  If not defined, aggregate maps will not be stored at logstash stop and will be lost.
250
247
  Must be defined in only one aggregate filter (as aggregate maps are global).
251
248
  Example value : `"/path/to/.aggregate_maps"`
252
249
 
253
- - **push_previous_map_as_event:**
254
- When this option is enabled, each time aggregate plugin detects a new task id, it pushes previous aggregate map as a new logstash event,
255
- and then creates a new empty map for the next task.
256
- _WARNING:_ this option works fine only if tasks come one after the other. It means : all task1 events, then all task2 events, etc...
257
- Default value: `false`
258
-
259
- - **push_map_as_event_on_timeout**
260
- When this option is enabled, each time a task timeout is detected, it pushes task aggregation map as a new logstash event.
261
- This enables to detect and process task timeouts in logstash, but also to manage tasks that have no explicit end event.
250
+ - **timeout:**
251
+ The amount of seconds after a task "end event" can be considered lost.
252
+ When timeout occurs for a task, The task "map" is evicted.
253
+ Timeout can be defined for each "task_id" pattern.
254
+ If no timeout is defined, default timeout will be applied : 1800 seconds.
262
255
 
263
256
  - **timeout_code**
264
257
  The code to execute to complete timeout generated event, when 'push_map_as_event_on_timeout' or 'push_previous_map_as_event' is set to true.
@@ -266,6 +259,17 @@ The code block will have access to the newly generated timeout event that is pre
266
259
  If 'timeout_task_id_field' is set, the event is also populated with the task_id value
267
260
  Example value: `"event['state'] = 'timeout'"`
268
261
 
262
+ - **push_map_as_event_on_timeout**
263
+ When this option is enabled, each time a task timeout is detected, it pushes task aggregation map as a new logstash event.
264
+ This enables to detect and process task timeouts in logstash, but also to manage tasks that have no explicit end event.
265
+ Default value: `false`
266
+
267
+ - **push_previous_map_as_event:**
268
+ When this option is enabled, each time aggregate plugin detects a new task id, it pushes previous aggregate map as a new logstash event,
269
+ and then creates a new empty map for the next task.
270
+ _WARNING:_ this option works fine only if tasks come one after the other. It means : all task1 events, then all task2 events, etc...
271
+ Default value: `false`
272
+
269
273
  - **timeout_task_id_field**
270
274
  This option indicates the timeout generated event's field for the "task_id" value.
271
275
  The task id will then be set into the timeout event. This can help correlate which tasks have been timed out.
@@ -220,7 +220,8 @@ require "logstash/util/decorators"
220
220
  # * after the final event, the map attached to task is deleted
221
221
  # * in one filter configuration, it is recommanded to define a timeout option to protect the feature against unterminated tasks. It tells the filter to delete expired maps
222
222
  # * if no timeout is defined, by default, all maps older than 1800 seconds are automatically deleted
223
- # * finally, if `code` execution raises an exception, the error is logged and event is tagged '_aggregateexception'
223
+ # * all timeout options have to be defined in only one aggregate filter per task_id pattern. Timeout options are : timeout, timeout_code, push_map_as_event_on_timeout, push_previous_map_as_event, timeout_task_id_field, timeout_tags
224
+ # * if `code` execution raises an exception, the error is logged and event is tagged '_aggregateexception'
224
225
  #
225
226
  #
226
227
  # ==== Use Cases
@@ -252,30 +253,6 @@ class LogStash::Filters::Aggregate < LogStash::Filters::Base
252
253
  # Example value : `"map['sql_duration'] += event['duration']"`
253
254
  config :code, :validate => :string, :required => true
254
255
 
255
-
256
-
257
- # The code to execute to complete timeout generated event, when 'push_map_as_event_on_timeout' or 'push_previous_map_as_event' is set to true.
258
- # The code block will have access to the newly generated timeout event that is pre-populated with the aggregation map.
259
- #
260
- # If 'timeout_task_id_field' is set, the event is also populated with the task_id value
261
- #
262
- # Example value: `"event['state'] = 'timeout'"`
263
- config :timeout_code, :validate => :string, :required => false
264
-
265
-
266
- # This option indicates the timeout generated event's field for the "task_id" value.
267
- # The task id will then be set into the timeout event. This can help correlate which tasks have been timed out.
268
- #
269
- # This field has no default value and will not be set on the event if not configured.
270
- #
271
- # Example:
272
- #
273
- # If the task_id is "12345" and this field is set to "my_id", the generated event will have:
274
- # event[ "my_id" ] = "12345"
275
- #
276
- config :timeout_task_id_field, :validate => :string, :required => false
277
-
278
-
279
256
  # Tell the filter what to do with aggregate map.
280
257
  #
281
258
  # `create`: create the map, and execute the code only if map wasn't created before
@@ -285,16 +262,9 @@ class LogStash::Filters::Aggregate < LogStash::Filters::Base
285
262
  # `create_or_update`: create the map if it wasn't created before, execute the code in all cases
286
263
  config :map_action, :validate => :string, :default => "create_or_update"
287
264
 
288
- # Tell the filter that task is ended, and therefore, to delete map after code execution.
265
+ # Tell the filter that task is ended, and therefore, to delete aggregate map after code execution.
289
266
  config :end_of_task, :validate => :boolean, :default => false
290
267
 
291
- # The amount of seconds after a task "end event" can be considered lost.
292
- #
293
- # When timeout occurs for a task, The task "map" is evicted.
294
- #
295
- # If no timeout is defined, default timeout will be applied : 1800 seconds.
296
- config :timeout, :validate => :number, :required => false
297
-
298
268
  # The path to file where aggregate maps are stored when logstash stops
299
269
  # and are loaded from when logstash starts.
300
270
  #
@@ -304,15 +274,44 @@ class LogStash::Filters::Aggregate < LogStash::Filters::Base
304
274
  # Example value : `"/path/to/.aggregate_maps"`
305
275
  config :aggregate_maps_path, :validate => :string, :required => false
306
276
 
277
+ # The amount of seconds after a task "end event" can be considered lost.
278
+ #
279
+ # When timeout occurs for a task, The task "map" is evicted.
280
+ #
281
+ # Timeout can be defined for each "task_id" pattern.
282
+ #
283
+ # If no timeout is defined, default timeout will be applied : 1800 seconds.
284
+ config :timeout, :validate => :number, :required => false
285
+
286
+ # The code to execute to complete timeout generated event, when 'push_map_as_event_on_timeout' or 'push_previous_map_as_event' is set to true.
287
+ # The code block will have access to the newly generated timeout event that is pre-populated with the aggregation map.
288
+ #
289
+ # If 'timeout_task_id_field' is set, the event is also populated with the task_id value
290
+ #
291
+ # Example value: `"event['state'] = 'timeout'"`
292
+ config :timeout_code, :validate => :string, :required => false
293
+
294
+ # When this option is enabled, each time a task timeout is detected, it pushes task aggregation map as a new logstash event.
295
+ # This enables to detect and process task timeouts in logstash, but also to manage tasks that have no explicit end event.
296
+ config :push_map_as_event_on_timeout, :validate => :boolean, :required => false, :default => false
297
+
307
298
  # When this option is enabled, each time aggregate plugin detects a new task id, it pushes previous aggregate map as a new logstash event,
308
299
  # and then creates a new empty map for the next task.
309
300
  #
310
301
  # WARNING: this option works fine only if tasks come one after the other. It means : all task1 events, then all task2 events, etc...
311
302
  config :push_previous_map_as_event, :validate => :boolean, :required => false, :default => false
312
303
 
313
- # When this option is enabled, each time a task timeout is detected, it pushes task aggregation map as a new logstash event.
314
- # This enables to detect and process task timeouts in logstash, but also to manage tasks that have no explicit end event.
315
- config :push_map_as_event_on_timeout, :validate => :boolean, :required => false, :default => false
304
+ # This option indicates the timeout generated event's field for the "task_id" value.
305
+ # The task id will then be set into the timeout event. This can help correlate which tasks have been timed out.
306
+ #
307
+ # This field has no default value and will not be set on the event if not configured.
308
+ #
309
+ # Example:
310
+ #
311
+ # If the task_id is "12345" and this field is set to "my_id", the generated event will have:
312
+ # event[ "my_id" ] = "12345"
313
+ #
314
+ config :timeout_task_id_field, :validate => :string, :required => false
316
315
 
317
316
  # Defines tags to add when a timeout event is generated and yield
318
317
  config :timeout_tags, :validate => :array, :required => false, :default => []
@@ -329,11 +328,15 @@ class LogStash::Filters::Aggregate < LogStash::Filters::Base
329
328
  # Mutex used to synchronize access to 'aggregate_maps'
330
329
  @@mutex = Mutex.new
331
330
 
332
- # Aggregate instance which will evict all zombie Aggregate elements (older than timeout)
333
- @@eviction_instance = nil
331
+ # Default timeout for task_id patterns where timeout is not defined in logstash filter configuration
332
+ @@default_timeout = nil
333
+
334
+ # For each "task_id" pattern, defines which Aggregate instance will evict all expired Aggregate elements (older than timeout)
335
+ # For each entry, key is "task_id pattern" and value is "aggregate instance"
336
+ @@eviction_instance_map = {}
334
337
 
335
- # last time where eviction was launched
336
- @@last_eviction_timestamp = nil
338
+ # last time where eviction was launched, per "task_id" pattern
339
+ @@last_eviction_timestamp_map = {}
337
340
 
338
341
  # flag indicating if aggregate_maps_path option has been already set on one aggregate instance
339
342
  @@aggregate_maps_path_set = false
@@ -349,30 +352,44 @@ class LogStash::Filters::Aggregate < LogStash::Filters::Base
349
352
  if @timeout_code
350
353
  eval("@timeout_codeblock = lambda { |event| #{@timeout_code} }", binding, "(aggregate filter timeout code)")
351
354
  end
352
-
355
+
353
356
  @@mutex.synchronize do
354
- # define eviction_instance
355
- if (!@timeout.nil? && (@@eviction_instance.nil? || @timeout < @@eviction_instance.timeout))
356
- @@eviction_instance = self
357
- @logger.info("Aggregate, timeout: #{@timeout} seconds")
357
+
358
+ # timeout management : define eviction_instance for current task_id pattern
359
+ if has_timeout_options?
360
+ if @@eviction_instance_map.has_key?(@task_id)
361
+ # all timeout options have to be defined in only one aggregate filter per task_id pattern
362
+ raise LogStash::ConfigurationError, "Aggregate plugin: For task_id pattern #{@task_id}, there are more than one filter which defines timeout options. All timeout options have to be defined in only one aggregate filter per task_id pattern. Timeout options are : #{display_timeout_options}"
363
+ end
364
+ @@eviction_instance_map[@task_id] = self
365
+ @logger.info("Aggregate plugin: timeout for '#{@task_id}' pattern: #{@timeout} seconds")
366
+ end
367
+
368
+ # timeout management : define default_timeout
369
+ if !@timeout.nil? && (@@default_timeout.nil? || @timeout < @@default_timeout)
370
+ @@default_timeout = @timeout
371
+ @logger.info("Aggregate plugin: default timeout: #{@timeout} seconds")
358
372
  end
359
373
 
360
374
  # check if aggregate_maps_path option has already been set on another instance
361
- if (!@aggregate_maps_path.nil?)
362
- if (@@aggregate_maps_path_set)
375
+ if !@aggregate_maps_path.nil?
376
+ if @@aggregate_maps_path_set
363
377
  @@aggregate_maps_path_set = false
364
- raise LogStash::ConfigurationError, "Option 'aggregate_maps_path' must be set on only one aggregate filter"
378
+ raise LogStash::ConfigurationError, "Aggregate plugin: Option 'aggregate_maps_path' must be set on only one aggregate filter"
365
379
  else
366
380
  @@aggregate_maps_path_set = true
367
381
  end
368
382
  end
369
383
 
370
384
  # load aggregate maps from file (if option defined)
371
- if (!@aggregate_maps_path.nil? && File.exist?(@aggregate_maps_path))
385
+ if !@aggregate_maps_path.nil? && File.exist?(@aggregate_maps_path)
372
386
  File.open(@aggregate_maps_path, "r") { |from_file| @@aggregate_maps = Marshal.load(from_file) }
373
387
  File.delete(@aggregate_maps_path)
374
- @logger.info("Aggregate, load aggregate maps from : #{@aggregate_maps_path}")
388
+ @logger.info("Aggregate plugin: load aggregate maps from : #{@aggregate_maps_path}")
375
389
  end
390
+
391
+ # init aggregate_maps
392
+ @@aggregate_maps[@task_id] ||= {}
376
393
  end
377
394
  end
378
395
 
@@ -380,18 +397,21 @@ class LogStash::Filters::Aggregate < LogStash::Filters::Base
380
397
  public
381
398
  def close
382
399
 
383
- # Protection against logstash reload
384
- @@aggregate_maps_path_set = false if @@aggregate_maps_path_set
385
- @@eviction_instance = nil unless @@eviction_instance.nil?
386
-
400
+ # store aggregate maps to file (if option defined)
387
401
  @@mutex.synchronize do
388
- # store aggregate maps to file (if option defined)
389
- if (!@aggregate_maps_path.nil? && !@@aggregate_maps.empty?)
402
+ @@aggregate_maps.delete_if { |key, value| value.empty? }
403
+ if !@aggregate_maps_path.nil? && !@@aggregate_maps.empty?
390
404
  File.open(@aggregate_maps_path, "w"){ |to_file| Marshal.dump(@@aggregate_maps, to_file) }
391
- @@aggregate_maps.clear()
392
- @logger.info("Aggregate, store aggregate maps to : #{@aggregate_maps_path}")
405
+ @logger.info("Aggregate plugin: store aggregate maps to : #{@aggregate_maps_path}")
393
406
  end
407
+ @@aggregate_maps.clear()
394
408
  end
409
+
410
+ # Protection against logstash reload
411
+ @@aggregate_maps_path_set = false if @@aggregate_maps_path_set
412
+ @@default_timeout = nil unless @@default_timeout.nil?
413
+ @@eviction_instance_map = {} unless @@eviction_instance_map.empty?
414
+
395
415
  end
396
416
 
397
417
  # This method is invoked each time an event matches the filter
@@ -409,19 +429,19 @@ class LogStash::Filters::Aggregate < LogStash::Filters::Base
409
429
  @@mutex.synchronize do
410
430
 
411
431
  # retrieve the current aggregate map
412
- aggregate_maps_element = @@aggregate_maps[task_id]
432
+ aggregate_maps_element = @@aggregate_maps[@task_id][task_id]
413
433
 
414
434
 
415
435
  # create aggregate map, if it doesn't exist
416
- if (aggregate_maps_element.nil?)
436
+ if aggregate_maps_element.nil?
417
437
  return if @map_action == "update"
418
438
  # create new event from previous map, if @push_previous_map_as_event is enabled
419
- if (@push_previous_map_as_event and !@@aggregate_maps.empty?)
420
- previous_map = @@aggregate_maps.shift[1].map
439
+ if @push_previous_map_as_event && !@@aggregate_maps[@task_id].empty?
440
+ previous_map = @@aggregate_maps[@task_id].shift[1].map
421
441
  event_to_yield = create_timeout_event(previous_map, task_id)
422
442
  end
423
443
  aggregate_maps_element = LogStash::Filters::Aggregate::Element.new(Time.now);
424
- @@aggregate_maps[task_id] = aggregate_maps_element
444
+ @@aggregate_maps[@task_id][task_id] = aggregate_maps_element
425
445
  else
426
446
  return if @map_action == "create"
427
447
  end
@@ -437,7 +457,7 @@ class LogStash::Filters::Aggregate < LogStash::Filters::Base
437
457
  end
438
458
 
439
459
  # delete the map if task is ended
440
- @@aggregate_maps.delete(task_id) if @end_of_task
460
+ @@aggregate_maps[@task_id].delete(task_id) if @end_of_task
441
461
 
442
462
  end
443
463
 
@@ -484,15 +504,20 @@ class LogStash::Filters::Aggregate < LogStash::Filters::Base
484
504
  # This method is invoked by LogStash every 5 seconds.
485
505
  def flush(options = {})
486
506
  # Protection against no timeout defined by logstash conf : define a default eviction instance with timeout = DEFAULT_TIMEOUT seconds
487
- if (@@eviction_instance.nil?)
488
- @@eviction_instance = self
489
- @timeout = DEFAULT_TIMEOUT
507
+ if @@default_timeout.nil?
508
+ @@default_timeout = DEFAULT_TIMEOUT
509
+ end
510
+ if !@@eviction_instance_map.has_key?(@task_id)
511
+ @@eviction_instance_map[@task_id] = self
512
+ @timeout = @@default_timeout
513
+ elsif @@eviction_instance_map[@task_id].timeout.nil?
514
+ @@eviction_instance_map[@task_id].timeout = @@default_timeout
490
515
  end
491
516
 
492
517
  # Launch eviction only every interval of (@timeout / 2) seconds
493
- if (@@eviction_instance == self && (@@last_eviction_timestamp.nil? || Time.now > @@last_eviction_timestamp + @timeout / 2))
518
+ if @@eviction_instance_map[@task_id] == self && (!@@last_eviction_timestamp_map.has_key?(@task_id) || Time.now > @@last_eviction_timestamp_map[@task_id] + @timeout / 2)
494
519
  events_to_flush = remove_expired_maps()
495
- @@last_eviction_timestamp = Time.now
520
+ @@last_eviction_timestamp_map[@task_id] = Time.now
496
521
  return events_to_flush
497
522
  end
498
523
 
@@ -507,9 +532,9 @@ class LogStash::Filters::Aggregate < LogStash::Filters::Base
507
532
 
508
533
  @@mutex.synchronize do
509
534
 
510
- @@aggregate_maps.delete_if do |key, element|
511
- if (element.creation_timestamp < min_timestamp)
512
- if (@push_previous_map_as_event) || (@push_map_as_event_on_timeout)
535
+ @@aggregate_maps[@task_id].delete_if do |key, element|
536
+ if element.creation_timestamp < min_timestamp
537
+ if @push_previous_map_as_event || @push_map_as_event_on_timeout
513
538
  events_to_flush << create_timeout_event(element.map, key)
514
539
  end
515
540
  next true
@@ -520,6 +545,30 @@ class LogStash::Filters::Aggregate < LogStash::Filters::Base
520
545
 
521
546
  return events_to_flush
522
547
  end
548
+
549
+ # return if this filter instance has any timeout option enabled in logstash configuration
550
+ def has_timeout_options?()
551
+ return (
552
+ timeout ||
553
+ timeout_code ||
554
+ push_map_as_event_on_timeout ||
555
+ push_previous_map_as_event ||
556
+ timeout_task_id_field ||
557
+ !timeout_tags.empty?
558
+ )
559
+ end
560
+
561
+ # display all possible timeout options
562
+ def display_timeout_options()
563
+ return [
564
+ "timeout",
565
+ "timeout_code",
566
+ "push_map_as_event_on_timeout",
567
+ "push_previous_map_as_event",
568
+ "timeout_task_id_field",
569
+ "timeout_tags"
570
+ ].join(", ")
571
+ end
523
572
 
524
573
  end # class LogStash::Filters::Aggregate
525
574
 
@@ -1,6 +1,6 @@
1
1
  Gem::Specification.new do |s|
2
2
  s.name = 'logstash-filter-aggregate'
3
- s.version = '2.3.1'
3
+ s.version = '2.4.0'
4
4
  s.licenses = ['Apache License (2.0)']
5
5
  s.summary = "The aim of this filter is to aggregate information available among several events (typically log lines) belonging to a same task, and finally push aggregated information into final task event."
6
6
  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"
@@ -6,7 +6,7 @@ require_relative "aggregate_spec_helper"
6
6
  describe LogStash::Filters::Aggregate do
7
7
 
8
8
  before(:each) do
9
- set_eviction_instance(nil)
9
+ reset_timeout_management()
10
10
  aggregate_maps.clear()
11
11
  @start_filter = setup_filter({ "map_action" => "create", "code" => "map['sql_duration'] = 0" })
12
12
  @update_filter = setup_filter({ "map_action" => "update", "code" => "map['sql_duration'] += event['duration']" })
@@ -17,7 +17,7 @@ describe LogStash::Filters::Aggregate do
17
17
  describe "and receiving an event without task_id" do
18
18
  it "does not record it" do
19
19
  @start_filter.filter(event())
20
- expect(aggregate_maps).to be_empty
20
+ expect(aggregate_maps["%{taskid}"]).to be_empty
21
21
  end
22
22
  end
23
23
  describe "and receiving an event with task_id" do
@@ -25,10 +25,10 @@ describe LogStash::Filters::Aggregate do
25
25
  event = start_event("taskid" => "id123")
26
26
  @start_filter.filter(event)
27
27
 
28
- expect(aggregate_maps.size).to eq(1)
29
- expect(aggregate_maps["id123"]).not_to be_nil
30
- expect(aggregate_maps["id123"].creation_timestamp).to be >= event["@timestamp"]
31
- expect(aggregate_maps["id123"].map["sql_duration"]).to eq(0)
28
+ expect(aggregate_maps["%{taskid}"].size).to eq(1)
29
+ expect(aggregate_maps["%{taskid}"]["id123"]).not_to be_nil
30
+ expect(aggregate_maps["%{taskid}"]["id123"].creation_timestamp).to be >= event["@timestamp"]
31
+ expect(aggregate_maps["%{taskid}"]["id123"].map["sql_duration"]).to eq(0)
32
32
  end
33
33
  end
34
34
 
@@ -45,9 +45,9 @@ describe LogStash::Filters::Aggregate do
45
45
  second_start_event = start_event("taskid" => "id124")
46
46
  @start_filter.filter(second_start_event)
47
47
 
48
- expect(aggregate_maps.size).to eq(1)
49
- expect(aggregate_maps["id124"].creation_timestamp).to be < second_start_event["@timestamp"]
50
- expect(aggregate_maps["id124"].map["sql_duration"]).to eq(first_update_event["duration"])
48
+ expect(aggregate_maps["%{taskid}"].size).to eq(1)
49
+ expect(aggregate_maps["%{taskid}"]["id124"].creation_timestamp).to be < second_start_event["@timestamp"]
50
+ expect(aggregate_maps["%{taskid}"]["id124"].map["sql_duration"]).to eq(first_update_event["duration"])
51
51
  end
52
52
  end
53
53
  end
@@ -59,7 +59,7 @@ describe LogStash::Filters::Aggregate do
59
59
  end_event = end_event("taskid" => "id124")
60
60
  @end_filter.filter(end_event)
61
61
 
62
- expect(aggregate_maps).to be_empty
62
+ expect(aggregate_maps["%{taskid}"]).to be_empty
63
63
  expect(end_event["sql_duration"]).to be_nil
64
64
  end
65
65
  end
@@ -72,7 +72,7 @@ describe LogStash::Filters::Aggregate do
72
72
  @task_id_value = "id_123"
73
73
  @start_event = start_event({"taskid" => @task_id_value})
74
74
  @start_filter.filter(@start_event)
75
- expect(aggregate_maps.size).to eq(1)
75
+ expect(aggregate_maps["%{taskid}"].size).to eq(1)
76
76
  end
77
77
 
78
78
  describe "and receiving an end event" do
@@ -80,7 +80,7 @@ describe LogStash::Filters::Aggregate do
80
80
  it "does nothing" do
81
81
  end_event = end_event()
82
82
  @end_filter.filter(end_event)
83
- expect(aggregate_maps.size).to eq(1)
83
+ expect(aggregate_maps["%{taskid}"].size).to eq(1)
84
84
  expect(end_event["sql_duration"]).to be_nil
85
85
  end
86
86
  end
@@ -90,21 +90,21 @@ describe LogStash::Filters::Aggregate do
90
90
  different_id_value = @task_id_value + "_different"
91
91
  @end_filter.filter(end_event("taskid" => different_id_value))
92
92
 
93
- expect(aggregate_maps.size).to eq(1)
94
- expect(aggregate_maps[@task_id_value]).not_to be_nil
93
+ expect(aggregate_maps["%{taskid}"].size).to eq(1)
94
+ expect(aggregate_maps["%{taskid}"][@task_id_value]).not_to be_nil
95
95
  end
96
96
  end
97
97
 
98
98
  describe "and the same id of the 'start event'" do
99
99
  it "add 'sql_duration' field to the end event and deletes the aggregate map associated to taskid" do
100
- expect(aggregate_maps.size).to eq(1)
100
+ expect(aggregate_maps["%{taskid}"].size).to eq(1)
101
101
 
102
102
  @update_filter.filter(update_event("taskid" => @task_id_value, "duration" => 2))
103
103
 
104
104
  end_event = end_event("taskid" => @task_id_value)
105
105
  @end_filter.filter(end_event)
106
106
 
107
- expect(aggregate_maps).to be_empty
107
+ expect(aggregate_maps["%{taskid}"]).to be_empty
108
108
  expect(end_event["sql_duration"]).to eq(2)
109
109
  end
110
110
 
@@ -117,7 +117,7 @@ describe LogStash::Filters::Aggregate do
117
117
  it "works as well as with a string task id" do
118
118
  start_event = start_event("taskid" => 124)
119
119
  @start_filter.filter(start_event)
120
- expect(aggregate_maps.size).to eq(1)
120
+ expect(aggregate_maps["%{taskid}"].size).to eq(1)
121
121
  end
122
122
  end
123
123
 
@@ -138,25 +138,25 @@ describe LogStash::Filters::Aggregate do
138
138
  @task_id_value = "id_123"
139
139
  @start_event = start_event({"taskid" => @task_id_value})
140
140
  @start_filter.filter(@start_event)
141
- expect(aggregate_maps.size).to eq(1)
141
+ expect(aggregate_maps["%{taskid}"].size).to eq(1)
142
142
  end
143
143
 
144
144
  describe "no timeout defined in none filter" do
145
145
  it "defines a default timeout on a default filter" do
146
- set_eviction_instance(nil)
147
- expect(eviction_instance).to be_nil
146
+ reset_timeout_management()
147
+ expect(taskid_eviction_instance).to be_nil
148
148
  @end_filter.flush()
149
- expect(eviction_instance).to eq(@end_filter)
149
+ expect(taskid_eviction_instance).to eq(@end_filter)
150
150
  expect(@end_filter.timeout).to eq(LogStash::Filters::Aggregate::DEFAULT_TIMEOUT)
151
151
  end
152
152
  end
153
153
 
154
154
  describe "timeout is defined on another filter" do
155
- it "eviction_instance is not updated" do
156
- expect(eviction_instance).not_to be_nil
155
+ it "taskid eviction_instance is not updated" do
156
+ expect(taskid_eviction_instance).not_to be_nil
157
157
  @start_filter.flush()
158
- expect(eviction_instance).not_to eq(@start_filter)
159
- expect(eviction_instance).to eq(@end_filter)
158
+ expect(taskid_eviction_instance).not_to eq(@start_filter)
159
+ expect(taskid_eviction_instance).to eq(@end_filter)
160
160
  end
161
161
  end
162
162
 
@@ -164,20 +164,20 @@ describe LogStash::Filters::Aggregate do
164
164
  it "event is not removed" do
165
165
  sleep(2)
166
166
  @start_filter.flush()
167
- expect(aggregate_maps.size).to eq(1)
167
+ expect(aggregate_maps["%{taskid}"].size).to eq(1)
168
168
  end
169
169
  end
170
170
 
171
171
  describe "timeout defined on the filter" do
172
172
  it "event is not removed if not expired" do
173
173
  entries = @end_filter.flush()
174
- expect(aggregate_maps.size).to eq(1)
174
+ expect(aggregate_maps["%{taskid}"].size).to eq(1)
175
175
  expect(entries).to be_empty
176
176
  end
177
177
  it "removes event if expired and creates a new timeout event" do
178
178
  sleep(2)
179
179
  entries = @end_filter.flush()
180
- expect(aggregate_maps).to be_empty
180
+ expect(aggregate_maps["%{taskid}"]).to be_empty
181
181
  expect(entries.size).to eq(1)
182
182
  expect(entries[0]['my_id']).to eq("id_123") # task id
183
183
  expect(entries[0]["sql_duration"]).to eq(0) # Aggregation map
@@ -186,21 +186,29 @@ describe LogStash::Filters::Aggregate do
186
186
  end
187
187
  end
188
188
 
189
+ describe "timeout defined on another filter with another task_id pattern" do
190
+ it "does not remove event" do
191
+ another_filter = setup_filter({ "task_id" => "%{another_taskid}", "code" => "", "timeout" => 1 })
192
+ sleep(2)
193
+ entries = another_filter.flush()
194
+ expect(aggregate_maps["%{taskid}"].size).to eq(1)
195
+ expect(entries).to be_empty
196
+ end
197
+ end
189
198
  end
190
199
 
191
200
  context "aggregate_maps_path option is defined, " do
192
201
  describe "close event append then register event append, " do
193
202
  it "stores aggregate maps to configured file and then loads aggregate maps from file" do
194
-
195
203
  store_file = "aggregate_maps"
196
204
  expect(File.exist?(store_file)).to be false
197
205
 
198
206
  store_filter = setup_filter({ "code" => "map['sql_duration'] = 0", "aggregate_maps_path" => store_file })
199
- expect(aggregate_maps).to be_empty
207
+ expect(aggregate_maps["%{taskid}"]).to be_empty
200
208
 
201
209
  start_event = start_event("taskid" => 124)
202
210
  filter = store_filter.filter(start_event)
203
- expect(aggregate_maps.size).to eq(1)
211
+ expect(aggregate_maps["%{taskid}"].size).to eq(1)
204
212
 
205
213
  store_filter.close()
206
214
  expect(File.exist?(store_file)).to be true
@@ -208,44 +216,49 @@ describe LogStash::Filters::Aggregate do
208
216
 
209
217
  store_filter = setup_filter({ "code" => "map['sql_duration'] = 0", "aggregate_maps_path" => store_file })
210
218
  expect(File.exist?(store_file)).to be false
211
- expect(aggregate_maps.size).to eq(1)
212
-
219
+ expect(aggregate_maps["%{taskid}"].size).to eq(1)
213
220
  end
214
221
  end
215
222
 
216
223
  describe "when aggregate_maps_path option is defined in 2 instances, " do
217
224
  it "raises Logstash::ConfigurationError" do
218
-
219
225
  expect {
220
226
  setup_filter({ "code" => "", "aggregate_maps_path" => "aggregate_maps1" })
221
227
  setup_filter({ "code" => "", "aggregate_maps_path" => "aggregate_maps2" })
222
228
  }.to raise_error(LogStash::ConfigurationError)
223
-
224
229
  end
225
230
  end
226
231
  end
227
232
 
228
233
  context "push_previous_map_as_event option is defined, " do
234
+ describe "when push_previous_map_as_event option is activated on another filter with same task_id pattern" do
235
+ it "should throw a LogStash::ConfigurationError" do
236
+ expect {
237
+ setup_filter({"code" => "map['taskid'] = event['taskid']", "push_previous_map_as_event" => true})
238
+ }.to raise_error(LogStash::ConfigurationError)
239
+ end
240
+ end
241
+
229
242
  describe "when a new task id is detected, " do
230
243
  it "should push previous map as new event" do
231
- push_filter = setup_filter({ "code" => "map['taskid'] = event['taskid']", "push_previous_map_as_event" => true, "timeout" => 5 })
232
- push_filter.filter(event({"taskid" => "1"})) { |yield_event| fail "task 1 shouldn't have yield event" }
233
- push_filter.filter(event({"taskid" => "2"})) { |yield_event| expect(yield_event["taskid"]).to eq("1") }
234
- expect(aggregate_maps.size).to eq(1)
244
+ push_filter = setup_filter({ "task_id" => "%{ppm_id}", "code" => "map['ppm_id'] = event['ppm_id']", "push_previous_map_as_event" => true, "timeout" => 5 })
245
+ push_filter.filter(event({"ppm_id" => "1"})) { |yield_event| fail "task 1 shouldn't have yield event" }
246
+ push_filter.filter(event({"ppm_id" => "2"})) { |yield_event| expect(yield_event["ppm_id"]).to eq("1") }
247
+ expect(aggregate_maps["%{ppm_id}"].size).to eq(1)
235
248
  end
236
249
  end
237
250
 
238
251
  describe "when timeout happens, " do
239
252
  it "flush method should return last map as new event" do
240
- push_filter = setup_filter({ "code" => "map['taskid'] = event['taskid']", "push_previous_map_as_event" => true, "timeout" => 1, "timeout_code" => "event['test'] = 'testValue'" })
241
- push_filter.filter(event({"taskid" => "1"}))
253
+ push_filter = setup_filter({ "task_id" => "%{ppm_id}", "code" => "map['ppm_id'] = event['ppm_id']", "push_previous_map_as_event" => true, "timeout" => 1, "timeout_code" => "event['test'] = 'testValue'" })
254
+ push_filter.filter(event({"ppm_id" => "1"}))
242
255
  sleep(2)
243
256
  events_to_flush = push_filter.flush()
244
257
  expect(events_to_flush).not_to be_nil
245
258
  expect(events_to_flush.size).to eq(1)
246
- expect(events_to_flush[0]["taskid"]).to eq("1")
259
+ expect(events_to_flush[0]["ppm_id"]).to eq("1")
247
260
  expect(events_to_flush[0]['test']).to eq("testValue")
248
- expect(aggregate_maps.size).to eq(0)
261
+ expect(aggregate_maps["%{ppm_id}"].size).to eq(0)
249
262
  end
250
263
  end
251
264
  end
@@ -39,11 +39,11 @@ def aggregate_maps()
39
39
  LogStash::Filters::Aggregate.class_variable_get(:@@aggregate_maps)
40
40
  end
41
41
 
42
- def eviction_instance()
43
- LogStash::Filters::Aggregate.class_variable_get(:@@eviction_instance)
42
+ def taskid_eviction_instance()
43
+ LogStash::Filters::Aggregate.class_variable_get(:@@eviction_instance_map)["%{taskid}"]
44
44
  end
45
45
 
46
- def set_eviction_instance(new_value)
47
- LogStash::Filters::Aggregate.class_variable_set(:@@eviction_instance, new_value)
46
+ def reset_timeout_management()
47
+ LogStash::Filters::Aggregate.class_variable_set(:@@default_timeout, nil)
48
+ LogStash::Filters::Aggregate.class_variable_get(:@@eviction_instance_map).clear()
48
49
  end
49
-
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: logstash-filter-aggregate
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.3.1
4
+ version: 2.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Elastic
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2016-10-01 00:00:00.000000000 Z
12
+ date: 2016-10-15 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  requirement: !ruby/object:Gem::Requirement