logstash-core 6.0.0.beta1-java → 6.0.0.beta2-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 (44) hide show
  1. checksums.yaml +4 -4
  2. data/lib/logstash-core/logstash-core.jar +0 -0
  3. data/lib/logstash-core/version.rb +1 -1
  4. data/lib/logstash/agent.rb +0 -16
  5. data/lib/logstash/compiler/lscl.rb +2 -53
  6. data/lib/logstash/compiler/lscl/helpers.rb +55 -0
  7. data/lib/logstash/config/config_ast.rb +6 -3
  8. data/lib/logstash/config/modules_common.rb +4 -1
  9. data/lib/logstash/elasticsearch_client.rb +4 -1
  10. data/lib/logstash/environment.rb +8 -2
  11. data/lib/logstash/filter_delegator.rb +11 -6
  12. data/lib/logstash/instrument/collector.rb +7 -5
  13. data/lib/logstash/instrument/metric_store.rb +6 -9
  14. data/lib/logstash/instrument/namespaced_metric.rb +4 -0
  15. data/lib/logstash/instrument/namespaced_null_metric.rb +4 -0
  16. data/lib/logstash/instrument/null_metric.rb +10 -0
  17. data/lib/logstash/instrument/wrapped_write_client.rb +33 -24
  18. data/lib/logstash/modules/kibana_client.rb +5 -3
  19. data/lib/logstash/modules/kibana_config.rb +1 -4
  20. data/lib/logstash/modules/scaffold.rb +2 -0
  21. data/lib/logstash/modules/settings_merger.rb +52 -4
  22. data/lib/logstash/output_delegator.rb +7 -5
  23. data/lib/logstash/pipeline.rb +37 -14
  24. data/lib/logstash/pipeline_settings.rb +2 -0
  25. data/lib/logstash/runner.rb +14 -2
  26. data/lib/logstash/settings.rb +26 -0
  27. data/lib/logstash/util/cloud_setting_auth.rb +29 -0
  28. data/lib/logstash/util/cloud_setting_id.rb +41 -0
  29. data/lib/logstash/util/modules_setting_array.rb +28 -0
  30. data/lib/logstash/util/wrapped_acked_queue.rb +5 -6
  31. data/lib/logstash/util/wrapped_synchronous_queue.rb +14 -9
  32. data/lib/logstash/version.rb +1 -1
  33. data/locales/en.yml +16 -0
  34. data/spec/logstash/agent/converge_spec.rb +6 -7
  35. data/spec/logstash/config/source/multi_local_spec.rb +11 -0
  36. data/spec/logstash/filter_delegator_spec.rb +20 -8
  37. data/spec/logstash/legacy_ruby_event_spec.rb +4 -4
  38. data/spec/logstash/modules/scaffold_spec.rb +2 -7
  39. data/spec/logstash/modules/settings_merger_spec.rb +111 -0
  40. data/spec/logstash/output_delegator_spec.rb +15 -5
  41. data/spec/logstash/pipeline_spec.rb +39 -7
  42. data/spec/logstash/runner_spec.rb +4 -1
  43. data/spec/logstash/settings/modules_spec.rb +115 -0
  44. metadata +10 -2
@@ -50,12 +50,14 @@ module LogStash module Modules class KibanaClient
50
50
 
51
51
  @client = Manticore::Client.new(client_options)
52
52
  @host = @settings.fetch("var.kibana.host", "localhost:5601")
53
- username = @settings["var.kibana.username"]
54
- password = @settings["var.kibana.password"]
55
-
56
53
  @scheme = @settings.fetch("var.kibana.scheme", "http")
57
54
  @http_options = {:headers => {'Content-Type' => 'application/json'}}
55
+ username = @settings["var.kibana.username"]
58
56
  if username
57
+ password = @settings["var.kibana.password"]
58
+ if password.is_a?(LogStash::Util::Password)
59
+ password = password.value
60
+ end
59
61
  @http_options[:headers]['Authorization'] = 'Basic ' + Base64.encode64( "#{username}:#{password}" ).chomp
