logstash-core 5.0.0.alpha5.snapshot1-java → 5.0.0.alpha6.snapshot1-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.

Potentially problematic release.


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

Files changed (52) hide show
  1. checksums.yaml +4 -4
  2. data/lib/logstash-core/version.rb +1 -1
  3. data/lib/logstash/agent.rb +1 -1
  4. data/lib/logstash/api/commands/default_metadata.rb +1 -1
  5. data/lib/logstash/api/commands/hot_threads_reporter.rb +4 -7
  6. data/lib/logstash/api/commands/node.rb +5 -4
  7. data/lib/logstash/api/commands/stats.rb +8 -3
  8. data/lib/logstash/api/modules/base.rb +5 -0
  9. data/lib/logstash/api/modules/node.rb +1 -2
  10. data/lib/logstash/api/modules/node_stats.rb +1 -2
  11. data/lib/logstash/codecs/base.rb +29 -1
  12. data/lib/logstash/config/mixin.rb +1 -1
  13. data/lib/logstash/environment.rb +5 -5
  14. data/lib/logstash/filter_delegator.rb +4 -5
  15. data/lib/logstash/instrument/periodic_poller/jvm.rb +43 -10
  16. data/lib/logstash/output_delegator.rb +33 -168
  17. data/lib/logstash/output_delegator_strategies/legacy.rb +29 -0
  18. data/lib/logstash/output_delegator_strategies/shared.rb +20 -0
  19. data/lib/logstash/output_delegator_strategies/single.rb +23 -0
  20. data/lib/logstash/output_delegator_strategy_registry.rb +36 -0
  21. data/lib/logstash/outputs/base.rb +39 -26
  22. data/lib/logstash/patches/clamp.rb +6 -0
  23. data/lib/logstash/pipeline.rb +42 -14
  24. data/lib/logstash/pipeline_reporter.rb +2 -8
  25. data/lib/logstash/plugin.rb +6 -10
  26. data/lib/logstash/runner.rb +12 -9
  27. data/lib/logstash/settings.rb +124 -21
  28. data/lib/logstash/util/wrapped_synchronous_queue.rb +17 -1
  29. data/lib/logstash/version.rb +1 -1
  30. data/lib/logstash/webserver.rb +44 -33
  31. data/locales/en.yml +5 -1
  32. data/logstash-core.gemspec +2 -2
  33. data/spec/api/lib/api/node_spec.rb +62 -10
  34. data/spec/api/lib/api/node_stats_spec.rb +16 -3
  35. data/spec/api/lib/api/support/resource_dsl_methods.rb +11 -1
  36. data/spec/api/spec_helper.rb +1 -1
  37. data/spec/conditionals_spec.rb +12 -1
  38. data/spec/logstash/agent_spec.rb +3 -0
  39. data/spec/logstash/codecs/base_spec.rb +74 -0
  40. data/spec/logstash/instrument/periodic_poller/jvm_spec.rb +37 -10
  41. data/spec/logstash/output_delegator_spec.rb +64 -89
  42. data/spec/logstash/outputs/base_spec.rb +91 -15
  43. data/spec/logstash/pipeline_reporter_spec.rb +1 -6
  44. data/spec/logstash/pipeline_spec.rb +20 -22
  45. data/spec/logstash/plugin_spec.rb +3 -3
  46. data/spec/logstash/runner_spec.rb +86 -3
  47. data/spec/logstash/settings/integer_spec.rb +20 -0
  48. data/spec/logstash/settings/numeric_spec.rb +28 -0
  49. data/spec/logstash/settings/port_range_spec.rb +93 -0
  50. data/spec/logstash/util/wrapped_synchronous_queue_spec.rb +6 -0
  51. data/spec/logstash/webserver_spec.rb +95 -0
  52. metadata +20 -6
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 31b35e84fcbac8fa8ccb7ac01aac834645c04084
4
- data.tar.gz: 3c5d884562c45235f897279458b3e0ee8a840d4b
3
+ metadata.gz: e89e8c25486877ae001285428c6e6d9fbe3dfc6b
4
+ data.tar.gz: 5e6318f6ae9f263864588fa2361f5e9ebdc94fb4
5
5
  SHA512:
