logstash-core 6.0.1-java → 6.1.0-java

Sign up to get free protection for your applications and to get access to all the features.
Files changed (64) hide show
  1. checksums.yaml +4 -4
  2. data/gemspec_jars.rb +1 -1
  3. data/lib/logstash-core/logstash-core.jar +0 -0
  4. data/lib/logstash-core/logstash-core.rb +14 -2
  5. data/lib/logstash-core_jars.rb +4 -2
  6. data/lib/logstash/agent.rb +8 -2
  7. data/lib/logstash/api/modules/node.rb +11 -5
  8. data/lib/logstash/api/modules/stats.rb +13 -7
  9. data/lib/logstash/compiler.rb +6 -10
  10. data/lib/logstash/compiler/lscl.rb +10 -1
  11. data/lib/logstash/compiler/lscl/helpers.rb +3 -1
  12. data/lib/logstash/config/mixin.rb +2 -2
  13. data/lib/logstash/environment.rb +1 -6
  14. data/lib/logstash/errors.rb +1 -1
  15. data/lib/logstash/event.rb +0 -2
  16. data/lib/logstash/filter_delegator.rb +1 -2
  17. data/lib/logstash/instrument/metric_type/counter.rb +1 -1
  18. data/lib/logstash/instrument/metric_type/gauge.rb +1 -1
  19. data/lib/logstash/instrument/wrapped_write_client.rb +1 -1
  20. data/lib/logstash/java_filter_delegator.rb +79 -0
  21. data/lib/logstash/java_pipeline.rb +690 -0
  22. data/lib/logstash/json.rb +4 -29
  23. data/lib/logstash/output_delegator.rb +3 -2
  24. data/lib/logstash/patches/bugfix_jruby_2558.rb +1 -1
  25. data/lib/logstash/pipeline.rb +32 -89
  26. data/lib/logstash/pipeline_action/create.rb +8 -2
  27. data/lib/logstash/pipeline_action/reload.rb +6 -1
  28. data/lib/logstash/pipeline_reporter.rb +2 -1
  29. data/lib/logstash/pipeline_settings.rb +1 -0
  30. data/lib/logstash/plugins/plugin_factory.rb +100 -0
  31. data/lib/logstash/plugins/registry.rb +18 -7
  32. data/lib/logstash/queue_factory.rb +3 -1
  33. data/lib/logstash/runner.rb +13 -56
  34. data/lib/logstash/settings.rb +2 -2
  35. data/lib/logstash/timestamp.rb +0 -1
  36. data/lib/logstash/util.rb +13 -21
  37. data/lib/logstash/util/java_version.rb +0 -1
  38. data/lib/logstash/util/settings_helper.rb +79 -0
  39. data/lib/logstash/util/{environment_variables.rb → substitution_variables.rb} +10 -8
  40. data/lib/logstash/util/wrapped_acked_queue.rb +17 -108
  41. data/lib/logstash/util/wrapped_synchronous_queue.rb +38 -178
  42. data/locales/en.yml +2 -0
  43. data/spec/conditionals_spec.rb +235 -80
  44. data/spec/logstash/api/modules/node_spec.rb +11 -0
  45. data/spec/logstash/compiler/compiler_spec.rb +28 -2
  46. data/spec/logstash/environment_spec.rb +0 -5
  47. data/spec/logstash/event_spec.rb +7 -2
  48. data/spec/logstash/filter_delegator_spec.rb +1 -1
  49. data/spec/logstash/filters/base_spec.rb +30 -28
  50. data/spec/logstash/instrument/wrapped_write_client_spec.rb +2 -2
  51. data/spec/logstash/java_filter_delegator_spec.rb +176 -0
  52. data/spec/logstash/java_pipeline_spec.rb +933 -0
  53. data/spec/logstash/json_spec.rb +27 -45
  54. data/spec/logstash/plugins/registry_spec.rb +7 -0
  55. data/spec/logstash/queue_factory_spec.rb +5 -2
  56. data/spec/logstash/settings_spec.rb +1 -1
  57. data/spec/logstash/util/java_version_spec.rb +1 -3
  58. data/spec/logstash/util/wrapped_synchronous_queue_spec.rb +27 -24
  59. data/spec/logstash/webserver_spec.rb +3 -6
  60. data/spec/support/helpers.rb +5 -0
  61. data/spec/support/pipeline/pipeline_helpers.rb +97 -0
  62. data/versions-gem-copy.yml +5 -2
  63. metadata +14 -5
  64. data/lib/logstash/patches/rubygems.rb +0 -38
@@ -2,33 +2,22 @@
2
2
 
3
3
  module LogStash; module Util
4
4
  class WrappedSynchronousQueue
5
- java_import java.util.concurrent.SynchronousQueue
5
+ java_import java.util.concurrent.ArrayBlockingQueue
6
6
  java_import java.util.concurrent.TimeUnit
7
+ java_import org.logstash.common.LsQueueUtils
7
8
 
8
- def initialize
9
- @queue = java.util.concurrent.SynchronousQueue.new
9
+ def initialize (size)
10
+ @queue = ArrayBlockingQueue.new(size)
10
11
  end
11
12
 
