logstash-core 5.0.0.alpha4.snapshot3-java → 5.0.0.alpha5.snapshot1-java

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of logstash-core might be problematic. Click here for more details.

Files changed (48) hide show
  1. checksums.yaml +4 -4
  2. data/lib/logstash/agent.rb +6 -2
  3. data/lib/logstash/api/app_helpers.rb +15 -0
  4. data/lib/logstash/api/command_factory.rb +3 -1
  5. data/lib/logstash/api/commands/base.rb +3 -5
  6. data/lib/logstash/api/commands/default_metadata.rb +27 -0
  7. data/lib/logstash/api/commands/hot_threads_reporter.rb +61 -0
  8. data/lib/logstash/api/commands/node.rb +9 -63
  9. data/lib/logstash/api/commands/stats.rb +5 -61
  10. data/lib/logstash/api/commands/system/basicinfo_command.rb +3 -6
  11. data/lib/logstash/api/modules/base.rb +3 -1
  12. data/lib/logstash/api/modules/node.rb +8 -18
  13. data/lib/logstash/api/modules/node_stats.rb +5 -41
  14. data/lib/logstash/api/modules/stats.rb +13 -33
  15. data/lib/logstash/build.rb +6 -0
  16. data/lib/logstash/environment.rb +9 -0
  17. data/lib/logstash/filter_delegator.rb +1 -1
  18. data/lib/logstash/instrument/metric.rb +7 -6
  19. data/lib/logstash/instrument/metric_type/base.rb +1 -4
  20. data/lib/logstash/instrument/namespaced_metric.rb +1 -1
  21. data/lib/logstash/instrument/null_metric.rb +6 -1
  22. data/lib/logstash/output_delegator.rb +2 -0
  23. data/lib/logstash/pipeline.rb +62 -93
  24. data/lib/logstash/pipeline_reporter.rb +14 -13
  25. data/lib/logstash/plugin.rb +8 -2
  26. data/lib/logstash/runner.rb +7 -1
  27. data/lib/logstash/settings.rb +17 -7
  28. data/lib/logstash/util/wrapped_synchronous_queue.rb +220 -0
  29. data/lib/logstash/version.rb +1 -1
  30. data/lib/logstash/webserver.rb +4 -0
  31. data/lib/logstash-core/version.rb +1 -1
  32. data/locales/en.yml +4 -0
  33. data/logstash-core.gemspec +2 -2
  34. data/spec/api/lib/api/node_spec.rb +0 -1
  35. data/spec/api/lib/api/node_stats_spec.rb +36 -34
  36. data/spec/api/lib/api/support/resource_dsl_methods.rb +15 -0
  37. data/spec/api/spec_helper.rb +5 -2
  38. data/spec/logstash/inputs/metrics_spec.rb +1 -1
  39. data/spec/logstash/instrument/metric_type/counter_spec.rb +1 -6
  40. data/spec/logstash/instrument/metric_type/gauge_spec.rb +1 -4
  41. data/spec/logstash/instrument/namespaced_metric_spec.rb +61 -2
  42. data/spec/logstash/instrument/null_metric_spec.rb +7 -9
  43. data/spec/logstash/pipeline_spec.rb +7 -7
  44. data/spec/logstash/plugin_spec.rb +73 -0
  45. data/spec/logstash/settings/string_spec.rb +21 -0
  46. data/spec/logstash/util/wrapped_synchronous_queue_spec.rb +70 -22
  47. data/spec/support/shared_examples.rb +98 -0
  48. metadata +11 -4
@@ -17,10 +17,7 @@ module LogStash module Instrument module MetricType
17
17
 
18
18
  def to_hash
19
19
  {
20
- "namespaces" => namespaces,
21
- "key" => key,
22
- "type" => type,
23
- "value" => value
20
+ key => value
24
21
  }
25
22
  end
26
23
 
@@ -24,7 +24,7 @@ module LogStash module Instrument
24
24
  @metric.increment(namespace_name, key, value)
25
25
  end
26
26
 