6
- metadata.gz: 2355f8393a9f37222e45f7698fdb2b3cf10be0a52cfb132a3bbaf546dadf11e05f4c2b8f60a216409b5bed7b49eba79803270dbac561217a0e36ac107e2348b2
7
- data.tar.gz: 5642f18d54cfb80d5b159575f1ba59b652ec2d57606a5609bda4644740367d7f82187718728c9ccc6854d944291855ec48f54d66b957e3e6e736c52d9c2b9339
6
+ metadata.gz: af57be94f22a74d3ffbbde98eca0eb92fa503c15254b666c5fdfe518c401af10260203b520b9ce26f2b9043c93fdd2289f2af5ecb1387deaf7f65bdfa6ad1a19
7
+ data.tar.gz: 9d6a1a9a69f77fb49cb0ec3f5d2be1f8a0f2f3a460590a4722ae071cd711fb5a792baa663c233a0f06b94dbea6e7a09a3fd9c0f60ee0ac7d488097b5a96f2a52
@@ -5,4 +5,4 @@
5
5
  # Note to authors: this should not include dashes because 'gem' barfs if
6
6
  # you include a dash in the version string.
7
7
 
8
- LOGSTASH_CORE_VERSION = "5.0.0.alpha5.snapshot1"
8
+ LOGSTASH_CORE_VERSION = "5.0.0.alpha6.snapshot1"
@@ -135,7 +135,7 @@ class LogStash::Agent
135
135
 
136
136
  private
137
137
  def start_webserver
138
- options = {:http_host => @http_host, :http_port => @http_port, :http_environment => @http_environment }
138
+ options = {:http_host => @http_host, :http_ports => @http_port, :http_environment => @http_environment }
139
139
  @webserver = LogStash::WebServer.new(@logger, self, options)
140
140
  Thread.new(@webserver) do |webserver|
141
141
  LogStash::Util.set_thread_name("Api Webserver")
@@ -9,7 +9,7 @@ module LogStash
9
9
  def all
10
10
  {:host => host, :version => version, :http_address => http_address}
11
11
  end
12
-
12
+
13
13
  def host
14
14
  Socket.gethostname
15
15
  end
@@ -1,6 +1,7 @@
1
1
  # encoding: utf-8
2
2
 
3
3
  class HotThreadsReport
4
+ STRING_SEPARATOR_LENGTH = 80.freeze
4
5
  HOT_THREADS_STACK_TRACES_SIZE_DEFAULT = 10.freeze
5
6
 
6
7
  def initialize(cmd, options)
@@ -13,19 +14,16 @@ class HotThreadsReport
13
14
  def to_s
14
15
  hash = to_hash[:hot_threads]
15
16
  report = "#{I18n.t("logstash.web_api.hot_threads.title", :hostname => hash[:hostname], :time => hash[:time], :top_count => @thread_dump.top_count )} \n"
16
- report << '=' * 80
17
+ report << '=' * STRING_SEPARATOR_LENGTH
17
18
  report << "\n"
18
19
  hash[:threads].each do |thread|
19
- thread_report = ""
20
- thread_report = "#{I18n.t("logstash.web_api.
21
- hot_threads.thread_title", :percent_of_cpu_time => thread[:percent_of_cpu_time], :thread_state => thread[:state], :thread_name => thread[:name])} \n"
22
- thread_report = "#{thread[:percent_of_cpu_time]} % of of cpu usage by #{thread[:state]} thread named '#{thread[:name]}'\n"
20
+ thread_report = "#{I18n.t("logstash.web_api.hot_threads.thread_title", :percent_of_cpu_time => thread[:percent_of_cpu_time], :thread_state => thread[:state], :thread_name => thread[:name])} \n"
23
21
  thread_report << "#{thread[:path]}\n" if thread[:path]