60
62
  end
61
63
 
@@ -11,7 +11,6 @@ module LogStash module Modules class KibanaConfig
11
11
  include LogStash::Util::Loggable
12
12
 
13
13
  ALLOWED_DIRECTORIES = ["search", "visualization"]
14
- METRICS_MAX_BUCKETS = (24 * 60 * 60).freeze # 24 hours of events/sec buckets.
15
14
  attr_reader :index_name # not used when importing via kibana but for BWC with ElastsearchConfig
16
15
 
17
16
  # We name it `modul` here because `module` has meaning in Ruby.
@@ -21,10 +20,8 @@ module LogStash module Modules class KibanaConfig
21
20
  @settings = settings
22
21
  @index_name = "kibana"
23
22
  @pattern_name = "#{@name}-*"
24
- @metrics_max_buckets = @settings.fetch("dashboards.metrics_max_buckets", METRICS_MAX_BUCKETS).to_i
25
23
  @kibana_settings = [
26
- KibanaSettings::Setting.new("defaultIndex", @pattern_name),
27
- KibanaSettings::Setting.new("metrics:max_buckets", @metrics_max_buckets)
24
+ KibanaSettings::Setting.new("defaultIndex", @pattern_name)
28
25
  ]
29
26
  end
30
27
 
@@ -1,6 +1,7 @@
1
1
  # encoding: utf-8
2
2
  require "logstash/namespace"
3
3
  require "logstash/logging"
4
+ require "logstash/util/loggable"
4
5
  require "erb"
5
6
 
6
7
  require_relative "elasticsearch_config"
@@ -17,6 +18,7 @@ module LogStash module Modules class Scaffold
17
18
  @module_name = name
18
19
  @directory = directory # this is the 'configuration folder in the GEM root.'
19
20
  @kibana_version_parts = "6.0.0".split('.') # this is backup in case kibana client fails to connect
21
+ logger.info("Initializing module", :module_name => name, :directory => directory)
20
22
  end
21
23
 
22
24
  def add_kibana_version(version_parts)
@@ -1,8 +1,13 @@
1
1
  # encoding: utf-8
2
2
  require "logstash/namespace"
3
+ require "logstash/util"
4
+ require "logstash/util/loggable"
3
5
 
4
- module LogStash module Modules class SettingsMerger
5
- def self.merge(cli_settings, yml_settings)
6
+ module LogStash module Modules module SettingsMerger
7
+ include LogStash::Util::Loggable
8
+ extend self
9
+
10
+ def merge(cli_settings, yml_settings)
6
11
  # both args are arrays of hashes, e.g.
7
12
  # [{"name"=>"mod1", "var.input.tcp.port"=>"3333"}, {"name"=>"mod2"}]
8
13
  # [{"name"=>"mod1", "var.input.tcp.port"=>2222, "var.kibana.username"=>"rupert", "var.kibana.password"=>"fotherington"}, {"name"=>"mod3", "var.input.tcp.port"=>4445}]
@@ -11,13 +16,56 @@ module LogStash module Modules class SettingsMerger
11
16
  # union will also coalesce identical hashes
12
17
  union_of_settings = (cli_settings | yml_settings)
13
18
  grouped_by_name = union_of_settings.group_by{|e| e["name"]}
14
- grouped_by_name.each do |name, array|
19
+ grouped_by_name.each do |_, array|
15
20
  if array.size == 2
16
- merged << array.first.merge(array.last)
21
+ merged << array.last.merge(array.first)
17
22
  else
18
23
  merged.concat(array)
19
24
  end
20
25
  end
21
26
  merged
22
27
  end