27
- def decrement(namespace, key, value = 1)
27
+ def decrement(key, value = 1)
28
28
  @metric.decrement(namespace_name, key, value)
29
29
  end
30
30
 
@@ -9,20 +9,25 @@ module LogStash module Instrument
9
9
  attr_reader :namespace_name, :collector
10
10
 
11
11
  def increment(key, value = 1)
12
+ Metric.validate_key!(key)
12
13
  end
13
14
 
14
- def decrement(namespace, key, value = 1)
15
+ def decrement(key, value = 1)
16
+ Metric.validate_key!(key)
15
17
  end
16
18
 
17
19
  def gauge(key, value)
20
+ Metric.validate_key!(key)
18
21
  end
19
22
 
20
23
  def report_time(key, duration)
24
+ Metric.validate_key!(key)
21
25
  end
22
26
 
23
27
  # We have to manually redefine this method since it can return an
24
28
  # object this object also has to be implemented as a NullObject
25
29
  def time(key)
30
+ Metric.validate_key!(key)
26
31
  if block_given?
27
32
  yield
28
33
  else
@@ -27,6 +27,8 @@ module LogStash class OutputDelegator
27
27
 
28
28
  # Scope the metrics to the plugin
29
29
  namespaced_metric = metric.namespace(output.plugin_unique_name.to_sym)
30
+ output.metric = namespaced_metric
31
+
30
32
  @metric_events = namespaced_metric.namespace(:events)
31
33
  namespaced_metric.gauge(:name, config_name)
32
34
 
@@ -33,7 +33,9 @@ module LogStash; class Pipeline
33
33
  :thread,
34
34
  :config_str,
35
35
  :settings,
36
- :metric
36
+ :metric,
37
+ :filter_queue_client,
38
+ :input_queue_client
37
39
 
38
40
  MAX_INFLIGHT_WARN_THRESHOLD = 10_000
39
41
 
@@ -82,14 +84,18 @@ module LogStash; class Pipeline
82
84
  raise
83
85
  end
84
86
 
85
- @input_queue = LogStash::Util::WrappedSynchronousQueue.new
87
+ queue = LogStash::Util::WrappedSynchronousQueue.new
88
+ @input_queue_client = queue.write_client
89
+ @filter_queue_client = queue.read_client
90
+ # Note that @infilght_batches as a central mechanism for tracking inflight
91
+ # batches will fail if we have multiple read clients here.
92
+ @filter_queue_client.set_events_metric(metric.namespace([:stats, :events]))
93
+ @filter_queue_client.set_pipeline_metric(
94
+ metric.namespace([:stats, :pipelines, pipeline_id.to_s.to_sym, :events])
95
+ )
86
96
  @events_filtered = Concurrent::AtomicFixnum.new(0)
87
97
  @events_consumed = Concurrent::AtomicFixnum.new(0)
88
98
 
89
- # We generally only want one thread at a time able to access pop/take/poll operations
90
- # from this queue. We also depend on this to be able to block consumers while we snapshot
91
- # in-flight buffers
92
- @input_queue_pop_mutex = Mutex.new
93
99
  @input_threads = []
94
100
  # @ready requires thread safety since it is typically polled from outside the pipeline thread
95
101
  @ready = Concurrent::AtomicBoolean.new(false)
@@ -176,8 +182,6 @@ module LogStash; class Pipeline
176
182
  end
177
183
 
178
184
  def start_workers
179
- @inflight_batches = {}
180
-
181
185
  @worker_threads.clear # In case we're restarting the pipeline
182
186
  begin
183
187
  start_inputs
@@ -187,13 +191,14 @@ module LogStash; class Pipeline
187
191
  pipeline_workers = safe_pipeline_worker_count
188
192
  batch_size = @settings.get("pipeline.batch.size")
189
193
  batch_delay = @settings.get("pipeline.batch.delay")
194
+
190
195
  max_inflight = batch_size * pipeline_workers
191
196
 
192
- config_metric = metric.namespace([:stats, :pipelines, pipeline_id.to_s.to_sym, :config])
197
+ config_metric = metric.namespace([:stats, :pipelines, pipeline_id.to_s.to_sym, :config])
193
198
  config_metric.gauge(:workers, pipeline_workers)
