logstash-core 6.0.0.beta1-java → 6.0.0.beta2-java

Sign up to get free protection for your applications and to get access to all the features.
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