28
+
29
+ def merge_cloud_settings(module_settings, logstash_settings)
30
+ cloud_id = logstash_settings.get("cloud.id")
31
+ cloud_auth = logstash_settings.get("cloud.auth")
32
+ if cloud_id.nil?
33
+ if cloud_auth.nil?
34
+ return # user did not specify cloud settings
35
+ else
36
+ raise ArgumentError.new("Cloud Auth without Cloud Id")
37
+ end
38
+ end
39
+ if logger.debug?
40
+ settings_copy = LogStash::Util.deep_clone(module_settings)
41
+ end
42
+
43
+ module_settings["var.kibana.scheme"] = "https"
44
+ module_settings["var.kibana.host"] = cloud_id.kibana_host
45
+ module_settings["var.elasticsearch.hosts"] = cloud_id.elasticsearch_host
46
+ unless cloud_auth.nil?
47
+ module_settings["var.elasticsearch.username"] = cloud_auth.username
48
+ module_settings["var.elasticsearch.password"] = cloud_auth.password
49
+ module_settings["var.kibana.username"] = cloud_auth.username
50
+ module_settings["var.kibana.password"] = cloud_auth.password
51
+ end
52
+ if logger.debug?
53
+ format_module_settings(settings_copy, module_settings).each {|line| logger.debug(line)}
54
+ end
55
+ end
56
+
57
+ def format_module_settings(settings_before, settings_after)
58
+ output = []
59
+ output << "-------- Module Settings ---------"
60
+ settings_after.each do |setting_name, setting|
61
+ setting_before = settings_before.fetch(setting_name, "")
62
+ line = "#{setting_name}: '#{setting}'"
63
+ if setting_before != setting
64
+ line.concat(", was: '#{setting_before}'")
65
+ end
66
+ output << line
67
+ end
68
+ output << "-------- Module Settings ---------"
69
+ output
70
+ end
23
71
  end end end
@@ -19,7 +19,9 @@ module LogStash class OutputDelegator
19
19
  @namespaced_metric = metric.namespace(id.to_sym)
20
20
  @namespaced_metric.gauge(:name, config_name)
21
21
  @metric_events = @namespaced_metric.namespace(:events)
22
-
22
+ @in_counter = @metric_events.counter(:in)
23
+ @out_counter = @metric_events.counter(:out)
24
+ @time_metric = @metric_events.counter(:duration_in_millis)
23
25
  @strategy = strategy_registry.
24
26
  class_for(self.concurrency).
25
27
  new(@logger, @output_class, @namespaced_metric, execution_context, plugin_args)
@@ -42,11 +44,11 @@ module LogStash class OutputDelegator
42
44
  end
43
45
 
44
46
  def multi_receive(events)
45
- @metric_events.increment(:in, events.length)
46
- clock = @metric_events.time(:duration_in_millis)
47
+ @in_counter.increment(events.length)
48
+ start_time = java.lang.System.current_time_millis
47
49
  @strategy.multi_receive(events)
48
- clock.stop
49
- @metric_events.increment(:out, events.length)
50
+ @time_metric.increment(java.lang.System.current_time_millis - start_time)
51
+ @out_counter.increment(events.length)
50
52
  end
51
53
 
52
54
  def do_close
@@ -38,7 +38,7 @@ module LogStash; class BasePipeline
38
38
 
39
39
  def initialize(pipeline_config, namespaced_metric = nil, agent = nil)
40
40
  @logger = self.logger
41
-
41
+ @mutex = Mutex.new
42
42
  @ephemeral_id = SecureRandom.uuid
43
43
 
44
44
  @pipeline_config = pipeline_config
@@ -107,16 +107,27 @@ module LogStash; class BasePipeline
107
107
  LogStash::Compiler.compile_sources(sources_with_metadata, @settings)
108
108
  end
109
109
 
110
- def plugin(plugin_type, name, *args)
110
+ def plugin(plugin_type, name, line, column, *args)
111
111
  @plugin_counter += 1
112
112
 
113
113
  # Collapse the array of arguments into a single merged hash
114
114
  args = args.reduce({}, &:merge)
115
115
 