12
- # Push an object to the queue if the queue is full
13
- # it will block until the object can be added to the queue.
14
- #
15
- # @param [Object] Object to add to the queue
16
- def push(obj)
17
- @queue.put(obj)
18
- end
19
- alias_method(:<<, :push)
20
-
21
- # Block for X millis
22
- def poll(millis)
23
- @queue.poll(millis, TimeUnit::MILLISECONDS)
24
- end
13
+ attr_reader :queue
25
14
 
26
15
  def write_client
27
- WriteClient.new(self)
16
+ WriteClient.new(@queue)
28
17
  end
29
18
 
30
19
  def read_client
31
- ReadClient.new(self)
20
+ ReadClient.new(@queue)
32
21
  end
33
22
 
34
23
  def close
@@ -42,15 +31,14 @@ module LogStash; module Util
42
31
 
43
32
  def initialize(queue, batch_size = 125, wait_for = 250)
44
33
  @queue = queue
45
- @mutex = Mutex.new
46
34
  # Note that @inflight_batches as a central mechanism for tracking inflight
47
35
  # batches will fail if we have multiple read clients in the pipeline.
48
- @inflight_batches = {}
36
+ @inflight_batches = Concurrent::Map.new
49
37
 
50
38
  # allow the worker thread to report the execution time of the filter + output
51
- @inflight_clocks = {}
39
+ @inflight_clocks = Concurrent::Map.new
52
40
  @batch_size = batch_size
53
- @wait_for = wait_for
41
+ @wait_for = TimeUnit::NANOSECONDS.convert(wait_for, TimeUnit::MILLISECONDS)
54
42
  end
55
43
 
56
44
  def close
@@ -58,12 +46,12 @@ module LogStash; module Util
58
46
  end
59
47
 
60
48
  def empty?
61
- true # synchronous queue is alway empty
49
+ @queue.isEmpty
62
50
  end
63
51
 
64
52
  def set_batch_dimensions(batch_size, wait_for)
65
53
  @batch_size = batch_size
66
- @wait_for = wait_for
54
+ @wait_for = TimeUnit::NANOSECONDS.convert(wait_for, TimeUnit::MILLISECONDS)
67
55
  end
68
56
 
69
57
  def set_events_metric(metric)
@@ -89,185 +77,84 @@ module LogStash; module Util
89
77
  end
90
78
 
91
79
  def inflight_batches
92
- @mutex.lock
93
- begin
94
- yield(@inflight_batches)
95
- ensure
96
- @mutex.unlock
97
- end
98
- end
99
-
100
- def current_inflight_batch
101
- @inflight_batches.fetch(Thread.current, [])
80
+ yield(@inflight_batches)
102
81
  end
103
82
 
104
83
  # create a new empty batch
105
84
  # @return [ReadBatch] a new empty read batch
106
85
  def new_batch
107
- ReadBatch.new(@queue, @batch_size, @wait_for)
86
+ ReadBatch.new(@queue, 0, 0)
108
87
  end
109
88
 
110
89
  def read_batch
111
- batch = new_batch
112
- @mutex.lock
113
- begin
114
- batch.read_next
115
- ensure
116
- @mutex.unlock
117
- end
90
+ batch = ReadBatch.new(@queue, @batch_size, @wait_for)
118
91
  start_metrics(batch)
119
92
  batch
120
93
  end
121
94
 
122
95
  def start_metrics(batch)
123
- @mutex.lock
124
- # there seems to be concurrency issues with metrics, keep it in the mutex
125
- begin
126
- set_current_thread_inflight_batch(batch)
127
- start_clock
128
- ensure
129
- @mutex.unlock
130
- end
131
- end
132
-
133
- def set_current_thread_inflight_batch(batch)
134
- @inflight_batches[Thread.current] = batch
96
+ thread = Thread.current
97
+ @inflight_batches[thread] = batch
98
+ @inflight_clocks[thread] = java.lang.System.nano_time
135
99
  end
136
100
 
137
101
  def close_batch(batch)
138
- @mutex.lock
139
- begin
140
- # there seems to be concurrency issues with metrics, keep it in the mutex
141
- @inflight_batches.delete(Thread.current)
142
- stop_clock(batch)
143
- ensure
144
- @mutex.unlock
145
- end
146
- end
147
-
148
- def start_clock
149
- @inflight_clocks[Thread.current] = java.lang.System.nano_time
150
- end
151
-
152
- def stop_clock(batch)
153
- unless @inflight_clocks[Thread.current].nil?
102
+ thread = Thread.current
103
+ @inflight_batches.delete(thread)
104
+ start_time = @inflight_clocks.get_and_set(thread, nil)
105
+ unless start_time.nil?
154
106
  if batch.size > 0
155
107
  # only stop (which also records) the metrics if the batch is non-empty.
156
108
  # start_clock is now called at empty batch creation and an empty batch could
157
109
  # stay empty all the way down to the close_batch call.
158
- time_taken = (java.lang.System.nano_time - @inflight_clocks[Thread.current]) / 1_000_000
110
+ time_taken = (java.lang.System.nano_time - start_time) / 1_000_000
159
111
  @event_metric_time.increment(time_taken)
160
112
  @pipeline_metric_time.increment(time_taken)
161
113
  end
162
- @inflight_clocks.delete(Thread.current)
163
114
  end
164
115
  end
165
116
 