24
22
  thread[:traces].each do |trace|
25
23
  thread_report << "\t#{trace}\n"
26
24
  end
27
25
  report << thread_report
28
- report << '-' * 80
26
+ report << '-' * STRING_SEPARATOR_LENGTH
29
27
  report << "\n"
30
28
  end
31
29
  report
@@ -57,5 +55,4 @@ class HotThreadsReport
57
55
  def cpu_time(hash)
58
56
  hash["cpu.time"] / 1000000.0
59
57
  end
60
-
61
58
  end
@@ -20,7 +20,7 @@ module LogStash
20
20
  def pipeline
21
21
  extract_metrics(
22
22
  [:stats, :pipelines, :main, :config],
23
- :workers, :batch_size, :batch_delay
23
+ :workers, :batch_size, :batch_delay, :config_reload_automatic, :config_reload_interval
24
24
  )
25
25
  end
26
26
 
@@ -35,27 +35,28 @@ module LogStash
35
35
 
36
36
  def jvm
37
37
  memory_bean = ManagementFactory.getMemoryMXBean()
38
+
38
39
  {
39
40
  :pid => ManagementFactory.getRuntimeMXBean().getName().split("@").first.to_i,
40
41
  :version => java.lang.System.getProperty("java.version"),
41
42
  :vm_name => java.lang.System.getProperty("java.vm.name"),
42
43
  :vm_version => java.lang.System.getProperty("java.version"),
43
44
  :vm_vendor => java.lang.System.getProperty("java.vendor"),
44
- :vm_name => java.lang.System.getProperty("java.vm.name"),
45
+ :vm_name => java.lang.System.getProperty("java.vm.name"),
45
46
  :start_time_in_millis => started_at,
46
47
  :mem => {
47
48
  :heap_init_in_bytes => (memory_bean.getHeapMemoryUsage().getInit() < 0 ? 0 : memory_bean.getHeapMemoryUsage().getInit()),
48
49
  :heap_max_in_bytes => (memory_bean.getHeapMemoryUsage().getMax() < 0 ? 0 : memory_bean.getHeapMemoryUsage().getMax()),
49
50
  :non_heap_init_in_bytes => (memory_bean.getNonHeapMemoryUsage().getInit() < 0 ? 0 : memory_bean.getNonHeapMemoryUsage().getInit()),
50
51
  :non_heap_max_in_bytes => (memory_bean.getNonHeapMemoryUsage().getMax() < 0 ? 0 : memory_bean.getNonHeapMemoryUsage().getMax())
51
- }
52
+ },
53
+ :gc_collectors => ManagementFactory.getGarbageCollectorMXBeans().collect(&:getName)
52
54
  }
53
55
  end
54
56
 
55
57
  def hot_threads(options={})
56
58
  HotThreadsReport.new(self, options)
57
59
  end
58
-
59
60
  end
60
61
  end
61
62
  end
@@ -14,7 +14,8 @@ module LogStash
14
14
  :count,
15
15
  :peak_count
16
16
  ),
17
- :mem => memory
17
+ :mem => memory,
18
+ :gc => gc
18
19
  }
19
20
  end
20
21
 
@@ -32,7 +33,7 @@ module LogStash
32
33
  def events
33
34
  extract_metrics(
34
35
  [:stats, :events],
35
- :in, :filtered, :out
36
+ :in, :filtered, :out, :duration_in_millis
36
37
  )
37
38
  end
38
39
 
@@ -59,6 +60,10 @@ module LogStash
59
60
  }
60
61
  end
61
62
 
63
+ def gc
64
+ service.get_shallow(:jvm, :gc)
65
+ end
66
+
62
67
  def hot_threads(options={})
63
68
  HotThreadsReport.new(self, options)
64
69
  end
@@ -70,7 +75,7 @@ module LogStash
70
75
  # Turn the `plugins` stats hash into an array of [ {}, {}, ... ]
71
76
  # This is to produce an array of data points, one point for each