116
- id = if args["id"].nil? || args["id"].empty?
117
- args["id"] = "#{@config_hash}-#{@plugin_counter}"
116
+ if plugin_type == "codec"
117
+ id = SecureRandom.uuid # codecs don't really use their IDs for metrics, so we can use anything here
118
118
  else
119
- args["id"]
119
+ # Pull the ID from LIR to keep IDs consistent between the two representations
120
+ id = lir.graph.vertices.filter do |v|
121
+ v.source_with_metadata &&
122
+ v.source_with_metadata.line == line &&
123
+ v.source_with_metadata.column == column
124
+ end.findFirst.get.id
125
+ end
126
+
127
+ args["id"] = id # some code pulls the id out of the args
128
+
129
+ if !id
130
+ raise ConfigurationError, "Could not determine ID for #{plugin_type}/#{plugin_name}"
120
131
  end
121
132
 
122
133
  raise ConfigurationError, "Two plugins have the id '#{id}', please fix this conflict" if @plugins_by_id[id]
@@ -231,6 +242,7 @@ module LogStash; class Pipeline < BasePipeline
231
242
  @running = Concurrent::AtomicBoolean.new(false)
232
243
  @flushing = Concurrent::AtomicReference.new(false)
233
244
  @force_shutdown = Concurrent::AtomicBoolean.new(false)
245
+ @outputs_registered = Concurrent::AtomicBoolean.new(false)
234
246
  end # def initialize
235
247
 
236
248
  def ready?
@@ -392,9 +404,9 @@ module LogStash; class Pipeline < BasePipeline
392
404
 
393
405
  def start_workers
394
406
  @worker_threads.clear # In case we're restarting the pipeline
407
+ @outputs_registered.make_false
395
408
  begin
396
- register_plugins(@outputs)
397
- register_plugins(@filters)
409
+ maybe_setup_out_plugins
398
410
 
399
411
  pipeline_workers = safe_pipeline_worker_count
400
412
  batch_size = @settings.get("pipeline.batch.size")
@@ -460,16 +472,17 @@ module LogStash; class Pipeline < BasePipeline
460
472
  shutdown_requested |= signal.shutdown? # latch on shutdown signal
461
473
 
462
474
  batch = @filter_queue_client.read_batch # metrics are started in read_batch
463
- if (batch.size > 0)
475
+ if batch.size > 0
464
476
  @events_consumed.increment(batch.size)
465
477
  filter_batch(batch)
466
- flush_filters_to_batch(batch, :final => false) if signal.flush?
478
+ end
479
+ flush_filters_to_batch(batch, :final => false) if signal.flush?
480
+ if batch.size > 0
467
481
  output_batch(batch)
468
482
  unless @force_shutdown.true? # ack the current batch
469
483
  @filter_queue_client.close_batch(batch)
470
484
  end
471
485
  end
472
-
473
486
  # keep break at end of loop, after the read_batch operation, some pipeline specs rely on this "final read_batch" before shutdown.
474
487
  break if (shutdown_requested && !draining_queue?) || @force_shutdown.true?
475
488
  end
@@ -652,11 +665,11 @@ module LogStash; class Pipeline < BasePipeline
652
665
  # for backward compatibility in devutils for the rspec helpers, this method is not used
653
666
  # in the pipeline anymore.
654
667
  def filter(event, &block)
668
+ maybe_setup_out_plugins
655
669
  # filter_func returns all filtered events, including cancelled ones
656
- filter_func(event).each { |e| block.call(e) }
670
+ filter_func(event).each {|e| block.call(e)}
657
671
  end
658
672
 
659
-
660
673
  # perform filters flush and yield flushed event to the passed block
661
674
  # @param options [Hash]
662
675
  # @option options [Boolean] :final => true to signal a final shutdown flush
@@ -791,9 +804,16 @@ module LogStash; class Pipeline < BasePipeline
791
804
 
792
805
  private
793
806
 