166
- def add_filtered_metrics(batch)
167
- @event_metric_filtered.increment(batch.filtered_size)
168
- @pipeline_metric_filtered.increment(batch.filtered_size)
117
+ def add_filtered_metrics(filtered_size)
118
+ @event_metric_filtered.increment(filtered_size)
119
+ @pipeline_metric_filtered.increment(filtered_size)
169
120
  end
170
121
 
171
- def add_output_metrics(batch)
172
- @event_metric_out.increment(batch.filtered_size)
173
- @pipeline_metric_out.increment(batch.filtered_size)
122
+ def add_output_metrics(filtered_size)
123
+ @event_metric_out.increment(filtered_size)
124
+ @pipeline_metric_out.increment(filtered_size)
174
125
  end
175
126
  end
176
127
 
177
128
  class ReadBatch
178
129
  def initialize(queue, size, wait)
179
- @queue = queue
180
- @size = size
181
- @wait = wait
182
-
183
- @originals = Hash.new
184
-
185
130
  # TODO: disabled for https://github.com/elastic/logstash/issues/6055 - will have to properly refactor
186
131
  # @cancelled = Hash.new
187
132
 
188
- @generated = Hash.new
189
- @iterating_temp = Hash.new
190
- @iterating = false # Atomic Boolean maybe? Although batches are not shared across threads
191
- @acked_batch = nil
192
- end
193
-
194
- def read_next
195
- @size.times do |t|
196
- event = @queue.poll(@wait)
197
- return if event.nil? # queue poll timed out
198
-
199
- @originals[event] = true
200
- end
133
+ @originals = LsQueueUtils.drain(queue, size, wait)
201
134
  end
202
135
 
203
136
  def merge(event)
204
- return if event.nil? || @originals.key?(event)
205
- # take care not to cause @generated to change during iteration
206
- # @iterating_temp is merged after the iteration
207
- if iterating?
208
- @iterating_temp[event] = true
209
- else
210
- # the periodic flush could generate events outside of an each iteration
211
- @generated[event] = true
212
- end
213
- end
214
-
215
- def cancel(event)
216
- # TODO: disabled for https://github.com/elastic/logstash/issues/6055 - will have to properly refactor
217
- raise("cancel is unsupported")
218
- # @cancelled[event] = true
137
+ return if event.nil?
138
+ @originals.add(event)
219
139
  end
220
140
 
221
141
  def to_a
222
142
  events = []
223
- each {|e| events << e}
143
+ @originals.each {|e| events << e unless e.cancelled?}
224
144
  events
225
145
  end
226
146
 
227
147
  def each(&blk)
228
- # take care not to cause @originals or @generated to change during iteration
229
- @iterating = true
230
-
231
148
  # below the checks for @cancelled.include?(e) have been replaced by e.cancelled?
232
149
  # TODO: for https://github.com/elastic/logstash/issues/6055 = will have to properly refactor
233
- @originals.each do |e, _|
234
- blk.call(e) unless e.cancelled?
235
- end
236
- @generated.each do |e, _|
237
- blk.call(e) unless e.cancelled?
238
- end
239
- @iterating = false
240
- update_generated
241
- end
242
-
243
- def size
244
- filtered_size
245
- end
246
-
247
- def starting_size
248
- @originals.size
150
+ @originals.each {|e| blk.call(e) unless e.cancelled?}
249
151
  end
250
152
 
251
153
  def filtered_size
252
- @originals.size + @generated.size
253
- end
254
-
255
- def cancelled_size
256
- # TODO: disabled for https://github.com/elastic/logstash/issues/6055 = will have to properly refactor
257
- raise("cancelled_size is unsupported ")
258
- # @cancelled.size
259
- end
260
-
261
- private
262
-
263
- def iterating?
264
- @iterating
154
+ @originals.size
265
155
  end
266
156
 
267
- def update_generated
268
- @generated.update(@iterating_temp)
269
- @iterating_temp.clear
270
- end
157
+ alias_method(:size, :filtered_size)
271
158
  end
272
159
 
273
160
  class WriteClient
@@ -275,40 +162,13 @@ module LogStash; module Util
275
162
  @queue = queue
276
163
  end
277
164
 
278
- def get_new_batch
279
- WriteBatch.new
280
- end
281
-
282
165
  def push(event)
283
- @queue.push(event)
166
+ @queue.put(event)
284
167
  end
285
168
  alias_method(:<<, :push)
286
169
 
287
170
  def push_batch(batch)
288
- batch.each do |event|
289
- push(event)
290
- end
291
- end
292
- end
293
-
294
- class WriteBatch
295
- def initialize
296
- @events = []
297
- end
298
-
299
- def size
300
- @events.size
301
- end
302
-
303
- def push(event)
304
- @events.push(event)
305
- end
306
- alias_method(:<<, :push)
307
-
308
- def each(&blk)
309
- @events.each do |e|
310
- blk.call(e)
311
- end
171
+ LsQueueUtils.addAll(@queue, batch)
312
172
  end
313
173
  end
314
174
  end
@@ -264,6 +264,8 @@ en:
264
264
  http_port: Web API http port
265
265
  pipeline-workers: |+
266
266
  Sets the number of pipeline workers to run.