72
77
  # plugin instance.
73
- return [] unless stats[:plugins].include?(plugin_type)
78
+ return [] unless stats[:plugins] && stats[:plugins].include?(plugin_type)
74
79
  stats[:plugins][plugin_type].collect do |id, data|
75
80
  { :id => id }.merge(data)
76
81
  end
@@ -34,6 +34,11 @@ module LogStash
34
34
  text = as == :string ? "" : {}
35
35
  respond_with(text, :as => as)
36
36
  end
37
+
38
+ protected
39
+ def human?
40
+ params.has_key?("human") && (params["human"].nil? || as_boolean(params["human"]) == true)
41
+ end
37
42
  end
38
43
  end
39
44
  end
@@ -14,7 +14,7 @@ module LogStash
14
14
 
15
15
  options = {
16
16
  :ignore_idle_threads => as_boolean(ignore_idle_threads),
17
- :human => params.has_key?("human")
17
+ :human => human?
18
18
  }
19
19
  options[:threads] = params["threads"].to_i if params.has_key?("threads")
20
20
 
@@ -26,7 +26,6 @@ module LogStash
26
26
  selected_fields = extract_fields(params["filter"].to_s.strip)
27
27
  respond_with node.all(selected_fields)
28
28
  end
29
-
30
29
  end
31
30
  end
32
31
  end
@@ -12,8 +12,7 @@ module LogStash
12
12
  payload = {
13
13
  :jvm => jvm_payload,
14
14
  :process => process_payload,
15
- :mem => mem_payload,
16
- :pipeline => pipeline_payload
15
+ :pipeline => pipeline_payload,
17
16
  }
18
17
  respond_with(payload, {:filter => params["filter"]})
19
18
  end
@@ -18,6 +18,7 @@ module LogStash::Codecs; class Base < LogStash::Plugin
18
18
  super
19
19
  config_init(@params)
20
20
  register if respond_to?(:register)
21
+ setup_multi_encode!
21
22
  end
22
23
 
23
24
  public
@@ -28,10 +29,37 @@ module LogStash::Codecs; class Base < LogStash::Plugin
28
29
  alias_method :<<, :decode
29
30
 
30
31
  public
32
+ # DEPRECATED: Prefer defining encode_sync or multi_encode
31
33
  def encode(event)
32
- raise "#{self.class}#encode must be overidden"
34
+ encoded = multi_encode([event])
35
+ encoded.each {|event,data| @on_event.call(event,data) }
33
36
  end # def encode
34
37
 
38
+ public
39
+ # Relies on the codec being synchronous (which they all are!)
40
+ # We need a better long term design here, but this is an improvement
41
+ # over the current API for shared plugins
42
+ # It is best if the codec implements this directly
43
+ def multi_encode(events)
44
+ if @has_encode_sync
45
+ events.map {|event| [event, self.encode_sync(event)]}
46
+ else
47
+ batch = Thread.current[:logstash_output_codec_batch] ||= []
48
+ batch.clear
49
+
50
+ events.each {|event| self.encode(event) }
51
+ batch
52
+ end
53
+ end
54
+
55
+ def setup_multi_encode!
56
+ @has_encode_sync = self.methods.include?(:encode_sync)
57
+
58
+ on_event do |event, data|
59
+ Thread.current[:logstash_output_codec_batch] << [event, data]
60
+ end
61
+ end
62
+
35
63
  public
36
64
  def close; end;
37
65
 
@@ -209,7 +209,7 @@ module LogStash::Config::Mixin
209
209
 
210
210
  name = name.to_s if name.is_a?(Symbol)
211
211
  @config[name] = opts # ok if this is empty
212
-
212
+
213
213
  if name.is_a?(String)
214
214
  define_method(name) { instance_variable_get("@#{name}") }
215
215
  define_method("#{name}=") { |v| instance_variable_set("@#{name}", v) }
@@ -21,12 +21,12 @@ module LogStash
21
21
  Setting::String.new("config.string", nil, false),