807
+ def maybe_setup_out_plugins
808
+ if @outputs_registered.make_true
809
+ register_plugins(@outputs)
810
+ register_plugins(@filters)
811
+ end
812
+ end
813
+
794
814
  def default_logging_keys(other_keys = {})
795
815
  keys = super
796
- keys[:thread] = thread.inspect if thread
816
+ keys[:thread] ||= thread.inspect if thread
797
817
  keys
798
818
  end
799
819
 
@@ -802,6 +822,9 @@ module LogStash; class Pipeline < BasePipeline
802
822
  end
803
823
 
804
824
  def wrapped_write_client(plugin)
805
- LogStash::Instrument::WrappedWriteClient.new(@input_queue_client, self, metric, plugin)
825
+ #need to ensure that metrics are initialized one plugin at a time, else a race condition can exist.
826
+ @mutex.synchronize do
827
+ LogStash::Instrument::WrappedWriteClient.new(@input_queue_client, self, metric, plugin)
828
+ end
806
829
  end
807
830
  end; end
@@ -12,8 +12,10 @@ module LogStash
12
12
  "config.reload.interval",
13
13
  "config.string",
14
14
  "dead_letter_queue.enable",
15
+ "dead_letter_queue.max_bytes",
15
16
  "metric.collect",
16
17
  "path.config",
18
+ "path.dead_letter_queue",
17
19
  "path.queue",
18
20
  "pipeline.batch.delay",
19
21
  "pipeline.batch.size",
@@ -74,6 +74,19 @@ class LogStash::Runner < Clamp::StrictCommand
74
74
  :multivalued => true,
75
75
  :attribute_name => "modules_variable_list"
76
76
 
77
+ option ["--setup"], :flag,
78
+ I18n.t("logstash.runner.flag.modules_setup"),
79
+ :default => LogStash::SETTINGS.get_default("modules_setup"),
80
+ :attribute_name => "modules_setup"
81
+
82
+ option ["--cloud.id"], "CLOUD_ID",
83
+ I18n.t("logstash.runner.flag.cloud_id"),
84
+ :attribute_name => "cloud.id"
85
+
86
+ option ["--cloud.auth"], "CLOUD_AUTH",
87
+ I18n.t("logstash.runner.flag.cloud_auth"),
88
+ :attribute_name => "cloud.auth"
89
+
77
90
  # Pipeline settings
78
91
  option ["-w", "--pipeline.workers"], "COUNT",
79
92
  I18n.t("logstash.runner.flag.pipeline-workers"),
@@ -468,8 +481,7 @@ class LogStash::Runner < Clamp::StrictCommand
468
481
  Stud::trap("INT") do
469
482
  if @interrupted_once
470
483
  logger.fatal(I18n.t("logstash.agent.forced_sigint"))
471
- @agent.force_shutdown!
472
- exit
484
+ exit(1)
473
485
  else
474
486
  logger.warn(I18n.t("logstash.agent.sigint"))
475
487
  Thread.new(logger) {|lg| sleep 5; lg.warn(I18n.t("logstash.agent.slow_shutdown")) }
@@ -255,6 +255,7 @@ module LogStash
255
255
  @default = default
256
256
  end
257
257
  end
258
+
258
259
  def set(value)
259
260
  coerced_value = coerce(value)
260
261
  validate(coerced_value)
@@ -557,7 +558,32 @@ module LogStash
557
558
  end
558
559
  end
559
560
  end
561
+
562
+ class Modules < Coercible
563
+ def initialize(name, klass, default = nil)
564
+ super(name, klass, default, false)
565
+ end
566
+
567
+ def set(value)
568
+ @value = coerce(value)
569
+ @value_is_set = true
570
+ @value
571
+ end
572
+
573
+ def coerce(value)
574
+ if value.is_a?(@klass)
575
+ return value
576
+ end
577
+ @klass.new(value)
578
+ end
579
+
580
+ protected
581
+ def validate(value)
582
+ coerce(value)
583
+ end
584
+ end
560
585
  end
