logstash-core 2.0.1.snapshot1-java → 2.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.
- checksums.yaml +4 -4
- data/lib/logstash/agent.rb +21 -6
- data/lib/logstash/config/mixin.rb +0 -6
- data/lib/logstash/event.rb +1 -1
- data/lib/logstash/filters/base.rb +1 -2
- data/lib/logstash/inputs/base.rb +0 -5
- data/lib/logstash/outputs/base.rb +7 -5
- data/lib/logstash/pipeline.rb +85 -15
- data/lib/logstash/plugin.rb +5 -0
- data/lib/logstash/shutdown_controller.rb +127 -0
- data/lib/logstash/util.rb +35 -0
- data/lib/logstash/util/defaults_printer.rb +1 -1
- data/lib/logstash/util/worker_threads_default_printer.rb +14 -2
- data/lib/logstash/version.rb +1 -1
- data/locales/en.yml +5 -0
- data/logstash-core.gemspec +2 -1
- data/spec/core/event_spec.rb +19 -0
- data/spec/core/pipeline_spec.rb +113 -7
- data/spec/core/shutdown_controller_spec.rb +107 -0
- data/spec/license_spec.rb +1 -0
- data/spec/plugin_manager/install_spec.rb +28 -0
- data/spec/plugin_manager/update_spec.rb +39 -0
- data/spec/plugin_manager/util_spec.rb +71 -0
- data/spec/util/compress_spec.rb +121 -0
- data/spec/util/defaults_printer_spec.rb +3 -2
- data/spec/util/worker_threads_default_printer_spec.rb +30 -11
- metadata +76 -54
- data/lib/logstash/util/reporter.rb +0 -28
- data/spec/pluginmanager/util_spec.rb +0 -42
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA1:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 79705663d762e3aa65d23f6c98c0fb39923546ef
|
|
4
|
+
data.tar.gz: 66c3fc910d9d9290189c55843bdfbf7ed4d3ea65
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 455434efcd9788628c5350cb6f593eaad237a10269b68627d32e084aaa284c9a76b41d305b71ec4e0ee4d34e09f51a8dcaa2e03985f2be8f8e03ac0bef7fc3b0
|
|
7
|
+
data.tar.gz: b64af5a363fcd0096d06f4163b82595735acb58a3a958b39c6258a8cb08eb415342be4206f7d6a5e7765ba8c7a1cee2339f4eae8543e6e851c2d0d92c77c27a6
|
data/lib/logstash/agent.rb
CHANGED
|
@@ -23,7 +23,7 @@ class LogStash::Agent < Clamp::Command
|
|
|
23
23
|
option ["-w", "--filterworkers"], "COUNT",
|
|
24
24
|
I18n.t("logstash.agent.flag.filterworkers"),
|
|
25
25
|
:attribute_name => :filter_workers,
|
|
26
|
-
:default =>
|
|
26
|
+
:default => 0, &:to_i
|
|
27
27
|
|
|
28
28
|
option ["-l", "--log"], "FILE",
|
|
29
29
|
I18n.t("logstash.agent.flag.log"),
|
|
@@ -50,6 +50,11 @@ class LogStash::Agent < Clamp::Command
|
|
|
50
50
|
I18n.t("logstash.agent.flag.configtest"),
|
|
51
51
|
:attribute_name => :config_test
|
|
52
52
|
|
|
53
|
+
option "--[no-]allow-unsafe-shutdown", :flag,
|
|
54
|
+
I18n.t("logstash.agent.flag.unsafe_shutdown"),
|
|
55
|
+
:attribute_name => :unsafe_shutdown,
|
|
56
|
+
:default => false
|
|
57
|
+
|
|
53
58
|
# Emit a warning message.
|
|
54
59
|
def warn(message)
|
|
55
60
|
# For now, all warnings are fatal.
|
|
@@ -75,6 +80,9 @@ class LogStash::Agent < Clamp::Command
|
|
|
75
80
|
require "logstash/plugin"
|
|
76
81
|
@logger = Cabin::Channel.get(LogStash)
|
|
77
82
|
|
|
83
|
+
LogStash::ShutdownController.unsafe_shutdown = unsafe_shutdown?
|
|
84
|
+
LogStash::ShutdownController.logger = @logger
|
|
85
|
+
|
|
78
86
|
if version?
|
|
79
87
|
show_version
|
|
80
88
|
return 0
|
|
@@ -143,7 +151,7 @@ class LogStash::Agent < Clamp::Command
|
|
|
143
151
|
configure_logging(log_file)
|
|
144
152
|
end
|
|
145
153
|
|
|
146
|
-
pipeline.configure("filter-workers", filter_workers)
|
|
154
|
+
pipeline.configure("filter-workers", filter_workers) if filter_workers > 0
|
|
147
155
|
|
|
148
156
|
# Stop now if we are only asking for a config test.
|
|
149
157
|
if config_test?
|
|
@@ -176,8 +184,7 @@ class LogStash::Agent < Clamp::Command
|
|
|
176
184
|
|
|
177
185
|
def shutdown(pipeline)
|
|
178
186
|
pipeline.shutdown do
|
|
179
|
-
|
|
180
|
-
InflightEventsReporter.start(pipeline.input_to_filter, pipeline.filter_to_output, pipeline.outputs)
|
|
187
|
+
::LogStash::ShutdownController.start(pipeline)
|
|
181
188
|
end
|
|
182
189
|
end
|
|
183
190
|
|
|
@@ -312,19 +319,27 @@ class LogStash::Agent < Clamp::Command
|
|
|
312
319
|
Dir.glob(path).sort.each do |file|
|
|
313
320
|
next unless File.file?(file)
|
|
314
321
|
if file.match(/~$/)
|
|
315
|
-
@logger.debug("NOT reading config file because it is a temp file", :
|
|
322
|
+
@logger.debug("NOT reading config file because it is a temp file", :config_file => file)
|
|
316
323
|
next
|
|
317
324
|
end
|
|
318
|
-
@logger.debug("Reading config file", :
|
|
325
|
+
@logger.debug("Reading config file", :config_file => file)
|
|
319
326
|
cfg = File.read(file)
|
|
320
327
|
if !cfg.ascii_only? && !cfg.valid_encoding?
|
|
321
328
|
encoding_issue_files << file
|
|
322
329
|
end
|
|
323
330
|
config << cfg + "\n"
|
|
331
|
+
if config_test?
|
|
332
|
+
@logger.debug? && @logger.debug("\nThe following is the content of a file", :config_file => file.to_s)
|
|
333
|
+
@logger.debug? && @logger.debug("\n" + cfg + "\n\n")
|
|
334
|
+
end
|
|
324
335
|
end
|
|
325
336
|
if (encoding_issue_files.any?)
|
|
326
337
|
fail("The following config files contains non-ascii characters but are not UTF-8 encoded #{encoding_issue_files}")
|
|
327
338
|
end
|
|
339
|
+
if config_test?
|
|
340
|
+
@logger.debug? && @logger.debug("\nThe following is the merged configuration")
|
|
341
|
+
@logger.debug? && @logger.debug("\n" + config + "\n\n")
|
|
342
|
+
end
|
|
328
343
|
return config
|
|
329
344
|
end # def load_config
|
|
330
345
|
|
|
@@ -35,12 +35,6 @@ module LogStash::Config::Mixin
|
|
|
35
35
|
attr_accessor :config
|
|
36
36
|
attr_accessor :original_params
|
|
37
37
|
|
|
38
|
-
CONFIGSORT = {
|
|
39
|
-
Symbol => 0,
|
|
40
|
-
String => 0,
|
|
41
|
-
Regexp => 100,
|
|
42
|
-
}
|
|
43
|
-
|
|
44
38
|
PLUGIN_VERSION_1_0_0 = LogStash::Util::PluginVersion.new(1, 0, 0)
|
|
45
39
|
PLUGIN_VERSION_0_9_0 = LogStash::Util::PluginVersion.new(0, 9, 0)
|
|
46
40
|
|
data/lib/logstash/event.rb
CHANGED
|
@@ -117,8 +117,6 @@ class LogStash::Filters::Base < LogStash::Plugin
|
|
|
117
117
|
# Optional.
|
|
118
118
|
config :periodic_flush, :validate => :boolean, :default => false
|
|
119
119
|
|
|
120
|
-
RESERVED = ["type", "tags", "exclude_tags", "include_fields", "exclude_fields", "add_tag", "remove_tag", "add_field", "remove_field", "include_any", "exclude_any"]
|
|
121
|
-
|
|
122
120
|
public
|
|
123
121
|
def initialize(params)
|
|
124
122
|
super
|
|
@@ -145,6 +143,7 @@ class LogStash::Filters::Base < LogStash::Plugin
|
|
|
145
143
|
# @return [Array<LogStash::Event] filtered events and any new events generated by the filter
|
|
146
144
|
public
|
|
147
145
|
def multi_filter(events)
|
|
146
|
+
LogStash::Util.set_thread_plugin(self)
|
|
148
147
|
result = []
|
|
149
148
|
events.each do |event|
|
|
150
149
|
unless event.cancelled?
|
data/lib/logstash/inputs/base.rb
CHANGED
|
@@ -89,11 +89,6 @@ class LogStash::Inputs::Base < LogStash::Plugin
|
|
|
89
89
|
@stop_called.value
|
|
90
90
|
end
|
|
91
91
|
|
|
92
|
-
protected
|
|
93
|
-
def to_event(raw, source)
|
|
94
|
-
raise LogStash::ThisMethodWasRemoved("LogStash::Inputs::Base#to_event - you should use codecs now instead of to_event. Not sure what this means? Get help on https://discuss.elastic.co/c/logstash")
|
|
95
|
-
end # def to_event
|
|
96
|
-
|
|
97
92
|
protected
|
|
98
93
|
def decorate(event)
|
|
99
94
|
# Only set 'type' if not already set. This is backwards-compatible behavior
|
|
@@ -1,11 +1,9 @@
|
|
|
1
1
|
# encoding: utf-8
|
|
2
|
-
require "cgi"
|
|
3
2
|
require "logstash/event"
|
|
4
3
|
require "logstash/logging"
|
|
5
4
|
require "logstash/plugin"
|
|
6
5
|
require "logstash/namespace"
|
|
7
6
|
require "logstash/config/mixin"
|
|
8
|
-
require "uri"
|
|
9
7
|
|
|
10
8
|
class LogStash::Outputs::Base < LogStash::Plugin
|
|
11
9
|
include LogStash::Config::Mixin
|
|
@@ -25,7 +23,7 @@ class LogStash::Outputs::Base < LogStash::Plugin
|
|
|
25
23
|
# Note that this setting may not be useful for all outputs.
|
|
26
24
|
config :workers, :validate => :number, :default => 1
|
|
27
25
|
|
|
28
|
-
attr_reader :worker_plugins, :worker_queue
|
|
26
|
+
attr_reader :worker_plugins, :worker_queue, :worker_threads
|
|
29
27
|
|
|
30
28
|
public
|
|
31
29
|
def workers_not_supported(message=nil)
|
|
@@ -58,13 +56,15 @@ class LogStash::Outputs::Base < LogStash::Plugin
|
|
|
58
56
|
def worker_setup
|
|
59
57
|
if @workers == 1
|
|
60
58
|
@worker_plugins = [self]
|
|
59
|
+
@worker_threads = []
|
|
61
60
|
else
|
|
62
61
|
define_singleton_method(:handle, method(:handle_worker))
|
|
63
62
|
@worker_queue = SizedQueue.new(20)
|
|
64
63
|
@worker_plugins = @workers.times.map { self.class.new(@original_params.merge("workers" => 1)) }
|
|
65
|
-
@worker_plugins.map.with_index do |plugin, i|
|
|
64
|
+
@worker_threads = @worker_plugins.map.with_index do |plugin, i|
|
|
66
65
|
Thread.new(original_params, @worker_queue) do |params, queue|
|
|
67
|
-
LogStash::Util
|
|
66
|
+
LogStash::Util.set_thread_name(">#{self.class.config_name}.#{i}")
|
|
67
|
+
LogStash::Util.set_thread_plugin(self)
|
|
68
68
|
plugin.register
|
|
69
69
|
while true
|
|
70
70
|
event = queue.pop
|
|
@@ -77,10 +77,12 @@ class LogStash::Outputs::Base < LogStash::Plugin
|
|
|
77
77
|
|
|
78
78
|
public
|
|
79
79
|
def handle(event)
|
|
80
|
+
LogStash::Util.set_thread_plugin(self)
|
|
80
81
|
receive(event)
|
|
81
82
|
end # def handle
|
|
82
83
|
|
|
83
84
|
def handle_worker(event)
|
|
85
|
+
LogStash::Util.set_thread_plugin(self)
|
|
84
86
|
@worker_queue.push(event)
|
|
85
87
|
end
|
|
86
88
|
|
data/lib/logstash/pipeline.rb
CHANGED
|
@@ -9,11 +9,11 @@ require "logstash/config/file"
|
|
|
9
9
|
require "logstash/filters/base"
|
|
10
10
|
require "logstash/inputs/base"
|
|
11
11
|
require "logstash/outputs/base"
|
|
12
|
-
require "logstash/util/reporter"
|
|
13
12
|
require "logstash/config/cpu_core_strategy"
|
|
14
13
|
require "logstash/util/defaults_printer"
|
|
14
|
+
require "logstash/shutdown_controller"
|
|
15
15
|
|
|
16
|
-
class
|
|
16
|
+
module LogStash; class Pipeline
|
|
17
17
|
attr_reader :inputs, :filters, :outputs, :input_to_filter, :filter_to_output
|
|
18
18
|
|
|
19
19
|
def initialize(configstr)
|
|
@@ -25,6 +25,7 @@ class LogStash::Pipeline
|
|
|
25
25
|
|
|
26
26
|
grammar = LogStashConfigParser.new
|
|
27
27
|
@config = grammar.parse(configstr)
|
|
28
|
+
|
|
28
29
|
if @config.nil?
|
|
29
30
|
raise LogStash::ConfigurationError, grammar.failure_reason
|
|
30
31
|
end
|
|
@@ -46,7 +47,7 @@ class LogStash::Pipeline
|
|
|
46
47
|
@filter_to_output = filters? ? SizedQueue.new(20) : @input_to_filter
|
|
47
48
|
|
|
48
49
|
@settings = {
|
|
49
|
-
"filter-workers" => LogStash::Config::CpuCoreStrategy.fifty_percent
|
|
50
|
+
"default-filter-workers" => LogStash::Config::CpuCoreStrategy.fifty_percent
|
|
50
51
|
}
|
|
51
52
|
|
|
52
53
|
# @ready requires thread safety since it is typically polled from outside the pipeline thread
|
|
@@ -59,14 +60,32 @@ class LogStash::Pipeline
|
|
|
59
60
|
end
|
|
60
61
|
|
|
61
62
|
def configure(setting, value)
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
63
|
+
@settings[setting] = value
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def safe_filter_worker_count
|
|
67
|
+
default = @settings["default-filter-workers"]
|
|
68
|
+
thread_count = @settings["filter-workers"] #override from args "-w 8" or config
|
|
69
|
+
safe_filters, unsafe_filters = @filters.partition(&:threadsafe?)
|
|
70
|
+
if unsafe_filters.any?
|
|
71
|
+
plugins = unsafe_filters.collect { |f| f.class.config_name }
|
|
72
|
+
case thread_count
|
|
73
|
+
when nil
|
|
74
|
+
# user did not specify a worker thread count
|
|
75
|
+
# warn if the default is multiple
|
|
76
|
+
@logger.warn("Defaulting filter worker threads to 1 because there are some filters that might not work with multiple worker threads",
|
|
77
|
+
:count_was => default, :filters => plugins) if default > 1
|
|
78
|
+
1 # can't allow the default value to propagate if there are unsafe filters
|
|
79
|
+
when 0, 1
|
|
80
|
+
1
|
|
81
|
+
else
|
|
82
|
+
@logger.warn("Warning: Manual override - there are filters that might not work with multiple worker threads",
|
|
83
|
+
:worker_threads => thread_count, :filters => plugins)
|
|
84
|
+
thread_count # allow user to force this even if there are unsafe filters
|
|
67
85
|
end
|
|
86
|
+
else
|
|
87
|
+
thread_count || default
|
|
68
88
|
end
|
|
69
|
-
@settings[setting] = value
|
|
70
89
|
end
|
|
71
90
|
|
|
72
91
|
def filters?
|
|
@@ -149,9 +168,14 @@ class LogStash::Pipeline
|
|
|
149
168
|
|
|
150
169
|
def start_filters
|
|
151
170
|
@filters.each(&:register)
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
171
|
+
# dynamically get thread count based on filter threadsafety
|
|
172
|
+
# moved this test to here to allow for future config reloading
|
|
173
|
+
to_start = safe_filter_worker_count
|
|
174
|
+
@filter_threads = to_start.times.collect do |i|
|
|
175
|
+
Thread.new do
|
|
176
|
+
LogStash::Util.set_thread_name("|filterworker.#{i}")
|
|
177
|
+
filterworker
|
|
178
|
+
end
|
|
155
179
|
end
|
|
156
180
|
actually_started = @filter_threads.select(&:alive?).size
|
|
157
181
|
msg = "Worker threads expected: #{to_start}, worker threads started: #{actually_started}"
|
|
@@ -175,7 +199,8 @@ class LogStash::Pipeline
|
|
|
175
199
|
end
|
|
176
200
|
|
|
177
201
|
def inputworker(plugin)
|
|
178
|
-
LogStash::Util
|
|
202
|
+
LogStash::Util.set_thread_name("<#{plugin.class.config_name}")
|
|
203
|
+
LogStash::Util.set_thread_plugin(plugin)
|
|
179
204
|
begin
|
|
180
205
|
plugin.run(@input_to_filter)
|
|
181
206
|
rescue => e
|
|
@@ -208,7 +233,6 @@ class LogStash::Pipeline
|
|
|
208
233
|
end # def inputworker
|
|
209
234
|
|
|
210
235
|
def filterworker
|
|
211
|
-
LogStash::Util.set_thread_name("|worker")
|
|
212
236
|
begin
|
|
213
237
|
while true
|
|
214
238
|
event = @input_to_filter.pop
|
|
@@ -250,6 +274,7 @@ class LogStash::Pipeline
|
|
|
250
274
|
event = @filter_to_output.pop
|
|
251
275
|
break if event == LogStash::SHUTDOWN
|
|
252
276
|
output_func(event)
|
|
277
|
+
LogStash::Util.set_thread_plugin(nil)
|
|
253
278
|
end
|
|
254
279
|
ensure
|
|
255
280
|
@outputs.each do |output|
|
|
@@ -309,4 +334,49 @@ class LogStash::Pipeline
|
|
|
309
334
|
end
|
|
310
335
|
end # flush_filters_to_output!
|
|
311
336
|
|
|
312
|
-
|
|
337
|
+
def inflight_count
|
|
338
|
+
data = {}
|
|
339
|
+
total = 0
|
|
340
|
+
|
|
341
|
+
input_to_filter = @input_to_filter.size
|
|
342
|
+
total += input_to_filter
|
|
343
|
+
filter_to_output = @filter_to_output.size
|
|
344
|
+
total += filter_to_output
|
|
345
|
+
|
|
346
|
+
data["input_to_filter"] = input_to_filter if input_to_filter > 0
|
|
347
|
+
data["filter_to_output"] = filter_to_output if filter_to_output > 0
|
|
348
|
+
|
|
349
|
+
output_worker_queues = []
|
|
350
|
+
@outputs.each do |output|
|
|
351
|
+
next unless output.worker_queue && output.worker_queue.size > 0
|
|
352
|
+
plugin_info = output.debug_info
|
|
353
|
+
size = output.worker_queue.size
|
|
354
|
+
total += size
|
|
355
|
+
plugin_info << size
|
|
356
|
+
output_worker_queues << plugin_info
|
|
357
|
+
end
|
|
358
|
+
data["output_worker_queues"] = output_worker_queues unless output_worker_queues.empty?
|
|
359
|
+
data["total"] = total
|
|
360
|
+
data
|
|
361
|
+
end
|
|
362
|
+
|
|
363
|
+
def stalling_threads
|
|
364
|
+
plugin_threads
|
|
365
|
+
.reject {|t| t["blocked_on"] } # known begnin blocking statuses
|
|
366
|
+
.each {|t| t.delete("backtrace") }
|
|
367
|
+
.each {|t| t.delete("blocked_on") }
|
|
368
|
+
.each {|t| t.delete("status") }
|
|
369
|
+
end
|
|
370
|
+
|
|
371
|
+
def plugin_threads
|
|
372
|
+
input_threads = @input_threads.select {|t| t.alive? }.map {|t| thread_info(t) }
|
|
373
|
+
filter_threads = @filter_threads.select {|t| t.alive? }.map {|t| thread_info(t) }
|
|
374
|
+
output_threads = @output_threads.select {|t| t.alive? }.map {|t| thread_info(t) }
|
|
375
|
+
output_worker_threads = @outputs.flat_map {|output| output.worker_threads }.map {|t| thread_info(t) }
|
|
376
|
+
input_threads + filter_threads + output_threads + output_worker_threads
|
|
377
|
+
end
|
|
378
|
+
|
|
379
|
+
def thread_info(thread)
|
|
380
|
+
LogStash::Util.thread_info(thread)
|
|
381
|
+
end
|
|
382
|
+
end; end
|
data/lib/logstash/plugin.rb
CHANGED
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
|
|
3
|
+
module LogStash
|
|
4
|
+
class ShutdownController
|
|
5
|
+
|
|
6
|
+
CHECK_EVERY = 1 # second
|
|
7
|
+
REPORT_EVERY = 5 # checks
|
|
8
|
+
ABORT_AFTER = 3 # stalled reports
|
|
9
|
+
|
|
10
|
+
attr_reader :cycle_period, :report_every, :abort_threshold
|
|
11
|
+
|
|
12
|
+
def initialize(pipeline, cycle_period=CHECK_EVERY, report_every=REPORT_EVERY, abort_threshold=ABORT_AFTER)
|
|
13
|
+
@pipeline = pipeline
|
|
14
|
+
@cycle_period = cycle_period
|
|
15
|
+
@report_every = report_every
|
|
16
|
+
@abort_threshold = abort_threshold
|
|
17
|
+
@reports = []
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def self.unsafe_shutdown=(boolean)
|
|
21
|
+
@unsafe_shutdown = boolean
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def self.unsafe_shutdown?
|
|
25
|
+
@unsafe_shutdown
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def self.logger=(logger)
|
|
29
|
+
@logger = logger
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def self.logger
|
|
33
|
+
@logger ||= Cabin::Channel.get(LogStash)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def self.start(pipeline, cycle_period=CHECK_EVERY, report_every=REPORT_EVERY, abort_threshold=ABORT_AFTER)
|
|
37
|
+
controller = self.new(pipeline, cycle_period, report_every, abort_threshold)
|
|
38
|
+
Thread.new(controller) { |controller| controller.start }
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def logger
|
|
42
|
+
self.class.logger
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def start
|
|
46
|
+
sleep(@cycle_period)
|
|
47
|
+
cycle_number = 0
|
|
48
|
+
stalled_count = 0
|
|
49
|
+
Stud.interval(@cycle_period) do
|
|
50
|
+
@reports << Report.from_pipeline(@pipeline)
|
|
51
|
+
@reports.delete_at(0) if @reports.size > @report_every # expire old report
|
|
52
|
+
if cycle_number == (@report_every - 1) # it's report time!
|
|
53
|
+
logger.warn(@reports.last.to_hash)
|
|
54
|
+
|
|
55
|
+
if shutdown_stalled?
|
|
56
|
+
logger.error("The shutdown process appears to be stalled due to busy or blocked plugins. Check the logs for more information.") if stalled_count == 0
|
|
57
|
+
stalled_count += 1
|
|
58
|
+
|
|
59
|
+
if self.class.unsafe_shutdown? && @abort_threshold == stalled_count
|
|
60
|
+
logger.fatal("Forcefully quitting logstash..")
|
|
61
|
+
force_exit()
|
|
62
|
+
break
|
|
63
|
+
end
|
|
64
|
+
else
|
|
65
|
+
stalled_count = 0
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
cycle_number = (cycle_number + 1) % @report_every
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
# A pipeline shutdown is stalled if
|
|
73
|
+
# * at least REPORT_EVERY reports have been created
|
|
74
|
+
# * the inflight event count is in monotonically increasing
|
|
75
|
+
# * there are worker threads running which aren't blocked on SizedQueue pop/push
|
|
76
|
+
# * the stalled thread list is constant in the previous REPORT_EVERY reports
|
|
77
|
+
def shutdown_stalled?
|
|
78
|
+
return false unless @reports.size == @report_every #
|
|
79
|
+
# is stalled if inflight count is either constant or increasing
|
|
80
|
+
stalled_event_count = @reports.each_cons(2).all? do |prev_report, next_report|
|
|
81
|
+
prev_report.inflight_count["total"] <= next_report.inflight_count["total"]
|
|
82
|
+
end
|
|
83
|
+
if stalled_event_count
|
|
84
|
+
@reports.each_cons(2).all? do |prev_report, next_report|
|
|
85
|
+
prev_report.stalling_threads == next_report.stalling_threads
|
|
86
|
+
end
|
|
87
|
+
else
|
|
88
|
+
false
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def force_exit
|
|
93
|
+
exit(-1)
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
class Report
|
|
98
|
+
|
|
99
|
+
attr_reader :inflight_count, :stalling_threads
|
|
100
|
+
|
|
101
|
+
def self.from_pipeline(pipeline)
|
|
102
|
+
new(pipeline.inflight_count, pipeline.stalling_threads)
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def initialize(inflight_count, stalling_threads)
|
|
106
|
+
@inflight_count = inflight_count
|
|
107
|
+
@stalling_threads = format_threads_by_plugin(stalling_threads)
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def to_hash
|
|
111
|
+
{
|
|
112
|
+
"INFLIGHT_EVENT_COUNT" => @inflight_count,
|
|
113
|
+
"STALLING_THREADS" => @stalling_threads
|
|
114
|
+
}
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
def format_threads_by_plugin(stalling_threads)
|
|
118
|
+
stalled_plugins = {}
|
|
119
|
+
stalling_threads.each do |thr|
|
|
120
|
+
key = (thr.delete("plugin") || "other")
|
|
121
|
+
stalled_plugins[key] ||= []
|
|
122
|
+
stalled_plugins[key] << thr
|
|
123
|
+
end
|
|
124
|
+
stalled_plugins
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
end
|