267
+ experimental-java-execution: |+
268
+ (Experimental) Use new Java execution engine.
267
269
  pipeline-batch-size: |+
268
270
  Size of batches the pipeline is to work in.
269
271
  pipeline-batch-delay: |+
@@ -1,7 +1,9 @@
1
1
  # encoding: utf-8
2
2
  require 'spec_helper'
3
+ require 'support/pipeline/pipeline_helpers'
3
4
 
4
5
  module ConditionalFanciness
6
+ include PipelineHelpers
5
7
  def description
6
8
  return self.metadata[:description]
7
9
  end
@@ -76,21 +78,21 @@ describe "conditionals in filter" do
76
78
  }
77
79
  CONFIG
78
80
 
79
- sample({"foo" => "bar"}) do
81
+ sample_one({"foo" => "bar"}) do
80
82
  expect(subject.get("always")).to eq("awesome")
81
83
  expect(subject.get("hello")).to eq("world")
82
84
  expect(subject.get("fancy")).to be_nil
83
85
  expect(subject.get("free")).to be_nil
84
86
  end
85
87
 
86
- sample({"notfoo" => "bar"}) do
88
+ sample_one({"notfoo" => "bar"}) do
87
89
  expect(subject.get("always")).to eq("awesome")
88
90
  expect(subject.get("hello")).to be_nil
89
91
  expect(subject.get("fancy")).to be_nil
90
92
  expect(subject.get("free")).to eq("hugs")
91
93
  end
92
94
 
93
- sample({"bar" => "baz"}) do
95
+ sample_one({"bar" => "baz"}) do
94
96
  expect(subject.get("always")).to eq("awesome")
95
97
  expect(subject.get("hello")).to be_nil
96
98
  expect(subject.get("fancy")).to eq("pants")
@@ -114,28 +116,28 @@ describe "conditionals in filter" do
114
116
  }
115
117
  CONFIG
116
118
 
117
- sample("foo" => "bar", "nest" => 124) do
119
+ sample_one("foo" => "bar", "nest" => 124) do
118
120
  expect(subject.get("always")).to be_nil
119
121
  expect(subject.get("hello")).to be_nil
120
122
  expect(subject.get("fancy")).to be_nil
121
123
  expect(subject.get("free")).to be_nil
122
124
  end
123
125
 
124
- sample("foo" => "bar", "nest" => 123) do
126
+ sample_one("foo" => "bar", "nest" => 123) do
125
127
  expect(subject.get("always")).to eq("awesome")
126
128
  expect(subject.get("hello")).to eq("world")
127
129
  expect(subject.get("fancy")).to be_nil
128
130
  expect(subject.get("free")).to be_nil
129
131
  end
130
132
 
131
- sample("notfoo" => "bar", "nest" => 123) do
133
+ sample_one("notfoo" => "bar", "nest" => 123) do
132
134
  expect(subject.get("always")).to eq("awesome")
133
135
  expect(subject.get("hello")).to be_nil
134
136
  expect(subject.get("fancy")).to be_nil
135
137
  expect(subject.get("free")).to eq("hugs")
136
138
  end
137
139
 
138
- sample("bar" => "baz", "nest" => 123) do
140
+ sample_one("bar" => "baz", "nest" => 123) do
139
141
  expect(subject.get("always")).to eq("awesome")
140
142
  expect(subject.get("hello")).to be_nil
141
143
  expect(subject.get("fancy")).to eq("pants")
@@ -152,7 +154,7 @@ describe "conditionals in filter" do
152
154
  }
153
155
  CONFIG
154
156
 
155
- sample("foo" => 123, "bar" => 123) do
157
+ sample_one("foo" => 123, "bar" => 123) do
156
158
  expect(subject.get("tags") ).to include("woot")
157
159
  end
158
160
  end
@@ -181,7 +183,7 @@ describe "conditionals in filter" do
181
183
  }
182
184
  CONFIG
183
185
 
184
- sample("foo" => "foo", "foobar" => "foobar", "greeting" => "hello world") do
186
+ sample_one("foo" => "foo", "foobar" => "foobar", "greeting" => "hello world") do
185
187
  expect(subject.get("tags")).to include("field in field")
186
188
  expect(subject.get("tags")).to include("field in string")
187
189
  expect(subject.get("tags")).to include("string in field")
@@ -203,7 +205,7 @@ describe "conditionals in filter" do
203
205
  }
204
206
  CONFIG
205
207
 
206
- sample("foo" => "foo", "somelist" => [ "one", "two" ], "foobar" => "foobar", "greeting" => "hello world", "tags" => [ "fancypantsy" ]) do
208
+ sample_one("foo" => "foo", "somelist" => [ "one", "two" ], "foobar" => "foobar", "greeting" => "hello world", "tags" => [ "fancypantsy" ]) do
207
209
  # verify the original exists
208
210
  expect(subject.get("tags")).to include("fancypantsy")
209
211
 
@@ -218,94 +220,156 @@ describe "conditionals in filter" do
218
220
 
219
221
  describe "operators" do
220
222
  conditional "[message] == 'sample'" do
