logstash-filter-math 1.1.0 → 1.1.1

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
  SHA256:
3
- metadata.gz: bcecb6c0a2a2be1670c7c579a48b0565b796609329d51ef1f12f2a6603ff25eb
4
- data.tar.gz: 2ff472b3df566011a47c83bad03066c05ad7028f0a9be7aa4b29b11ae4f86bd6
3
+ metadata.gz: 7acec65c8e0272d6a835d61e803de1b244cf838d649930dfaf8649e45c3388d0
4
+ data.tar.gz: 4ca406f2e0403317e20aaf74463c61b834399aa4840ef6a69d81357a24373d6d
5
5
  SHA512:
6
- metadata.gz: af24bac7497bfd33eaf330480cabbee1598896c939cccafbfd55bb170cb41e566a8cd98552c7e1d1eb49e143f60ce678f1849cc5a9a7fd5b26b1bd68db859eb1
7
- data.tar.gz: 7b34a8f23971a80328ca1bc33f909a618d01ebddf5b0d47e24d531ed45a350b5e0fea820e0f08d9a7beb93b27646dbd0c18d8e03a5c3a6dc27c9e444d0cfa538
6
+ metadata.gz: a005e6bf35f50b0e18207c875cab6d4878ad49088b8fe90bc1210946610cee915f281f427851407f5927a81e7ebea1dce4d942ec2078da501ded4a70a85e0ce5
7
+ data.tar.gz: 4dc84a9bd0a94b1c4ae727485f0ef6ba26e4d4c4ff96dbbb14edd59dbde45a101b6ba32da9ba77c389893a188e13c0642ae6a9a9a8a4498784d30068700caafa
@@ -1,3 +1,6 @@
1
+ ## 1.1.1
2
+ - Fix to make registers threadsafe. [math filter #10](https://github.com/logstash-plugins/logstash-filter-math/issues/10)
3
+
1
4
  ## 1.1.0
2
5
  - Bumping to this version as this plugin was published to rubygems at v1.0
3
6
  - Fixed add backward compatible Operator reference aliases `'sub', 'mpx'` [math filter #8](https://github.com/logstash-plugins/logstash-filter-math/pull/8)
@@ -0,0 +1,31 @@
1
+ # encoding: utf-8
2
+
3
+ module LogStash module Filters
4
+ class EventRegisterContext
5
+
6
+ attr_reader :event, :register
7
+
8
+ def initialize(event)
9
+ @event = event
10
+ @register = []
11
+ end
12
+
13
+ def get(element)
14
+ case element
15
+ when MathCalculationElements::RegisterElement
16
+ @register[element.key]
17
+ when MathCalculationElements::FieldElement
18
+ @event.get(element.key)
19
+ end
20
+ end
21
+
22
+ def set(element, value)
23
+ case element
24
+ when MathCalculationElements::RegisterElement
25
+ @register[element.key] = value
26
+ when MathCalculationElements::FieldElement
27
+ @event.set(element.key, value)
28
+ end
29
+ end
30
+ end
31
+ end end
@@ -3,6 +3,7 @@
3
3
  require "logstash/namespace"
4
4
  require "logstash/filters/base"
5
5
 
6
+ require_relative "event_register_context"
6
7
  require_relative "math_functions"
7
8
  require_relative "math_calculation_elements"
8
9
 
@@ -58,7 +59,6 @@ module LogStash module Filters class Math < LogStash::Filters::Base
58
59
  # is exactly 4 fields and the first field is a valid calculation operator name.
59
60
  @calculate_copy = []
60
61
  all_function_keys = functions.keys
61
- @register = []
62
62
  calculate.each do |calculation|
63
63
  if calculation.size != 4
64
64
  raise LogStash::ConfigurationError, I18n.t(
@@ -79,8 +79,8 @@ module LogStash module Filters class Math < LogStash::Filters::Base
79
79
  end
80
80
  function = functions[function_key]
81
81
 
82
- left_element = MathCalculationElements.build(operand1, 1, @register)
83
- right_element = MathCalculationElements.build(operand2, 2, @register)
82
+ left_element = MathCalculationElements.build(operand1, 1)
83
+ right_element = MathCalculationElements.build(operand2, 2)
84
84
  if right_element.literal?
85
85
  lhs = left_element.literal? ? left_element.get : 1
86
86
  warning = function.invalid?(lhs, right_element.get)
@@ -93,7 +93,7 @@ module LogStash module Filters class Math < LogStash::Filters::Base
93
93
  )
94
94
  end
95
95
  end
96
- result_element = MathCalculationElements.build(target, 3, @register)
96
+ result_element = MathCalculationElements.build(target, 3)
97
97
  @calculate_copy << [function, left_element, right_element, result_element]
98
98
  end
99
99
  if @calculate_copy.last.last.is_a?(MathCalculationElements::RegisterElement)
@@ -108,18 +108,18 @@ module LogStash module Filters class Math < LogStash::Filters::Base
108
108
 
109
109
  def filter(event)
110
110
  event_changed = false # can exit if none of the calculations are are suitable
111
- @register.clear # don't carry over register results from one event to the next.
111
+ context = EventRegisterContext.new(event) # creates a new registers array, each element can use the event or register from the store
112
112
  @calculate_copy.each do |function, left_element, right_element, result_element|
113
113
  logger.debug("executing", "function" => function.name, "left_field" => left_element, "right_field" => right_element, "target" => result_element)
114
114
  # TODO add support for automatic conversion to Numeric if String
115
- operand1 = left_element.get(event)
116
- operand2 = right_element.get(event)
115
+ operand1 = left_element.get(context)
116
+ operand2 = right_element.get(context)
117
117
  # allow all the validation warnings to be logged before we skip to next
118
118
  next if operand1.nil? || operand2.nil?
119
119
  next if function.invalid?(operand1, operand2, event)
120
120
 
121
121
  result = function.call(operand1, operand2)
122
- result_element.set(result, event)
122
+ result_element.set(result, context)
123
123
  logger.debug("calculation result stored", "function" => function.name, "target" => result_element, "result" => result)
124
124
  event_changed = true
125
125
  end
@@ -5,7 +5,7 @@ module LogStash module Filters
5
5
  module MathCalculationElements
6
6
  REGISTER_REFERENCE_RE = /^MEM\[(\d+)]$/
7
7
 
8
- def self.build(reference, position, register)
8
+ def self.build(reference, position)
9
9
  case reference
10
10
  when Numeric
11
11
  if position == 3
@@ -17,7 +17,7 @@ module LogStash module Filters
17
17
  when String
18
18
  match = REGISTER_REFERENCE_RE.match(reference)
19
19
  if match
20
- RegisterElement.new(reference, position, match[1].to_i, register)
20
+ RegisterElement.new(reference, position, match[1].to_i)
21
21
  else
22
22
  FieldElement.new(reference, position)
23
23
  end
@@ -28,25 +28,28 @@ module LogStash module Filters
28
28
 
29
29
  class RegisterElement
30
30
  # supports `get` and `set`
31
- def initialize(reference, position, index, register)
31
+ def initialize(reference, position, index)
32
32
  @reference = reference
33
33
  @position = position
34
34
  @index = index
35
- @register = register
36
35
  @description = (position == 3 ? "#{@index}" : "operand #{@position}").prepend("register ").concat(": '#{@reference}'")
37
36
  end
38
37
 
38
+ def key
39
+ @index
40
+ end
41
+
39
42
  def literal?
40
43
  false
41
44
  end
42
45
 
43
- def set(value, event)
46
+ def set(value, event_register_context)
44
47
  # raise usage error if called when position != 3 ??
45
- @register[@index] = value
48
+ event_register_context.set(self, value)
46
49
  end
47
50
 
48
- def get(event)
49
- @register[@index] #log warning if nil
51
+ def get(event_register_context)
52
+ event_register_context.get(self) #log warning if nil
50
53
  end
51
54
 
52
55
  def inspect
@@ -60,6 +63,7 @@ module LogStash module Filters
60
63
 
61
64
  class FieldElement
62
65
  include LogStash::Util::Loggable
66
+
63
67
  # supports `get` and `set`
64
68
  def initialize(field, position)
65
69
  @field = field
@@ -67,18 +71,22 @@ module LogStash module Filters
67
71
  @description = (position == 3 ? "result" : "operand #{@position}").prepend("event ").concat(": '#{@field}'")
68
72
  end
69
73
 
74
+ def key
75
+ @field
76
+ end
77
+
70
78
  def literal?
71
79
  false
72
80
  end
73
81
 
74
- def set(value, event)
75
- event.set(@field, value)
82
+ def set(value, event_register_context)
83
+ event_register_context.set(self, value)
76
84
  end
77
85
 
78
- def get(event)
79
- value = event.get(@field)
86
+ def get(event_register_context)
87
+ value = event_register_context.get(self)
80
88
  if value.nil?
81
- logger.warn("field not found", "field" => @field, "event" => event.to_hash)
89
+ logger.warn("field not found", "field" => @field, "event" => event_register_context.event.to_hash)
82
90
  return nil
83
91
  end
84
92
  case value
@@ -87,7 +95,7 @@ module LogStash module Filters
87
95
  when LogStash::Timestamp, Time
88
96
  value.to_f
89
97
  else
90
- logger.warn("field value is not numeric or time", "field" => @field, "value" => value, "event" => event.to_hash)
98
+ logger.warn("field value is not numeric or time", "field" => @field, "value" => value, "event" => event_register_context.event.to_hash)
91
99
  nil
92
100
  end
93
101
  end
@@ -108,11 +116,15 @@ module LogStash module Filters
108
116
  @position = position
109
117
  end
110
118
 
119
+ def key
120
+ nil
121
+ end
122
+
111
123
  def literal?
112
124
  true
113
125
  end
114
126
 
115
- def get(event = nil)
127
+ def get(event_register_context = nil)
116
128
  @literal
117
129
  end
118
130
 
@@ -1,6 +1,6 @@
1
1
  Gem::Specification.new do |s|
2
2
  s.name = 'logstash-filter-math'
3
- s.version = '1.1.0'
3
+ s.version = '1.1.1'
4
4
  s.licenses = ['Apache License (2.0)']
5
5
  s.summary = "Do simple math functions on numeric fields."
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/plugin install gemname. This gem is not a stand-alone program"
@@ -443,6 +443,43 @@ describe LogStash::Filters::Math do
443
443
  end
444
444
  end
445
445
 
446
+ describe "multithreading" do
447
+ it "should still calculate correctly" do
448
+ array = [
449
+ [ "+", "[var1]", "[var2]", "MEM[0]" ],
450
+ [ "-", "[var3]", "[var4]", "MEM[1]" ],
451
+ [ "*", "MEM[0]", "MEM[1]", "[result]" ]
452
+ ]
453
+
454
+ math_hash = {"calculate" => array}
455
+
456
+ event1 = LogStash::Event.new("var1" => 3.4, "var2" => 6.6, "var3" => 4.4, "var4" => 2.4)
457
+ event2 = LogStash::Event.new("var1" => 6.8, "var2" => 13.2, "var3" => 8.8, "var4" => 4.8)
458
+ plugin = described_class.new(math_hash)
459
+ plugin.register
460
+ expect do
461
+ thread1 = Thread.new(plugin, event1) do |plugin, event|
462
+ 100.times do
463
+ plugin.filter(event)
464
+ result = event.get("result")
465
+ raise "Thread 1 failed, result is: #{result}" if result != 20.000000000000004
466
+ sleep 0.011
467
+ end
468
+ end
469
+ thread2 = Thread.new(plugin, event2) do |plugin, event|
470
+ 100.times do
471
+ plugin.filter(event)
472
+ result = event.get("result")
473
+ raise "Thread 2 failed, result is: #{result}" if result != 80.00000000000001
474
+ sleep 0.01
475
+ end
476
+ end
477
+ thread1.join
478
+ thread2.join
479
+ end.not_to raise_exception
480
+ end
481
+ end
482
+
446
483
  describe "Sequence" do
447
484
  # The logstash config.
448
485
  config <<-CONFIG
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: logstash-filter-math
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.0
4
+ version: 1.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Elastic
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-06-29 00:00:00.000000000 Z
11
+ date: 2018-09-18 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  requirement: !ruby/object:Gem::Requirement
@@ -58,6 +58,7 @@ files:
58
58
  - LICENSE
59
59
  - README.md
60
60
  - docs/index.asciidoc
61
+ - lib/logstash/filters/event_register_context.rb
61
62
  - lib/logstash/filters/math.rb
62
63
  - lib/logstash/filters/math_calculation_elements.rb
63
64
  - lib/logstash/filters/math_functions.rb