22
22
  Setting::Boolean.new("config.test_and_exit", false),
23
23
  Setting::Boolean.new("config.reload.automatic", false),
24
- Setting::Numeric.new("config.reload.interval", 3),
24
+ Setting::Numeric.new("config.reload.interval", 3), # in seconds
25
25
  Setting::Boolean.new("metric.collect", true) {|v| v == true }, # metric collection cannot be disabled
26
26
  Setting::String.new("pipeline.id", "main"),
27
- Setting::Numeric.new("pipeline.workers", LogStash::Config::CpuCoreStrategy.maximum),
28
- Setting::Numeric.new("pipeline.output.workers", 1),
29
- Setting::Numeric.new("pipeline.batch.size", 125),
27
+ Setting::PositiveInteger.new("pipeline.workers", LogStash::Config::CpuCoreStrategy.maximum),
28
+ Setting::PositiveInteger.new("pipeline.output.workers", 1),
29
+ Setting::PositiveInteger.new("pipeline.batch.size", 125),
30
30
  Setting::Numeric.new("pipeline.batch.delay", 5), # in milliseconds
31
31
  Setting::Boolean.new("pipeline.unsafe_shutdown", false),
32
32
  Setting.new("path.plugins", Array, []),
@@ -38,7 +38,7 @@ module LogStash
38
38
  Setting::String.new("path.log", nil, false),
39
39
  Setting::String.new("log.format", "plain", true, ["json", "plain"]),
40
40
  Setting::String.new("http.host", "127.0.0.1"),
41
- Setting::Port.new("http.port", 9600),
41
+ Setting::PortRange.new("http.port", 9600..9700),
42
42
  Setting::String.new("http.environment", "production"),
43
43
  ].each {|setting| SETTINGS.register(setting) }
44
44
 
@@ -13,15 +13,14 @@ module LogStash
13
13
  ]
14
14
  def_delegators :@filter, *DELEGATED_METHODS
15
15
 
16
- def initialize(logger, klass, metric, *args)
17
- options = args.reduce({}, :merge)
18
-
16
+ def initialize(logger, klass, metric, plugin_args)
19
17
  @logger = logger
20
18
  @klass = klass
21
- @filter = klass.new(options)
19
+ @id = plugin_args["id"]
20
+ @filter = klass.new(plugin_args)
22
21
 
23
22
  # Scope the metrics to the plugin
24
- namespaced_metric = metric.namespace(@filter.plugin_unique_name.to_sym)
23
+ namespaced_metric = metric.namespace("#{@klass.config_name}_#{@id}".to_sym)
25
24
  @filter.metric = namespaced_metric
26
25
 
27
26
  @metric_events = namespaced_metric.namespace(:events)
@@ -1,18 +1,36 @@
1
1
 
2
2
  # encoding: utf-8
3
3
  require "logstash/instrument/periodic_poller/base"
4
- require 'jrmonitor'
4
+ require "jrmonitor"
5
+ require "set"
5
6
 
6
7
  java_import 'java.lang.management.ManagementFactory'
7
8
  java_import 'java.lang.management.OperatingSystemMXBean'
9
+ java_import 'java.lang.management.GarbageCollectorMXBean'
8
10
  java_import 'com.sun.management.UnixOperatingSystemMXBean'
9
11
  java_import 'javax.management.MBeanServer'
10
12
  java_import 'javax.management.ObjectName'
11
13
  java_import 'javax.management.AttributeList'
12
14
  java_import 'javax.naming.directory.Attribute'
13
15
 
16
+
14
17
  module LogStash module Instrument module PeriodicPoller
15
18
  class JVM < Base
19
+ class GarbageCollectorName
20
+ YOUNG_GC_NAMES = Set.new(["Copy", "PS Scavenge", "ParNew", "G1 Young Generation"])
21
+ OLD_GC_NAMES = Set.new(["MarkSweepCompact", "PS MarkSweep", "ConcurrentMarkSweep", "G1 Old Generation"])
22
+
23
+ YOUNG = :young
24
+ OLD = :old
25
+
26
+ def self.get(gc_name)
27
+ if YOUNG_GC_NAMES.include?(gc_name)
28
+ YOUNG
29
+ elsif(OLD_GC_NAMES.include?(gc_name))
30
+ OLD
31
+ end
32
+ end
33
+ end
16
34
 