221
- sample("sample") { expect(subject.get("tags") ).to include("success") }
222
- sample("different") { expect(subject.get("tags") ).to include("failure") }
223
+ sample_one("sample") { expect(subject.get("tags") ).to include("success") }
224
+ sample_one("different") { expect(subject.get("tags") ).to include("failure") }
225
+ end
226
+
227
+ conditional "'sample' == [message]" do
228
+ sample_one("sample") {expect(subject.get("tags")).to include("success")}
229
+ sample_one("different") {expect(subject.get("tags")).to include("failure")}
230
+ end
231
+
232
+ conditional "'value' == 'value'" do
233
+ sample_one("sample") {expect(subject.get("tags")).to include("success")}
234
+ end
235
+
236
+ conditional "'value' == 'other'" do
237
+ sample_one("sample") {expect(subject.get("tags")).to include("failure")}
223
238
  end
224
239
 
225
240
  conditional "[message] != 'sample'" do
226
- sample("sample") { expect(subject.get("tags") ).to include("failure") }
227
- sample("different") { expect(subject.get("tags") ).to include("success") }
241
+ sample_one("sample") { expect(subject.get("tags") ).to include("failure") }
242
+ sample_one("different") { expect(subject.get("tags") ).to include("success") }
228
243
  end
229
244
 
230
245
  conditional "[message] < 'sample'" do
231
- sample("apple") { expect(subject.get("tags") ).to include("success") }
232
- sample("zebra") { expect(subject.get("tags") ).to include("failure") }
246
+ sample_one("apple") { expect(subject.get("tags") ).to include("success") }
247
+ sample_one("zebra") { expect(subject.get("tags") ).to include("failure") }
233
248
  end
234
249
 
235
250
  conditional "[message] > 'sample'" do
236
- sample("zebra") { expect(subject.get("tags") ).to include("success") }
237
- sample("apple") { expect(subject.get("tags") ).to include("failure") }
251
+ sample_one("zebra") { expect(subject.get("tags") ).to include("success") }
252
+ sample_one("apple") { expect(subject.get("tags") ).to include("failure") }
238
253
  end
239
254
 
240
255
  conditional "[message] <= 'sample'" do
241
- sample("apple") { expect(subject.get("tags") ).to include("success") }
242
- sample("zebra") { expect(subject.get("tags") ).to include("failure") }
243
- sample("sample") { expect(subject.get("tags") ).to include("success") }
256
+ sample_one("apple") { expect(subject.get("tags") ).to include("success") }
257
+ sample_one("zebra") { expect(subject.get("tags") ).to include("failure") }
258
+ sample_one("sample") { expect(subject.get("tags") ).to include("success") }
244
259
  end
245
260
 
246
261
  conditional "[message] >= 'sample'" do
247
- sample("zebra") { expect(subject.get("tags") ).to include("success") }
248
- sample("sample") { expect(subject.get("tags") ).to include("success") }
249
- sample("apple") { expect(subject.get("tags") ).to include("failure") }
262
+ sample_one("zebra") { expect(subject.get("tags") ).to include("success") }
263
+ sample_one("sample") { expect(subject.get("tags") ).to include("success") }
264
+ sample_one("apple") { expect(subject.get("tags") ).to include("failure") }
265
+ end
266
+
267
+ conditional "[message] == 5" do
268
+ sample_one("message" => 5) {expect(subject.get("tags")).to include("success")}
269
+ sample_one("message" => 3) {expect(subject.get("tags")).to include("failure")}
270
+ end
271
+
272
+ conditional "5 == [message]" do
273
+ sample_one("message" => 5) {expect(subject.get("tags")).to include("success")}
274
+ sample_one("message" => 3) {expect(subject.get("tags")).to include("failure")}
275
+ end
276
+
277
+ conditional "7 == 7" do
278
+ sample_one("message" => 7) {expect(subject.get("tags")).to include("success")}
279
+ sample_one("message" => 3) {expect(subject.get("tags")).to include("success")}
280
+ end
281
+
282
+ conditional "5 == 7" do
283
+ sample_one("message" => 3) {expect(subject.get("tags")).to include("failure")}
284
+ sample_one("message" => 2) {expect(subject.get("tags")).to include("failure")}
285
+ end
286
+
287
+ conditional "[message] != 5" do
288
+ sample_one("message" => 5) {expect(subject.get("tags")).to include("failure")}
289
+ sample_one("message" => 3) {expect(subject.get("tags")).to include("success")}
290
+ end
291
+
292
+ conditional "[message] < 5" do
293
+ sample_one("message" => 3) {expect(subject.get("tags")).to include("success")}
294
+ sample_one("message" => 5) {expect(subject.get("tags")).to include("failure")}
295
+ sample_one("message" => 9) {expect(subject.get("tags")).to include("failure")}
296
+ end
297
+
298
+ conditional "[message] > 5" do
299
+ sample_one("message" => 9) {expect(subject.get("tags")).to include("success")}
300
+ sample_one("message" => 5) {expect(subject.get("tags")).to include("failure")}
301
+ sample_one("message" => 4) {expect(subject.get("tags")).to include("failure")}
302
+ end
303
+
304
+ conditional "[message] <= 5" do
305
+ sample_one("message" => 9) {expect(subject.get("tags")).to include("failure")}
306
+ sample_one("message" => 5) {expect(subject.get("tags")).to include("success")}
307
+ sample_one("message" => 3) {expect(subject.get("tags")).to include("success")}
308
+ end
309
+
310
+ conditional "[message] >= 5" do
311
+ sample_one("message" => 5) {expect(subject.get("tags")).to include("success")}
312
+ sample_one("message" => 7) {expect(subject.get("tags")).to include("success")}
313
+ sample_one("message" => 3) {expect(subject.get("tags")).to include("failure")}
250
314
  end