561
586
 
587
+
562
588
  SETTINGS = Settings.new
563
589
  end
@@ -0,0 +1,29 @@
1
+ # encoding: utf-8
2
+ require "logstash/namespace"
3
+ require "logstash/util/password"
4
+
5
+ module LogStash module Util class CloudSettingAuth
6
+ attr_reader :original, :username, :password
7
+
8
+ def initialize(value)
9
+ return if value.nil?
10
+
11
+ unless value.is_a?(String)
12
+ raise ArgumentError.new("Cloud Auth must be String. Received: #{value.class}")
13
+ end
14
+ @original = value
15
+ @username, sep, password = @original.partition(":")
16
+ if @username.empty? || sep.empty? || password.empty?
17
+ raise ArgumentError.new("Cloud Auth username and password format should be \"<username>:<password>\".")
18
+ end
19
+ @password = LogStash::Util::Password.new(password)
20
+ end
21
+
22
+ def to_s
23
+ "#{@username}:#{@password}"
24
+ end
25
+
26
+ def inspect
27
+ to_s
28
+ end
29
+ end end end
@@ -0,0 +1,41 @@
1
+ # encoding: utf-8
2
+ require "logstash/namespace"
3
+ require "base64"
4
+
5
+ module LogStash module Util class CloudSettingId
6
+ attr_reader :original, :decoded, :label, :elasticsearch_host, :kibana_host
7
+
8
+ def initialize(value)
9
+ return if value.nil?
10
+
11
+ unless value.is_a?(String)
12
+ raise ArgumentError.new("Cloud Id must be String. Received: #{value.class}")
13
+ end
14
+ @original = value
15
+ @label, sep, last = value.partition(":")
16
+ if last.empty?
17
+ @decoded = Base64.urlsafe_decode64(@label) rescue ""
18
+ @label = ""
19
+ else
20
+ @decoded = Base64.urlsafe_decode64(last) rescue ""
21
+ end
22
+ unless @decoded.count("$") == 2
23
+ raise ArgumentError.new("Cloud Id does not decode. Received: \"#{@original}\".")
24
+ end
25
+ parts = @decoded.split("$")
26
+ if parts.any?(&:empty?)
27
+ raise ArgumentError.new("Cloud Id, after decoding, is invalid. Format: '<part1>$<part2>$<part3>'. Received: \"#{@decoded}\".")
28
+ end
29
+ cloud_host, es_server, kb_server = parts
30
+ @elasticsearch_host = sprintf("%s.%s:443", es_server, cloud_host)
31
+ @kibana_host = sprintf("%s.%s:443", kb_server, cloud_host)
32
+ end
33
+
34
+ def to_s
35
+ @original.to_s
36
+ end
37
+
38
+ def inspect
39
+ to_s
40
+ end
41
+ end end end
@@ -0,0 +1,28 @@
1
+ # encoding: utf-8
2
+ require "logstash/namespace"
3
+ require "logstash/util/password"
4
+
5
+ module LogStash module Util class ModulesSettingArray
6
+ extend Forwardable
7
+ DELEGATED_METHODS = [].public_methods.reject{|symbol| symbol.to_s.end_with?('__')}
8
+
9
+ def_delegators :@original, *DELEGATED_METHODS
10
+
11
+ attr_reader :original
12
+ def initialize(value)
13
+ unless value.is_a?(Array)
14
+ raise ArgumentError.new("Module Settings must be an Array. Received: #{value.class}")
15
+ end
16
+ @original = value
17
+ # wrap passwords
18
+ @original.each do |hash|
19
+ hash.keys.select{|key| key.to_s.end_with?('password')}.each do |key|
20
+ hash[key] = LogStash::Util::Password.new(hash[key])
21
+ end
22
+ end
23
+ end
24
+
25
+ def __class__
26
+ LogStash::Util::ModulesSettingArray
27
+ end
28
+ end end end