17
35
  attr_reader :metric
18
36
 
@@ -22,31 +40,46 @@ module LogStash module Instrument module PeriodicPoller
22
40
  end
23
41
 
24
42
  def collect
25
- raw = JRMonitor.memory.generate
43
+ raw = JRMonitor.memory.generate
26
44
  collect_heap_metrics(raw)
27
45
  collect_non_heap_metrics(raw)
28
46
  collect_pools_metrics(raw)
29
47
  collect_threads_metrics
30
48
  collect_process_metrics
49
+ collect_gc_stats
31
50
  end
32
51
 
33
52
  private
34
53
 
35
- def collect_threads_metrics
54
+ def collect_gc_stats
55
+ garbage_collectors = ManagementFactory.getGarbageCollectorMXBeans()
56
+
57
+ garbage_collectors.each do |collector|
58
+ name = GarbageCollectorName.get(collector.getName())
59
+ if name.nil?
60
+ logger.error("Unknown garbage collector name", :name => name)
61
+ else
62
+ metric.gauge([:jvm, :gc, :collectors, name], :collection_count, collector.getCollectionCount())
63
+ metric.gauge([:jvm, :gc, :collectors, name], :collection_time_in_millis, collector.getCollectionTime())
64
+ end
65
+ end
66
+ end
67
+
68
+ def collect_threads_metrics
36
69
  threads = JRMonitor.threads.generate
37
-
70
+
38
71
  current = threads.count
39
72
  if @peak_threads.nil? || @peak_threads < current
40
73
  @peak_threads = current
41
- end
42
-
43
- metric.gauge([:jvm, :threads], :count, threads.count)
74
+ end
75
+
76
+ metric.gauge([:jvm, :threads], :count, threads.count)
44
77
  metric.gauge([:jvm, :threads], :peak_count, @peak_threads)
45
78
  end
46
79
 
47
80
  def collect_process_metrics
48
81
  process_metrics = JRMonitor.process.generate
49
-
82
+
50
83
  path = [:jvm, :process]
51
84
 
52
85
 
@@ -91,6 +124,7 @@ module LogStash module Instrument module PeriodicPoller
91
124
  end
92
125
  end
93
126
 
127
+
94
128
  def build_pools_metrics(data)
95
129
  heap = data["heap"]
96
130
  old = {}
@@ -129,9 +163,8 @@ module LogStash module Instrument module PeriodicPoller
129
163
  :committed_in_bytes => 0,
130
164
  :max_in_bytes => 0,
131
165
  :peak_used_in_bytes => 0,
132
- :peak_max_in_bytes => 0
166
+ :peak_max_in_bytes => 0
133
167
  }
134
168
  end
135
-
136
169
  end
137
170
  end; end; end
@@ -1,192 +1,57 @@
1
- # encoding: utf-8
2
- require "concurrent/atomic/atomic_fixnum"
3
- java_import "java.util.concurrent.CopyOnWriteArrayList"
1
+ require "logstash/output_delegator_strategy_registry"
2
+
3
+ require "logstash/output_delegator_strategies/shared"
4
+ require "logstash/output_delegator_strategies/single"
5
+ require "logstash/output_delegator_strategies/legacy"
4
6
 
5
- # This class goes hand in hand with the pipeline to provide a pool of
6
- # free workers to be used by pipeline worker threads. The pool is
7
- # internally represented with a SizedQueue set the the size of the number
8
- # of 'workers' the output plugin is configured with.
9
- #
10
- # This plugin also records some basic statistics
11
7
  module LogStash class OutputDelegator