194
199
  config_metric.gauge(:batch_size, batch_size)
195
200
  config_metric.gauge(:batch_delay, batch_delay)
196
-
201
+
197
202
  @logger.info("Starting pipeline",
198
203
  "id" => self.pipeline_id,
199
204
  "pipeline.workers" => pipeline_workers,
@@ -211,7 +216,7 @@ module LogStash; class Pipeline
211
216
  end
212
217
  end
213
218
  ensure
214
- # it is important to garantee @ready to be true after the startup sequence has been completed
219
+ # it is important to guarantee @ready to be true after the startup sequence has been completed
215
220
  # to potentially unblock the shutdown method which may be waiting on @ready to proceed
216
221
  @ready.make_true
217
222
  end
@@ -222,73 +227,39 @@ module LogStash; class Pipeline
222
227
  def worker_loop(batch_size, batch_delay)
223
228
  running = true
224
229
 
225
- namespace_events = metric.namespace([:stats, :events])
226
- namespace_pipeline = metric.namespace([:stats, :pipelines, pipeline_id.to_s.to_sym, :events])
230
+ @filter_queue_client.set_batch_dimensions(batch_size, batch_delay)
227
231
 
228
232
  while running
229
- # To understand the purpose behind this synchronize please read the body of take_batch
230
- input_batch, signal = @input_queue_pop_mutex.synchronize { take_batch(batch_size, batch_delay) }
231
- running = false if signal == LogStash::SHUTDOWN
232
-
233
- @events_consumed.increment(input_batch.size)
234
- namespace_events.increment(:in, input_batch.size)
235
- namespace_pipeline.increment(:in, input_batch.size)
233
+ batch = @filter_queue_client.take_batch
234
+ @events_consumed.increment(batch.size)
235
+ running = false if batch.shutdown_signal_received?
236
+ filter_batch(batch)
236
237
 
237
- filtered_batch = filter_batch(input_batch)
238
-
239
- if signal # Flush on SHUTDOWN or FLUSH
240
- flush_options = (signal == LogStash::SHUTDOWN) ? {:final => true} : {}
241
- flush_filters_to_batch(filtered_batch, flush_options)
238
+ if batch.shutdown_signal_received? || batch.flush_signal_received?
239
+ flush_filters_to_batch(batch)
242
240
  end
243
241
 
244
- @events_filtered.increment(filtered_batch.size)
245
-
246
- namespace_events.increment(:filtered, filtered_batch.size)
247
- namespace_pipeline.increment(:filtered, filtered_batch.size)
248
-
249
- output_batch(filtered_batch)
250
-
251
- namespace_events.increment(:out, filtered_batch.size)
252
- namespace_pipeline.increment(:out, filtered_batch.size)
253
-
254
- inflight_batches_synchronize { set_current_thread_inflight_batch(nil) }
242
+ output_batch(batch)
243
+ @filter_queue_client.close_batch(batch)
255
244
  end
256
245
  end
257
246
 
258
- def take_batch(batch_size, batch_delay)
259
- batch = []
260
- # Since this is externally synchronized in `worker_look` wec can guarantee that the visibility of an insight batch
261
- # guaranteed to be a full batch not a partial batch
262
- set_current_thread_inflight_batch(batch)
263
-
264
- signal = false
265
- batch_size.times do |t|
266
- event = (t == 0) ? @input_queue.take : @input_queue.poll(batch_delay)
267
-
268
- if event.nil?
269
- next
270
- elsif event == LogStash::SHUTDOWN || event == LogStash::FLUSH
271
- # We MUST break here. If a batch consumes two SHUTDOWN events
272
- # then another worker may have its SHUTDOWN 'stolen', thus blocking
273
- # the pipeline. We should stop doing work after flush as well.
274
- signal = event
275
- break
276
- else
277
- batch << event
278
- end
279
- end
280
-
281
- [batch, signal]
282
- end
283
-
284
247
  def filter_batch(batch)
285
- batch.reduce([]) do |acc,e|
286
- if e.is_a?(LogStash::Event)
287
- filtered = filter_func(e)
288
- filtered.each {|fe| acc << fe unless fe.cancelled?}
248
+ batch.each do |event|
249
+ if event.is_a?(LogStash::Event)
250
+ filtered = filter_func(event)
251
+ filtered.each do |e|
252
+ #these are both original and generated events
253
+ if e.cancelled?
254
+ batch.cancel(e)
255
+ else
256
+ batch.merge(e)
257
+ end
258
+ end
289
259
  end
290
- acc
291
260
  end
261
+ @filter_queue_client.add_filtered_metrics(batch)
262
+ @events_filtered.increment(batch.size)
292
263
  rescue Exception => e
293
264
  # Plugins authors should manage their own exceptions in the plugin code
294
265
  # but if an exception is raised up to the worker thread they are considered
@@ -304,31 +275,21 @@ module LogStash; class Pipeline
304
275
  # Take an array of events and send them to the correct output
305
276
  def output_batch(batch)
306
277
  # Build a mapping of { output_plugin => [events...]}
307
- outputs_events = batch.reduce(Hash.new { |h, k| h[k] = [] }) do |acc, event|
278
+ output_events_map = Hash.new { |h, k| h[k] = [] }
279
+ batch.each do |event|
308
280
  # We ask the AST to tell us which outputs to send each event to
309
281
  # Then, we stick it in the correct bin
310
282
 
311
283
  # output_func should never return anything other than an Array but we have lots of legacy specs
312
284
  # that monkeypatch it and return nil. We can deprecate "|| []" after fixing these specs
313
- outputs_for_event = output_func(event) || []
314
-
315
- outputs_for_event.each { |output| acc[output] << event }
316
- acc
285
+ (output_func(event) || []).each do |output|
286
+ output_events_map[output].push(event)
287
+ end
317
288
  end
318
-
319
289
  # Now that we have our output to event mapping we can just invoke each output
320
290
  # once with its list of events
321
- outputs_events.each { |output, events| output.multi_receive(events) }
322
- end
323
-
324
- def set_current_thread_inflight_batch(batch)
325
- @inflight_batches[Thread.current] = batch
326
- end
327
-
328
- def inflight_batches_synchronize
329
- @input_queue_pop_mutex.synchronize do
330
- yield(@inflight_batches)
331
- end
291
+ output_events_map.each { |output, events| output.multi_receive(events) }
292
+ @filter_queue_client.add_output_metrics(batch)
332
293
  end
333
294
 
334
295
  def wait_inputs
@@ -359,7 +320,7 @@ module LogStash; class Pipeline
359
320
  def inputworker(plugin)
360
321
  LogStash::Util::set_thread_name("[#{pipeline_id}]<#{plugin.class.config_name}")
361
322
  begin
362
- plugin.run(@input_queue)
323
+ plugin.run(@input_queue_client)
363
324
  rescue => e
364
325
  if plugin.stop?
365
326
  @logger.debug("Input plugin raised exception during shutdown, ignoring it.",
@@ -413,7 +374,7 @@ module LogStash; class Pipeline
413
374
  # Each worker thread will receive this exactly once!
414
375
  @worker_threads.each do |t|
415
376
  @logger.debug("Pushing shutdown", :thread => t.inspect)
416
- @input_queue.push(LogStash::SHUTDOWN)
377
+ @input_queue_client.push(LogStash::SHUTDOWN)
417
378
  end
418
379
 
419
380
  @worker_threads.each do |t|
@@ -437,7 +398,11 @@ module LogStash; class Pipeline
437
398
  elsif plugin_type == "filter"
438
399
  LogStash::FilterDelegator.new(@logger, klass, pipeline_scoped_metric.namespace(:filters), *args)
439
400
  else
440
- klass.new(*args)
401
+ new_plugin = klass.new(*args)
402
+ inputs_metric = pipeline_scoped_metric.namespace(:inputs)
403
+ namespaced_metric = inputs_metric.namespace(new_plugin.plugin_unique_name.to_sym)
404
+ new_plugin.metric = namespaced_metric
405
+ new_plugin
441
406
  end
442
407
  end
443
408
 
@@ -449,7 +414,7 @@ module LogStash; class Pipeline
449
414
  end
450
415
 
451
416
 
452
- # perform filters flush and yeild flushed event to the passed block
417
+ # perform filters flush and yield flushed event to the passed block
453
418
  # @param options [Hash]
454
419
  # @option options [Boolean] :final => true to signal a final shutdown flush
455
420
  def flush_filters(options = {}, &block)
@@ -479,7 +444,7 @@ module LogStash; class Pipeline
479
444
  def flush
480
445
  if @flushing.compare_and_set(false, true)
481
446
  @logger.debug? && @logger.debug("Pushing flush onto pipeline")
482
- @input_queue.push(LogStash::FLUSH)
447
+ @input_queue_client.push(LogStash::FLUSH)
483
448
  end
484
449
  end
485
450
 
@@ -493,18 +458,22 @@ module LogStash; class Pipeline
493
458
  end
494
459
 
495
460
  # perform filters flush into the output queue
461
+ #
462
+ # @param batch [ReadClient::ReadBatch]
496
463
  # @param options [Hash]
497
- # @option options [Boolean] :final => true to signal a final shutdown flush
498
464
  def flush_filters_to_batch(batch, options = {})
465
+ options[:final] = batch.shutdown_signal_received?
499
466
  flush_filters(options) do |event|
500
- unless event.cancelled?
467
+ if event.cancelled?
468
+ batch.cancel(event)
469
+ else
501
470
  @logger.debug? and @logger.debug("Pushing flushed events", :event => event)
502
- batch << event
471
+ batch.merge(event)
503
472
  end
504
473
  end
505
474
 
506
475
  @flushing.set(false)
507
- end # flush_filters_to_output!
476
+ end # flush_filters_to_batch
508
477
 
509
478
  def plugin_threads_info
510
479
  input_threads = @input_threads.select {|t| t.alive? }
@@ -39,7 +39,7 @@ module LogStash; class PipelineReporter
39
39
  end
40
40
  end
41
41
 
42
- def initialize(logger,pipeline)
42
+ def initialize(logger, pipeline)
43
43
  @logger = logger
44
44
  @pipeline = pipeline
45
45
  end
@@ -52,7 +52,8 @@ module LogStash; class PipelineReporter
52
52
  end
53
53
 
54
54
  def to_hash
55
- pipeline.inflight_batches_synchronize do |batch_map|
55
+ # pipeline.filter_queue_client.inflight_batches is synchronized
56
+ pipeline.filter_queue_client.inflight_batches do |batch_map|
56
57
  worker_states_snap = worker_states(batch_map) # We only want to run this once
57
58
  inflight_count = worker_states_snap.map {|s| s[:inflight_count] }.reduce(0, :+)
58
59
 
@@ -83,17 +84,17 @@ module LogStash; class PipelineReporter
83
84
  pipeline.plugin_threads
84
85
  end
85
86
 
86
- # Not threadsafe! must be called within an `inflight_batches_synchronize` block
87
+ # Not threadsafe! ensure synchronization
87
88
  def worker_states(batch_map)
88
- pipeline.worker_threads.map.with_index do |thread,idx|
89
- status = thread.status || "dead"
90
- inflight_count = batch_map[thread] ? batch_map[thread].size : 0
91
- {
92
- :status => status,
93
- :alive => thread.alive?,
94
- :index => idx,
95
- :inflight_count => inflight_count
96
- }
89
+ pipeline.worker_threads.map.with_index do |thread, idx|
90
+ status = thread.status || "dead"
91
+ inflight_count = batch_map[thread] ? batch_map[thread].size : 0
92
+ {
93
+ :status => status,
94
+ :alive => thread.alive?,
95
+ :index => idx,
96
+ :inflight_count => inflight_count
97
+ }
97
98
  end
98
99
  end
99
100
 
@@ -111,4 +112,4 @@ module LogStash; class PipelineReporter
111
112
  }
112
113
  end
113
114
  end
114
- end end
115
+ end end
@@ -105,9 +105,15 @@ class LogStash::Plugin
105
105
  end
106
106
 
107
107
  def metric
108
- @metric_plugin ||= enable_metric ? @metric : LogStash::Instrument::NullMetric.new
108
+ # We can disable metric per plugin if we want in the configuration
109
+ # we will use the NullMetric in this case.
110
+ @metric_plugin ||= if @enable_metric
111
+ # Fallback when testing plugin and no metric collector are correctly configured.
112
+ @metric.nil? ? LogStash::Instrument::NullMetric.new : @metric
113
+ else
114
+ LogStash::Instrument::NullMetric.new
115
+ end
109
116
  end
110
-
111
117
  # return the configured name of this plugin
112
118
  # @return [String] The name of the plugin defined by `config_name`
113
119
  def config_name
@@ -16,6 +16,7 @@ require "logstash/config/defaults"
16
16
  require "logstash/shutdown_watcher"
17
17
  require "logstash/patches/clamp"
18
18
  require "logstash/settings"
19
+ require "logstash/version"
19
20
 
20
21
  class LogStash::Runner < Clamp::StrictCommand
21
22
  # The `path.settings` need to be defined in the runner instead of the `logstash-core/lib/logstash/environment.rb`
@@ -63,6 +64,12 @@ class LogStash::Runner < Clamp::StrictCommand
63
64
  :attribute_name => "pipeline.unsafe_shutdown",
64
65
  :default => LogStash::SETTINGS.get_default("pipeline.unsafe_shutdown")
65
66
 
67
+ # Data Path Setting
68
+ option ["--path.data"] , "PATH",
69
+ I18n.t("logstash.runner.flag.datapath"),
70
+ :attribute_name => "path.data",
71
+ :default => LogStash::SETTINGS.get_default("path.data")
72
+
66
73
  # Plugins Settings
67
74
  option ["-p", "--path.plugins"] , "PATH",
68
75
  I18n.t("logstash.runner.flag.pluginpath"),
@@ -256,7 +263,6 @@ class LogStash::Runner < Clamp::StrictCommand
256
263
  end # def show_version
257
264
 
258
265
  def show_version_logstash
259
- require "logstash/version"
260
266
  puts "logstash #{LOGSTASH_VERSION}"
261
267
  end # def show_version_logstash
262
268
 
@@ -210,12 +210,6 @@ module LogStash
210
210
  end
211
211
  end
212
212
 
213
- class String < Setting
214
- def initialize(name, default=nil, strict=true)
215
- super(name, ::String, default, strict)
216
- end
217
- end
218
-
219
213
  class Numeric < Setting
220
214
  def initialize(name, default=nil, strict=true)
221
215
  super(name, ::Numeric, default, strict)
@@ -241,11 +235,15 @@ module LogStash
241
235
 
242
236
  class String < Setting
243
237
  def initialize(name, default=nil, strict=true, possible_strings=[])
238
+ @possible_strings = possible_strings
244
239
  super(name, ::String, default, strict)
245
240
  end
246
241
 
247
242
  def validate(value)
248
- super(value) && possible_strings.include?(value)
243
+ super(value)
244
+ unless @possible_strings.empty? || @possible_strings.include?(value)
245
+ raise ArgumentError.new("invalid value \"#{value}\". Options are: #{@possible_strings.inspect}")
246
+ end
249
247
  end
250
248
  end
251
249
 
@@ -261,6 +259,18 @@ module LogStash
261
259
  end
262
260
  end
263
261
 
262
+ class WritableDirectory < Setting
263
+ def initialize(name, default=nil, strict=true)
264
+ super(name, ::String, default, strict) do |path|
265
+ if ::File.directory?(path) && ::File.writable?(path)
266
+ true
267
+ else
268
+ raise ::ArgumentError.new("Path \"#{path}\" is not a directory or not writable.")
269
+ end
270
+ end
271
+ end
272
+ end
273
+
264
274
  end
265
275
 
266
276
  SETTINGS = Settings.new