251
315
 
252
316
  conditional "[message] =~ /sample/" do
253
- sample("apple") { expect(subject.get("tags") ).to include("failure") }
254
- sample("sample") { expect(subject.get("tags") ).to include("success") }
255
- sample("some sample") { expect(subject.get("tags") ).to include("success") }
317
+ sample_one("apple") { expect(subject.get("tags") ).to include("failure") }
318
+ sample_one("sample") { expect(subject.get("tags") ).to include("success") }
319
+ sample_one("some sample") { expect(subject.get("tags") ).to include("success") }
256
320
  end
257
321
 
258
322
  conditional "[message] !~ /sample/" do
259
- sample("apple") { expect(subject.get("tags")).to include("success") }
260
- sample("sample") { expect(subject.get("tags")).to include("failure") }
261
- sample("some sample") { expect(subject.get("tags")).to include("failure") }
323
+ sample_one("apple") { expect(subject.get("tags")).to include("success") }
324
+ sample_one("sample") { expect(subject.get("tags")).to include("failure") }
325
+ sample_one("some sample") { expect(subject.get("tags")).to include("failure") }
262
326
  end
263
327
 
264
328
  end
265
329
 
266
330
  describe "negated expressions" do
267
331
  conditional "!([message] == 'sample')" do
268
- sample("sample") { expect(subject.get("tags")).not_to include("success") }
269
- sample("different") { expect(subject.get("tags")).not_to include("failure") }
332
+ sample_one("sample") { expect(subject.get("tags")).not_to include("success") }
333
+ sample_one("different") { expect(subject.get("tags")).not_to include("failure") }
270
334
  end
271
335
 
272
336
  conditional "!([message] != 'sample')" do
273
- sample("sample") { expect(subject.get("tags")).not_to include("failure") }
274
- sample("different") { expect(subject.get("tags")).not_to include("success") }
337
+ sample_one("sample") { expect(subject.get("tags")).not_to include("failure") }
338
+ sample_one("different") { expect(subject.get("tags")).not_to include("success") }
275
339
  end
276
340
 
277
341
  conditional "!([message] < 'sample')" do
278
- sample("apple") { expect(subject.get("tags")).not_to include("success") }
279
- sample("zebra") { expect(subject.get("tags")).not_to include("failure") }
342
+ sample_one("apple") { expect(subject.get("tags")).not_to include("success") }
343
+ sample_one("zebra") { expect(subject.get("tags")).not_to include("failure") }
280
344
  end
281
345
 
282
346
  conditional "!([message] > 'sample')" do
283
- sample("zebra") { expect(subject.get("tags")).not_to include("success") }
284
- sample("apple") { expect(subject.get("tags")).not_to include("failure") }
347
+ sample_one("zebra") { expect(subject.get("tags")).not_to include("success") }
348
+ sample_one("apple") { expect(subject.get("tags")).not_to include("failure") }
285
349
  end
286
350
 
287
351
  conditional "!([message] <= 'sample')" do
288
- sample("apple") { expect(subject.get("tags")).not_to include("success") }
289
- sample("zebra") { expect(subject.get("tags")).not_to include("failure") }
290
- sample("sample") { expect(subject.get("tags")).not_to include("success")}
352
+ sample_one("apple") { expect(subject.get("tags")).not_to include("success") }
353
+ sample_one("zebra") { expect(subject.get("tags")).not_to include("failure") }
354
+ sample_one("sample") { expect(subject.get("tags")).not_to include("success")}
291
355
  end
292
356
 
293
357
  conditional "!([message] >= 'sample')" do
294
- sample("zebra") { expect(subject.get("tags")).not_to include("success") }
295
- sample("sample") { expect(subject.get("tags")).not_to include("success") }
296
- sample("apple") { expect(subject.get("tags")).not_to include("failure") }
358
+ sample_one("zebra") { expect(subject.get("tags")).not_to include("success") }
359
+ sample_one("sample") { expect(subject.get("tags")).not_to include("success") }
360
+ sample_one("apple") { expect(subject.get("tags")).not_to include("failure") }
297
361
  end
298
362
 
299
363
  conditional "!([message] =~ /sample/)" do
300
- sample("apple") { expect(subject.get("tags")).not_to include("failure") }
301
- sample("sample") { expect(subject.get("tags")).not_to include("success") }
302
- sample("some sample") { expect(subject.get("tags")).not_to include("success") }
364
+ sample_one("apple") { expect(subject.get("tags")).not_to include("failure") }
365
+ sample_one("sample") { expect(subject.get("tags")).not_to include("success") }
366
+ sample_one("some sample") { expect(subject.get("tags")).not_to include("success") }
303
367
  end
304
368
 
305
369
  conditional "!([message] !~ /sample/)" do