12
- attr_reader :workers, :config, :threadsafe
8
+ attr_reader :metric, :metric_events, :strategy, :namespaced_metric, :metric_events , :plugin_args, :strategy_registry
13
9
 
14
- # The *args this takes are the same format that a Outputs::Base takes. A list of hashes with parameters in them
15
- # Internally these just get merged together into a single hash
16
- def initialize(logger, klass, default_worker_count, metric, *plugin_args)
10
+ def initialize(logger, output_class, metric, strategy_registry, plugin_args)
17
11
  @logger = logger
18
- @threadsafe = klass.threadsafe?
19
- @config = plugin_args.reduce({}, :merge)
20
- @klass = klass
21
- @workers = java.util.concurrent.CopyOnWriteArrayList.new
22
- @default_worker_count = default_worker_count
23
- @registered = false
24
-
25
- # Create an instance of the input so we can fetch the identifier
26
- output = @klass.new(@config)
27
-
28
- # Scope the metrics to the plugin
29
- namespaced_metric = metric.namespace(output.plugin_unique_name.to_sym)
30
- output.metric = namespaced_metric
31
-
32
- @metric_events = namespaced_metric.namespace(:events)
33
- namespaced_metric.gauge(:name, config_name)
12
+ @output_class = output_class
13
+ @metric = metric
14
+ @plugin_args = plugin_args
15
+ @strategy_registry = strategy_registry
16
+ raise ArgumentError, "No strategy registry specified" unless strategy_registry
17
+ raise ArgumentError, "No ID specified! Got args #{plugin_args}" unless id
18
+
19
+ build_strategy!
34
20
 
35
- @events_received = Concurrent::AtomicFixnum.new(0)
21
+ @namespaced_metric = metric.namespace(id.to_sym)
22
+ @metric_events = @namespaced_metric.namespace(:events)
23
+ @namespaced_metric.gauge(:name, id)
36
24
  end
37
25
 
38
- def threadsafe?
39
- !!@threadsafe
26
+ def config_name
27
+ @output_class.config_name
40
28
  end
41
29
 
42
- def warn_on_worker_override!
43
- # The user has configured extra workers, but this plugin doesn't support it :(
44
- if worker_limits_overriden?
45
- message = @klass.workers_not_supported_message
46
- warning_meta = {:plugin => @klass.config_name, :worker_count => @config["workers"]}
47
- if message
48
- warning_meta[:message] = message
49
- @logger.warn(I18n.t("logstash.pipeline.output-worker-unsupported-with-message", warning_meta))
50
- else
51
- @logger.warn(I18n.t("logstash.pipeline.output-worker-unsupported", warning_meta))
52
- end
53
- end
30
+ def concurrency
31
+ @output_class.concurrency
54
32
  end
55
33
 
56
- def worker_limits_overriden?
57
- @config["workers"] && @config["workers"] > 1 && @klass.workers_not_supported?
34
+ def build_strategy!
35
+ @strategy = strategy_registry.
36
+ class_for(self.concurrency).
37
+ new(@logger, @output_class, @metric, @plugin_args)
58
38
  end
59
39
 
60
- def target_worker_count
61
- # Remove in 5.0 after all plugins upgraded to use class level declarations
62
- raise ArgumentError, "Attempted to detect target worker count before instantiating a worker to test for legacy workers_not_supported!" if @workers.size == 0
63
-
64
- if @threadsafe || @klass.workers_not_supported?
65
- 1
66
- else
67
- @config["workers"] || @default_worker_count
68
- end
69
- end
70
-
71
- def config_name
72
- @klass.config_name
40
+ def id
41
+ @plugin_args["id"]
73
42
  end
74
43
 
75
44
  def register
