logstash-core 6.0.1-java → 6.1.0-java

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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