306
- sample("apple") { expect(subject.get("tags")).not_to include("success") }
307
- sample("sample") { expect(subject.get("tags")).not_to include("failure") }
308
- sample("some sample") { expect(subject.get("tags")).not_to include("failure") }
370
+ sample_one("apple") { expect(subject.get("tags")).not_to include("success") }
371
+ sample_one("sample") { expect(subject.get("tags")).not_to include("failure") }
372
+ sample_one("some sample") { expect(subject.get("tags")).not_to include("failure") }
309
373
  end
310
374
 
311
375
  end
@@ -313,66 +377,96 @@ describe "conditionals in filter" do
313
377
  describe "value as an expression" do
314
378
  # testing that a field has a value should be true.
315
379
  conditional "[message]" do
316
- sample("apple") { expect(subject.get("tags")).to include("success") }
317
- sample("sample") { expect(subject.get("tags")).to include("success") }
318
- sample("some sample") { expect(subject.get("tags")).to include("success") }
380
+ sample_one("apple") { expect(subject.get("tags")).to include("success") }
381
+ sample_one("sample") { expect(subject.get("tags")).to include("success") }
382
+ sample_one("some sample") { expect(subject.get("tags")).to include("success") }
319
383
  end
320
384
 
321
385
  # testing that a missing field has a value should be false.
322
386
  conditional "[missing]" do
323
- sample("apple") { expect(subject.get("tags")).to include("failure") }
324
- sample("sample") { expect(subject.get("tags")).to include("failure") }
325
- sample("some sample") { expect(subject.get("tags")).to include("failure") }
387
+ sample_one("apple") { expect(subject.get("tags")).to include("failure") }
388
+ sample_one("sample") { expect(subject.get("tags")).to include("failure") }
389
+ sample_one("some sample") { expect(subject.get("tags")).to include("failure") }
326
390
  end
327
391
  end
328
392
 
329
393
  describe "logic operators" do
330
394
  describe "and" do
331
395
  conditional "[message] and [message]" do
332
- sample("whatever") { expect(subject.get("tags")).to include("success") }
396
+ sample_one("whatever") { expect(subject.get("tags")).to include("success") }
333
397
  end
334
398
  conditional "[message] and ![message]" do
335
- sample("whatever") { expect(subject.get("tags")).to include("failure") }
399
+ sample_one("whatever") { expect(subject.get("tags")).to include("failure") }
336
400
  end
337
401
  conditional "![message] and [message]" do
338
- sample("whatever") { expect(subject.get("tags")).to include("failure") }
402
+ sample_one("whatever") { expect(subject.get("tags")).to include("failure") }
339
403
  end
340
404
  conditional "![message] and ![message]" do
341
- sample("whatever") { expect(subject.get("tags")).to include("failure") }
405
+ sample_one("whatever") { expect(subject.get("tags")).to include("failure") }
406
+ end
407
+ end
408
+
409
+ describe "nand" do
410
+ conditional "[message] nand [message]" do
411
+ sample_one("whatever") { expect(subject.get("tags")).to include("failure") }
412
+ end
413
+ conditional "[message] nand ![message]" do
414
+ sample_one("whatever") { expect(subject.get("tags")).to include("success") }
415
+ end
416
+ conditional "![message] nand [message]" do
417
+ sample_one("whatever") { expect(subject.get("tags")).to include("success") }
418
+ end
419
+ conditional "![message] nand ![message]" do
420
+ sample_one("whatever") { expect(subject.get("tags")).to include("success") }
421
+ end
422
+ end
423
+
424
+ describe "xor" do
425
+ conditional "[message] xor [message]" do
426
+ sample_one("whatever") { expect(subject.get("tags")).to include("failure") }
427
+ end
428
+ conditional "[message] xor ![message]" do
429
+ sample_one("whatever") { expect(subject.get("tags")).to include("success") }
430
+ end
431
+ conditional "![message] xor [message]" do
432
+ sample_one("whatever") { expect(subject.get("tags")).to include("success") }
433
+ end
434
+ conditional "![message] xor ![message]" do
435
+ sample_one("whatever") { expect(subject.get("tags")).to include("failure") }
342
436
  end
343
437
  end
344
438
 
345
439
  describe "or" do
346
440
  conditional "[message] or [message]" do
347
- sample("whatever") { expect(subject.get("tags")).to include("success") }
441
+ sample_one("whatever") { expect(subject.get("tags")).to include("success") }
348
442
  end
349
443
  conditional "[message] or ![message]" do
350
- sample("whatever") { expect(subject.get("tags")).to include("success") }
444
+ sample_one("whatever") { expect(subject.get("tags")).to include("success") }
351
445
  end
352
446
  conditional "![message] or [message]" do
353
- sample("whatever") { expect(subject.get("tags")).to include("success") }
447
+ sample_one("whatever") { expect(subject.get("tags")).to include("success") }
354
448
  end
355
449
  conditional "![message] or ![message]" do
356
- sample("whatever") { expect(subject.get("tags")).to include("failure") }
450
+ sample_one("whatever") { expect(subject.get("tags")).to include("failure") }
357
451
  end
358
452
  end
359
453
  end
360
454
 
361
455
  describe "field references" do
362
456
  conditional "[field with space]" do
363
- sample("field with space" => "hurray") do
457
+ sample_one("field with space" => "hurray") do
364
458
  expect(subject.get("tags")).to include("success")