76
- raise ArgumentError, "Attempted to register #{self} twice!" if @registered
77
- @registered = true
78
- # We define this as an array regardless of threadsafety
79
- # to make reporting simpler, even though a threadsafe plugin will just have
80
- # a single instance
81
- #
82
- # Older plugins invoke the instance method Outputs::Base#workers_not_supported
83
- # To detect these we need an instance to be created first :()
84
- # TODO: In the next major version after 2.x remove support for this
85
- @workers << @klass.new(@config)
86
- @workers.first.register # Needed in case register calls `workers_not_supported`
87
-
88
- @logger.debug("Will start workers for output", :worker_count => target_worker_count, :class => @klass.name)
89
-
90
- # Threadsafe versions don't need additional workers
91
- setup_additional_workers!(target_worker_count) unless @threadsafe
92
- # We skip the first worker because that's pre-registered to deal with legacy workers_not_supported
93
- @workers.subList(1,@workers.size).each(&:register)
94
- setup_multi_receive!
95
- end
96
-
97
- def setup_additional_workers!(target_worker_count)
98
- warn_on_worker_override!
99
-
100
- (target_worker_count - 1).times do
101
- inst = @klass.new(@config)
102
- inst.metric = @metric
103
- @workers << inst
104
- end
105
-
106
- # This queue is used to manage sharing across threads
107
- @worker_queue = SizedQueue.new(target_worker_count)
108
- @workers.each {|w| @worker_queue << w }
45
+ @strategy.register
109
46
  end
110
47
 
111
- def setup_multi_receive!
112
- # One might wonder why we don't use something like
113
- # define_singleton_method(:multi_receive, method(:threadsafe_multi_receive)
114
- # and the answer is this is buggy on Jruby 1.7.x . It works 98% of the time!
115
- # The other 2% you get weird errors about rebinding to the same object
116
- # Until we switch to Jruby 9.x keep the define_singleton_method parts
117
- # the way they are, with a block
118
- # See https://github.com/jruby/jruby/issues/3582
119
- if threadsafe?
120
- @threadsafe_worker = @workers.first
121
- define_singleton_method(:multi_receive) do |events|
122
- threadsafe_multi_receive(events)
123
- end
124
- else
125
- define_singleton_method(:multi_receive) do |events|
126
- worker_multi_receive(events)
127
- end
128
- end
129
- end
130
-
131
- def threadsafe_multi_receive(events)
132
- @events_received.increment(events.length)
48
+ def multi_receive(events)
133
49
  @metric_events.increment(:in, events.length)
134
-
135
- clock = @metric_events.time(:duration_in_millis)
136
- @threadsafe_worker.multi_receive(events)
137
- clock.stop
50
+ @strategy.multi_receive(events)
138
51
  @metric_events.increment(:out, events.length)
139
52
  end
140
53
 
141
- def worker_multi_receive(events)
142
- @events_received.increment(events.length)
143
- @metric_events.increment(:in, events.length)
144
-
145
- worker = @worker_queue.pop
146
- begin
147
- clock = @metric_events.time(:duration_in_millis)
148
- worker.multi_receive(events)
149
- clock.stop
150
- @metric_events.increment(:out, events.length)
151
- ensure
152
- @worker_queue.push(worker)
153
- end
154
- end
155
-
156
54
  def do_close
157
- @logger.debug("closing output delegator", :klass => @klass.name)
158
-
159
- if @threadsafe
160
- @workers.each(&:do_close)
161
- else
162
- worker_count.times do
163
- worker = @worker_queue.pop
164
- worker.do_close
165
- end
166
- end
55
+ @strategy.do_close
167
56
  end
168
-
169
- def events_received
170
- @events_received.value
171
- end
172
-
173
- # There's no concept of 'busy' workers for a threadsafe plugin!
174
- def busy_workers
175
- if @threadsafe
176
- 0
177
- else
178
- # The pipeline reporter can run before the outputs are registered trying to pull a value here
179
- # In that case @worker_queue is empty, we just return 0
180
- return 0 unless @worker_queue
181
- @workers.size - @worker_queue.size
182
- end
183
- end
184
-
185
- def worker_count
186
- @workers.size
187
- end
188
-
189
- private
190
- # Needed for testing, so private
191
- attr_reader :threadsafe_worker, :worker_queue
192
- end end
57
+ end; end