365
459
  end
366
460
  end
367
461
 
368
462
  conditional "[field with space] == 'hurray'" do
369
- sample("field with space" => "hurray") do
463
+ sample_one("field with space" => "hurray") do
370
464
  expect(subject.get("tags")).to include("success")
371
465
  end
372
466
  end
373
467
 
374
468
  conditional "[nested field][reference with][some spaces] == 'hurray'" do
375
- sample({"nested field" => { "reference with" => { "some spaces" => "hurray" } } }) do
469
+ sample_one({"nested field" => { "reference with" => { "some spaces" => "hurray" } } }) do
376
470
  expect(subject.get("tags")).to include("success")
377
471
  end
378
472
  end
@@ -394,15 +488,16 @@ describe "conditionals in filter" do
394
488
  }
395
489
  CONFIG
396
490
 
397
- sample({"type" => "original"}) do
491
+ sample_one({"type" => "original"}) do
398
492
  expect(subject).to be_an(Array)
399
493
  expect(subject.length).to eq(2)
494
+ subject.sort! {|a, b| a.get("type") <=> b.get("type")}
400
495
 
401
- expect(subject[0].get("type")).to eq("original")
402
- expect(subject[0].get("cond1")).to eq("true")
403
- expect(subject[0].get("cond2")).to eq(nil)
496
+ expect(subject[1].get("type")).to eq("original")
497
+ expect(subject[1].get("cond1")).to eq("true")
498
+ expect(subject[1].get("cond2")).to eq(nil)
404
499
 
405
- expect(subject[1].get("type")).to eq("clone")
500
+ expect(subject[0].get("type")).to eq("clone")
406
501
  # expect(subject[1].get("cond1")).to eq(nil)
407
502
  # expect(subject[1].get("cond2")).to eq("true")
408
503
  end
@@ -424,18 +519,78 @@ describe "conditionals in filter" do
424
519
  }
425
520
  CONFIG
426
521
 
427
- sample({"type" => "original"}) do
428
- # puts subject.inspect
429
- expect(subject[0].get("cond1")).to eq(nil)
522
+ sample_one({"type" => "original"}) do
523
+ expect(subject.length).to eq(3)
524
+ subject.sort! {|a, b| a.get("type") <=> b.get("type")}
525
+
526
+ expect(subject[0].get("type")).to eq("clone1")
527
+ expect(subject[0].get("cond1")).to eq("true")
430
528
  expect(subject[0].get("cond2")).to eq(nil)
431
529
 
432
- expect(subject[1].get("type")).to eq("clone1")
433
- expect(subject[1].get("cond1")).to eq("true")
434
- expect(subject[1].get("cond2")).to eq(nil)
530
+ expect(subject[1].get("type")).to eq("clone2")
531
+ expect(subject[1].get("cond1")).to eq(nil)
532
+ expect(subject[1].get("cond2")).to eq("true")
435
533
 
436
- expect(subject[2].get("type")).to eq("clone2")
534
+ expect(subject[2].get("type")).to eq("original")
437
535
  expect(subject[2].get("cond1")).to eq(nil)
438
- expect(subject[2].get("cond2")).to eq("true")
536
+ expect(subject[2].get("cond2")).to eq(nil)
537
+ end
538
+ end
539
+
540
+ describe "complex case" do
541
+ config <<-CONFIG
542
+ filter {
543
+ if ("foo" in [tags]) {
544
+ mutate { id => addbar add_tag => bar }
545
+
546
+ if ("bar" in [tags]) {
547
+ mutate { id => addbaz add_tag => baz }
548
+ }
549
+
550
+ if ("baz" in [tags]) {
551
+ mutate { id => addbot add_tag => bot }
552
+
553
+ if ("bot" in [tags]) {
554
+ mutate { id => addbonk add_tag => bonk }
555
+ }
556
+ }
557
+ }
558
+
559
+ if ("bot" in [tags]) {
560
+ mutate { id => addwat add_tag => wat }
561
+ }
562
+
563
+ mutate { id => addprev add_tag => prev }
564
+
565
+ mutate { id => addfinal add_tag => final }
566
+
567
+ }
568
+ CONFIG
569
+
570
+ sample_one("tags" => ["bot"]) do
571
+ tags = subject.get("tags")
572
+ expect(tags[0]).to eq("bot")
573
+ expect(tags[1]).to eq("wat")
574
+ expect(tags[2]).to eq("prev")
575
+ expect(tags[3]).to eq("final")
576
+ end
577
+
578
+ sample_one("tags" => ["foo"]) do
579
+ tags = subject.get("tags")
580
+ expect(tags[0]).to eq("foo")
581
+ expect(tags[1]).to eq("bar")
582
+ expect(tags[2]).to eq("baz")
583
+ expect(tags[3]).to eq("bot")
584
+ expect(tags[4]).to eq("bonk")
585
+ expect(tags[5]).to eq("wat")
586
+ expect(tags[6]).to eq("prev")
587
+ expect(tags[7]).to eq("final")
588
+ end
589
+
590
+ sample_one("type" => "original") do
591
+ tags = subject.get("tags")
592
+ expect(tags[0]).to eq("prev")
593
+ expect(tags[1]).to eq("final")
439
594
  end
440
595
  end
441
596
  end