logstash-core 2.1.3-java → 2.2.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.
Potentially problematic release.
This version of logstash-core might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/lib/logstash-core.rb +1 -3
- data/lib/logstash-core/logstash-core.rb +3 -0
- data/lib/logstash-core/version.rb +8 -0
- data/lib/logstash/agent.rb +48 -20
- data/lib/logstash/codecs/base.rb +2 -2
- data/lib/logstash/config/config_ast.rb +8 -3
- data/lib/logstash/environment.rb +0 -16
- data/lib/logstash/filters/base.rb +9 -5
- data/lib/logstash/inputs/base.rb +1 -1
- data/lib/logstash/output_delegator.rb +150 -0
- data/lib/logstash/outputs/base.rb +37 -40
- data/lib/logstash/pipeline.rb +259 -178
- data/lib/logstash/pipeline_reporter.rb +114 -0
- data/lib/logstash/plugin.rb +1 -1
- data/lib/logstash/{shutdown_controller.rb → shutdown_watcher.rb} +10 -37
- data/lib/logstash/util.rb +17 -0
- data/lib/logstash/util/decorators.rb +14 -7
- data/lib/logstash/util/worker_threads_default_printer.rb +4 -4
- data/lib/logstash/util/wrapped_synchronous_queue.rb +41 -0
- data/lib/logstash/version.rb +10 -2
- data/locales/en.yml +8 -3
- data/logstash-core.gemspec +5 -3
- data/spec/{core/conditionals_spec.rb → conditionals_spec.rb} +0 -0
- data/spec/{core/config_spec.rb → logstash/config/config_ast_spec.rb} +0 -0
- data/spec/{core/config_cpu_core_strategy_spec.rb → logstash/config/cpu_core_strategy_spec.rb} +0 -0
- data/spec/{core/config_defaults_spec.rb → logstash/config/defaults_spec.rb} +0 -0
- data/spec/{core/config_mixin_spec.rb → logstash/config/mixin_spec.rb} +0 -0
- data/spec/{core → logstash}/environment_spec.rb +0 -0
- data/spec/{filters → logstash/filters}/base_spec.rb +0 -0
- data/spec/{inputs → logstash/inputs}/base_spec.rb +0 -0
- data/spec/{lib/logstash → logstash}/java_integration_spec.rb +0 -0
- data/spec/{util → logstash}/json_spec.rb +0 -0
- data/spec/logstash/output_delegator_spec.rb +126 -0
- data/spec/logstash/outputs/base_spec.rb +40 -0
- data/spec/logstash/pipeline_reporter_spec.rb +85 -0
- data/spec/{core → logstash}/pipeline_spec.rb +128 -16
- data/spec/{core → logstash}/plugin_spec.rb +47 -1
- data/spec/logstash/runner_spec.rb +68 -0
- data/spec/{core/shutdown_controller_spec.rb → logstash/shutdown_watcher_spec.rb} +17 -11
- data/spec/{util → logstash/util}/buftok_spec.rb +0 -0
- data/spec/{util → logstash/util}/charset_spec.rb +0 -0
- data/spec/{util → logstash/util}/defaults_printer_spec.rb +4 -4
- data/spec/{util → logstash/util}/java_version_spec.rb +0 -0
- data/spec/{util → logstash/util}/plugin_version_spec.rb +0 -0
- data/spec/{util → logstash/util}/unicode_trimmer_spec.rb +0 -0
- data/spec/{util → logstash/util}/worker_threads_default_printer_spec.rb +8 -8
- data/spec/logstash/util/wrapped_synchronous_queue_spec.rb +28 -0
- data/spec/{util_spec.rb → logstash/util_spec.rb} +0 -0
- metadata +74 -81
- data/lib/logstash/event.rb +0 -275
- data/lib/logstash/patches/bundler.rb +0 -36
- data/lib/logstash/sized_queue.rb +0 -8
- data/lib/logstash/string_interpolation.rb +0 -140
- data/lib/logstash/timestamp.rb +0 -97
- data/lib/logstash/util/accessors.rb +0 -123
- data/spec/core/event_spec.rb +0 -518
- data/spec/core/runner_spec.rb +0 -40
- data/spec/core/timestamp_spec.rb +0 -84
- data/spec/coverage_helper.rb +0 -24
- data/spec/lib/logstash/bundler_spec.rb +0 -121
- data/spec/license_spec.rb +0 -67
- data/spec/outputs/base_spec.rb +0 -26
- data/spec/plugin_manager/install_spec.rb +0 -28
- data/spec/plugin_manager/update_spec.rb +0 -39
- data/spec/plugin_manager/util_spec.rb +0 -71
- data/spec/spec_helper.rb +0 -11
- data/spec/util/accessors_spec.rb +0 -170
- data/spec/util/compress_spec.rb +0 -121
- data/spec/util/gemfile_spec.rb +0 -212
- data/spec/util/retryable_spec.rb +0 -139
data/lib/logstash/pipeline.rb
CHANGED
@@ -11,21 +11,38 @@ require "logstash/inputs/base"
|
|
11
11
|
require "logstash/outputs/base"
|
12
12
|
require "logstash/config/cpu_core_strategy"
|
13
13
|
require "logstash/util/defaults_printer"
|
14
|
-
require "logstash/
|
14
|
+
require "logstash/shutdown_watcher"
|
15
|
+
require "logstash/util/wrapped_synchronous_queue"
|
16
|
+
require "logstash/pipeline_reporter"
|
17
|
+
require "logstash/output_delegator"
|
15
18
|
|
16
19
|
module LogStash; class Pipeline
|
17
|
-
attr_reader :inputs, :filters, :outputs, :
|
18
|
-
|
19
|
-
|
20
|
+
attr_reader :inputs, :filters, :outputs, :worker_threads, :events_consumed, :events_filtered, :reporter, :pipeline_id, :logger
|
21
|
+
|
22
|
+
DEFAULT_SETTINGS = {
|
23
|
+
:default_pipeline_workers => LogStash::Config::CpuCoreStrategy.maximum,
|
24
|
+
:pipeline_batch_size => 125,
|
25
|
+
:pipeline_batch_delay => 5, # in milliseconds
|
26
|
+
:flush_interval => 5, # in seconds
|
27
|
+
:flush_timeout_interval => 60 # in seconds
|
28
|
+
}
|
29
|
+
MAX_INFLIGHT_WARN_THRESHOLD = 10_000
|
30
|
+
|
31
|
+
def initialize(config_str, settings = {})
|
32
|
+
@pipeline_id = settings[:pipeline_id] || self.object_id
|
20
33
|
@logger = Cabin::Channel.get(LogStash)
|
34
|
+
@settings = DEFAULT_SETTINGS.clone
|
35
|
+
settings.each {|setting, value| configure(setting, value) }
|
36
|
+
@reporter = LogStash::PipelineReporter.new(@logger, self)
|
21
37
|
|
22
38
|
@inputs = nil
|
23
39
|
@filters = nil
|
24
40
|
@outputs = nil
|
25
41
|
|
26
|
-
|
27
|
-
@config = grammar.parse(configstr)
|
42
|
+
@worker_threads = []
|
28
43
|
|
44
|
+
grammar = LogStashConfigParser.new
|
45
|
+
@config = grammar.parse(config_str)
|
29
46
|
if @config.nil?
|
30
47
|
raise LogStash::ConfigurationError, grammar.failure_reason
|
31
48
|
end
|
@@ -42,18 +59,21 @@ module LogStash; class Pipeline
|
|
42
59
|
raise
|
43
60
|
end
|
44
61
|
|
45
|
-
@
|
46
|
-
|
47
|
-
@
|
48
|
-
|
49
|
-
@settings = {
|
50
|
-
"default-filter-workers" => LogStash::Config::CpuCoreStrategy.fifty_percent
|
51
|
-
}
|
62
|
+
@input_queue = LogStash::Util::WrappedSynchronousQueue.new
|
63
|
+
@events_filtered = Concurrent::AtomicFixnum.new(0)
|
64
|
+
@events_consumed = Concurrent::AtomicFixnum.new(0)
|
52
65
|
|
66
|
+
# We generally only want one thread at a time able to access pop/take/poll operations
|
67
|
+
# from this queue. We also depend on this to be able to block consumers while we snapshot
|
68
|
+
# in-flight buffers
|
69
|
+
@input_queue_pop_mutex = Mutex.new
|
70
|
+
@input_threads = []
|
53
71
|
# @ready requires thread safety since it is typically polled from outside the pipeline thread
|
54
72
|
@ready = Concurrent::AtomicBoolean.new(false)
|
55
|
-
@
|
56
|
-
@
|
73
|
+
@running = Concurrent::AtomicBoolean.new(false)
|
74
|
+
@flushing = Concurrent::AtomicReference.new(false)
|
75
|
+
|
76
|
+
start_flusher
|
57
77
|
end # def initialize
|
58
78
|
|
59
79
|
def ready?
|
@@ -64,24 +84,29 @@ module LogStash; class Pipeline
|
|
64
84
|
@settings[setting] = value
|
65
85
|
end
|
66
86
|
|
67
|
-
def
|
68
|
-
default =
|
69
|
-
thread_count = @settings[
|
87
|
+
def safe_pipeline_worker_count
|
88
|
+
default = DEFAULT_SETTINGS[:default_pipeline_workers]
|
89
|
+
thread_count = @settings[:pipeline_workers] #override from args "-w 8" or config
|
70
90
|
safe_filters, unsafe_filters = @filters.partition(&:threadsafe?)
|
91
|
+
|
71
92
|
if unsafe_filters.any?
|
72
93
|
plugins = unsafe_filters.collect { |f| f.class.config_name }
|
73
94
|
case thread_count
|
74
95
|
when nil
|
75
96
|
# user did not specify a worker thread count
|
76
97
|
# warn if the default is multiple
|
77
|
-
|
78
|
-
|
98
|
+
|
99
|
+
if default > 1
|
100
|
+
@logger.warn("Defaulting pipeline worker threads to 1 because there are some filters that might not work with multiple worker threads",
|
101
|
+
:count_was => default, :filters => plugins)
|
102
|
+
end
|
103
|
+
|
79
104
|
1 # can't allow the default value to propagate if there are unsafe filters
|
80
105
|
when 0, 1
|
81
106
|
1
|
82
107
|
else
|
83
108
|
@logger.warn("Warning: Manual override - there are filters that might not work with multiple worker threads",
|
84
|
-
|
109
|
+
:worker_threads => thread_count, :filters => plugins)
|
85
110
|
thread_count # allow user to force this even if there are unsafe filters
|
86
111
|
end
|
87
112
|
else
|
@@ -94,31 +119,25 @@ module LogStash; class Pipeline
|
|
94
119
|
end
|
95
120
|
|
96
121
|
def run
|
122
|
+
LogStash::Util.set_thread_name("[#{pipeline_id}]-pipeline-manager")
|
97
123
|
@logger.terminal(LogStash::Util::DefaultsPrinter.print(@settings))
|
98
124
|
|
99
|
-
|
100
|
-
start_inputs
|
101
|
-
start_filters if filters?
|
102
|
-
start_outputs
|
103
|
-
ensure
|
104
|
-
# it is important to garantee @ready to be true after the startup sequence has been completed
|
105
|
-
# to potentially unblock the shutdown method which may be waiting on @ready to proceed
|
106
|
-
@ready.make_true
|
107
|
-
end
|
125
|
+
start_workers
|
108
126
|
|
109
127
|
@logger.info("Pipeline started")
|
110
128
|
@logger.terminal("Logstash startup completed")
|
111
129
|
|
130
|
+
# Block until all inputs have stopped
|
131
|
+
# Generally this happens if SIGINT is sent and `shutdown` is called from an external thread
|
132
|
+
|
133
|
+
@running.make_true
|
112
134
|
wait_inputs
|
135
|
+
@running.make_false
|
113
136
|
|
114
|
-
|
115
|
-
shutdown_filters
|
116
|
-
wait_filters
|
117
|
-
flush_filters_to_output!(:final => true)
|
118
|
-
end
|
137
|
+
@logger.info("Input plugins stopped! Will shutdown filter/output workers.")
|
119
138
|
|
120
|
-
|
121
|
-
|
139
|
+
shutdown_flusher
|
140
|
+
shutdown_workers
|
122
141
|
|
123
142
|
@logger.info("Pipeline shutdown complete.")
|
124
143
|
@logger.terminal("Logstash shutdown completed")
|
@@ -127,27 +146,147 @@ module LogStash; class Pipeline
|
|
127
146
|
return 0
|
128
147
|
end # def run
|
129
148
|
|
130
|
-
def
|
131
|
-
@
|
149
|
+
def start_workers
|
150
|
+
@inflight_batches = {}
|
151
|
+
|
152
|
+
@worker_threads.clear # In case we're restarting the pipeline
|
153
|
+
begin
|
154
|
+
start_inputs
|
155
|
+
@outputs.each {|o| o.register }
|
156
|
+
@filters.each {|f| f.register}
|
157
|
+
|
158
|
+
pipeline_workers = safe_pipeline_worker_count
|
159
|
+
batch_size = @settings[:pipeline_batch_size]
|
160
|
+
batch_delay = @settings[:pipeline_batch_delay]
|
161
|
+
max_inflight = batch_size * pipeline_workers
|
162
|
+
@logger.info("Starting pipeline",
|
163
|
+
:id => self.pipeline_id,
|
164
|
+
:pipeline_workers => pipeline_workers,
|
165
|
+
:batch_size => batch_size,
|
166
|
+
:batch_delay => batch_delay,
|
167
|
+
:max_inflight => max_inflight)
|
168
|
+
if max_inflight > MAX_INFLIGHT_WARN_THRESHOLD
|
169
|
+
@logger.warn "CAUTION: Recommended inflight events max exceeded! Logstash will run with up to #{max_inflight} events in memory in your current configuration. If your message sizes are large this may cause instability with the default heap size. Please consider setting a non-standard heap size, changing the batch size (currently #{batch_size}), or changing the number of pipeline workers (currently #{pipeline_workers})"
|
170
|
+
end
|
171
|
+
|
172
|
+
pipeline_workers.times do |t|
|
173
|
+
@worker_threads << Thread.new do
|
174
|
+
LogStash::Util.set_thread_name("[#{pipeline_id}]>worker#{t}")
|
175
|
+
worker_loop(batch_size, batch_delay)
|
176
|
+
end
|
177
|
+
end
|
178
|
+
ensure
|
179
|
+
# it is important to garantee @ready to be true after the startup sequence has been completed
|
180
|
+
# to potentially unblock the shutdown method which may be waiting on @ready to proceed
|
181
|
+
@ready.make_true
|
182
|
+
end
|
132
183
|
end
|
133
184
|
|
134
|
-
|
135
|
-
|
136
|
-
|
185
|
+
# Main body of what a worker thread does
|
186
|
+
# Repeatedly takes batches off the queu, filters, then outputs them
|
187
|
+
def worker_loop(batch_size, batch_delay)
|
188
|
+
running = true
|
189
|
+
|
190
|
+
while running
|
191
|
+
# To understand the purpose behind this synchronize please read the body of take_batch
|
192
|
+
input_batch, signal = @input_queue_pop_mutex.synchronize { take_batch(batch_size, batch_delay) }
|
193
|
+
running = false if signal == LogStash::SHUTDOWN
|
194
|
+
|
195
|
+
@events_consumed.increment(input_batch.size)
|
196
|
+
|
197
|
+
filtered_batch = filter_batch(input_batch)
|
198
|
+
|
199
|
+
if signal # Flush on SHUTDOWN or FLUSH
|
200
|
+
flush_options = (signal == LogStash::SHUTDOWN) ? {:final => true} : {}
|
201
|
+
flush_filters_to_batch(filtered_batch, flush_options)
|
202
|
+
end
|
203
|
+
|
204
|
+
@events_filtered.increment(filtered_batch.size)
|
205
|
+
|
206
|
+
output_batch(filtered_batch)
|
207
|
+
|
208
|
+
inflight_batches_synchronize { set_current_thread_inflight_batch(nil) }
|
209
|
+
end
|
137
210
|
end
|
138
211
|
|
139
|
-
def
|
140
|
-
|
212
|
+
def take_batch(batch_size, batch_delay)
|
213
|
+
batch = []
|
214
|
+
# Since this is externally synchronized in `worker_look` wec can guarantee that the visibility of an insight batch
|
215
|
+
# guaranteed to be a full batch not a partial batch
|
216
|
+
set_current_thread_inflight_batch(batch)
|
217
|
+
|
218
|
+
signal = false
|
219
|
+
batch_size.times do |t|
|
220
|
+
event = (t == 0) ? @input_queue.take : @input_queue.poll(batch_delay)
|
221
|
+
|
222
|
+
if event.nil?
|
223
|
+
next
|
224
|
+
elsif event == LogStash::SHUTDOWN || event == LogStash::FLUSH
|
225
|
+
# We MUST break here. If a batch consumes two SHUTDOWN events
|
226
|
+
# then another worker may have its SHUTDOWN 'stolen', thus blocking
|
227
|
+
# the pipeline. We should stop doing work after flush as well.
|
228
|
+
signal = event
|
229
|
+
break
|
230
|
+
else
|
231
|
+
batch << event
|
232
|
+
end
|
233
|
+
end
|
234
|
+
|
235
|
+
[batch, signal]
|
141
236
|
end
|
142
237
|
|
143
|
-
def
|
144
|
-
|
145
|
-
|
238
|
+
def filter_batch(batch)
|
239
|
+
batch.reduce([]) do |acc,e|
|
240
|
+
if e.is_a?(LogStash::Event)
|
241
|
+
filtered = filter_func(e)
|
242
|
+
filtered.each {|fe| acc << fe unless fe.cancelled?}
|
243
|
+
end
|
244
|
+
acc
|
245
|
+
end
|
246
|
+
rescue Exception => e
|
247
|
+
# Plugins authors should manage their own exceptions in the plugin code
|
248
|
+
# but if an exception is raised up to the worker thread they are considered
|
249
|
+
# fatal and logstash will not recover from this situation.
|
250
|
+
#
|
251
|
+
# Users need to check their configuration or see if there is a bug in the
|
252
|
+
# plugin.
|
253
|
+
@logger.error("Exception in pipelineworker, the pipeline stopped processing new events, please check your filter configuration and restart Logstash.",
|
254
|
+
"exception" => e, "backtrace" => e.backtrace)
|
255
|
+
raise
|
256
|
+
end
|
257
|
+
|
258
|
+
# Take an array of events and send them to the correct output
|
259
|
+
def output_batch(batch)
|
260
|
+
# Build a mapping of { output_plugin => [events...]}
|
261
|
+
outputs_events = batch.reduce(Hash.new { |h, k| h[k] = [] }) do |acc, event|
|
262
|
+
# We ask the AST to tell us which outputs to send each event to
|
263
|
+
# Then, we stick it in the correct bin
|
264
|
+
|
265
|
+
# output_func should never return anything other than an Array but we have lots of legacy specs
|
266
|
+
# that monkeypatch it and return nil. We can deprecate "|| []" after fixing these specs
|
267
|
+
outputs_for_event = output_func(event) || []
|
268
|
+
|
269
|
+
outputs_for_event.each { |output| acc[output] << event }
|
270
|
+
acc
|
271
|
+
end
|
272
|
+
|
273
|
+
# Now that we have our output to event mapping we can just invoke each output
|
274
|
+
# once with its list of events
|
275
|
+
outputs_events.each { |output, events| output.multi_receive(events) }
|
146
276
|
end
|
147
277
|
|
148
|
-
def
|
149
|
-
|
150
|
-
|
278
|
+
def set_current_thread_inflight_batch(batch)
|
279
|
+
@inflight_batches[Thread.current] = batch
|
280
|
+
end
|
281
|
+
|
282
|
+
def inflight_batches_synchronize
|
283
|
+
@input_queue_pop_mutex.synchronize do
|
284
|
+
yield(@inflight_batches)
|
285
|
+
end
|
286
|
+
end
|
287
|
+
|
288
|
+
def wait_inputs
|
289
|
+
@input_threads.each(&:join)
|
151
290
|
end
|
152
291
|
|
153
292
|
def start_inputs
|
@@ -167,45 +306,15 @@ module LogStash; class Pipeline
|
|
167
306
|
end
|
168
307
|
end
|
169
308
|
|
170
|
-
def start_filters
|
171
|
-
@filters.each(&:register)
|
172
|
-
# dynamically get thread count based on filter threadsafety
|
173
|
-
# moved this test to here to allow for future config reloading
|
174
|
-
to_start = safe_filter_worker_count
|
175
|
-
@filter_threads = to_start.times.collect do |i|
|
176
|
-
Thread.new do
|
177
|
-
LogStash::Util.set_thread_name("|filterworker.#{i}")
|
178
|
-
filterworker
|
179
|
-
end
|
180
|
-
end
|
181
|
-
actually_started = @filter_threads.select(&:alive?).size
|
182
|
-
msg = "Worker threads expected: #{to_start}, worker threads started: #{actually_started}"
|
183
|
-
if actually_started < to_start
|
184
|
-
@logger.warn(msg)
|
185
|
-
else
|
186
|
-
@logger.info(msg)
|
187
|
-
end
|
188
|
-
@flusher_thread = Thread.new { Stud.interval(5) { @input_to_filter.push(LogStash::FLUSH) } }
|
189
|
-
end
|
190
|
-
|
191
|
-
def start_outputs
|
192
|
-
@outputs.each(&:register)
|
193
|
-
@output_threads = [
|
194
|
-
Thread.new { outputworker }
|
195
|
-
]
|
196
|
-
end
|
197
|
-
|
198
309
|
def start_input(plugin)
|
199
310
|
@input_threads << Thread.new { inputworker(plugin) }
|
200
311
|
end
|
201
312
|
|
202
313
|
def inputworker(plugin)
|
203
|
-
LogStash::Util
|
204
|
-
LogStash::Util.set_thread_plugin(plugin)
|
314
|
+
LogStash::Util::set_thread_name("[#{pipeline_id}]<#{plugin.class.config_name}")
|
205
315
|
begin
|
206
|
-
plugin.run(@
|
316
|
+
plugin.run(@input_queue)
|
207
317
|
rescue => e
|
208
|
-
# if plugin is stopping, ignore uncatched exceptions and exit worker
|
209
318
|
if plugin.stop?
|
210
319
|
@logger.debug("Input plugin raised exception during shutdown, ignoring it.",
|
211
320
|
:plugin => plugin.class.config_name, :exception => e,
|
@@ -233,56 +342,6 @@ module LogStash; class Pipeline
|
|
233
342
|
end
|
234
343
|
end # def inputworker
|
235
344
|
|
236
|
-
def filterworker
|
237
|
-
begin
|
238
|
-
while true
|
239
|
-
event = @input_to_filter.pop
|
240
|
-
|
241
|
-
case event
|
242
|
-
when LogStash::Event
|
243
|
-
# filter_func returns all filtered events, including cancelled ones
|
244
|
-
filter_func(event).each { |e| @filter_to_output.push(e) unless e.cancelled? }
|
245
|
-
when LogStash::FlushEvent
|
246
|
-
# handle filter flushing here so that non threadsafe filters (thus only running one filterworker)
|
247
|
-
# don't have to deal with thread safety implementing the flush method
|
248
|
-
flush_filters_to_output!
|
249
|
-
when LogStash::ShutdownEvent
|
250
|
-
# pass it down to any other filterworker and stop this worker
|
251
|
-
@input_to_filter.push(event)
|
252
|
-
break
|
253
|
-
end
|
254
|
-
end
|
255
|
-
rescue Exception => e
|
256
|
-
# Plugins authors should manage their own exceptions in the plugin code
|
257
|
-
# but if an exception is raised up to the worker thread they are considered
|
258
|
-
# fatal and logstash will not recover from this situation.
|
259
|
-
#
|
260
|
-
# Users need to check their configuration or see if there is a bug in the
|
261
|
-
# plugin.
|
262
|
-
@logger.error("Exception in filterworker, the pipeline stopped processing new events, please check your filter configuration and restart Logstash.",
|
263
|
-
"exception" => e, "backtrace" => e.backtrace)
|
264
|
-
raise
|
265
|
-
ensure
|
266
|
-
@filters.each(&:do_close)
|
267
|
-
end
|
268
|
-
end # def filterworker
|
269
|
-
|
270
|
-
def outputworker
|
271
|
-
LogStash::Util.set_thread_name(">output")
|
272
|
-
@outputs.each(&:worker_setup)
|
273
|
-
|
274
|
-
while true
|
275
|
-
event = @filter_to_output.pop
|
276
|
-
break if event == LogStash::SHUTDOWN
|
277
|
-
output_func(event)
|
278
|
-
LogStash::Util.set_thread_plugin(nil)
|
279
|
-
end
|
280
|
-
ensure
|
281
|
-
@outputs.each do |output|
|
282
|
-
output.worker_plugins.each(&:do_close)
|
283
|
-
end
|
284
|
-
end # def outputworker
|
285
|
-
|
286
345
|
# initiate the pipeline shutdown sequence
|
287
346
|
# this method is intended to be called from outside the pipeline thread
|
288
347
|
# @param before_stop [Proc] code block called before performing stop operation on input plugins
|
@@ -296,13 +355,44 @@ module LogStash; class Pipeline
|
|
296
355
|
|
297
356
|
before_stop.call if block_given?
|
298
357
|
|
358
|
+
@logger.info "Closing inputs"
|
299
359
|
@inputs.each(&:do_stop)
|
360
|
+
@logger.info "Closed inputs"
|
300
361
|
end # def shutdown
|
301
362
|
|
363
|
+
# After `shutdown` is called from an external thread this is called from the main thread to
|
364
|
+
# tell the worker threads to stop and then block until they've fully stopped
|
365
|
+
# This also stops all filter and output plugins
|
366
|
+
def shutdown_workers
|
367
|
+
# Each worker thread will receive this exactly once!
|
368
|
+
@worker_threads.each do |t|
|
369
|
+
@logger.debug("Pushing shutdown", :thread => t)
|
370
|
+
@input_queue.push(LogStash::SHUTDOWN)
|
371
|
+
end
|
372
|
+
|
373
|
+
@worker_threads.each do |t|
|
374
|
+
@logger.debug("Shutdown waiting for worker thread #{t}")
|
375
|
+
t.join
|
376
|
+
end
|
377
|
+
|
378
|
+
@filters.each(&:do_close)
|
379
|
+
@outputs.each(&:do_close)
|
380
|
+
end
|
381
|
+
|
302
382
|
def plugin(plugin_type, name, *args)
|
303
383
|
args << {} if args.empty?
|
384
|
+
|
304
385
|
klass = LogStash::Plugin.lookup(plugin_type, name)
|
305
|
-
|
386
|
+
|
387
|
+
if plugin_type == "output"
|
388
|
+
LogStash::OutputDelegator.new(@logger, klass, default_output_workers, *args)
|
389
|
+
else
|
390
|
+
klass.new(*args)
|
391
|
+
end
|
392
|
+
end
|
393
|
+
|
394
|
+
def default_output_workers
|
395
|
+
@settings[:pipeline_workers] || @settings[:default_pipeline_workers]
|
306
396
|
end
|
307
397
|
|
308
398
|
# for backward compatibility in devutils for the rspec helpers, this method is not used
|
@@ -312,6 +402,7 @@ module LogStash; class Pipeline
|
|
312
402
|
filter_func(event).each { |e| block.call(e) }
|
313
403
|
end
|
314
404
|
|
405
|
+
|
315
406
|
# perform filters flush and yeild flushed event to the passed block
|
316
407
|
# @param options [Hash]
|
317
408
|
# @option options [Boolean] :final => true to signal a final shutdown flush
|
@@ -323,61 +414,51 @@ module LogStash; class Pipeline
|
|
323
414
|
end
|
324
415
|
end
|
325
416
|
|
417
|
+
def start_flusher
|
418
|
+
@flusher_thread = Thread.new do
|
419
|
+
while Stud.stoppable_sleep(5, 0.1) { @running.false? }
|
420
|
+
flush
|
421
|
+
break if @running.false?
|
422
|
+
end
|
423
|
+
end
|
424
|
+
end
|
425
|
+
|
426
|
+
def shutdown_flusher
|
427
|
+
@flusher_thread.join
|
428
|
+
end
|
429
|
+
|
430
|
+
def flush
|
431
|
+
if @flushing.compare_and_set(false, true)
|
432
|
+
@logger.debug? && @logger.debug("Pushing flush onto pipeline")
|
433
|
+
@input_queue.push(LogStash::FLUSH)
|
434
|
+
end
|
435
|
+
end
|
436
|
+
|
326
437
|
# perform filters flush into the output queue
|
327
438
|
# @param options [Hash]
|
328
439
|
# @option options [Boolean] :final => true to signal a final shutdown flush
|
329
|
-
def
|
440
|
+
def flush_filters_to_batch(batch, options = {})
|
330
441
|
flush_filters(options) do |event|
|
331
442
|
unless event.cancelled?
|
332
443
|
@logger.debug? and @logger.debug("Pushing flushed events", :event => event)
|
333
|
-
|
444
|
+
batch << event
|
334
445
|
end
|
335
446
|
end
|
336
|
-
end # flush_filters_to_output!
|
337
|
-
|
338
|
-
def inflight_count
|
339
|
-
data = {}
|
340
|
-
total = 0
|
341
|
-
|
342
|
-
input_to_filter = @input_to_filter.size
|
343
|
-
total += input_to_filter
|
344
|
-
filter_to_output = @filter_to_output.size
|
345
|
-
total += filter_to_output
|
346
|
-
|
347
|
-
data["input_to_filter"] = input_to_filter if input_to_filter > 0
|
348
|
-
data["filter_to_output"] = filter_to_output if filter_to_output > 0
|
349
|
-
|
350
|
-
output_worker_queues = []
|
351
|
-
@outputs.each do |output|
|
352
|
-
next unless output.worker_queue && output.worker_queue.size > 0
|
353
|
-
plugin_info = output.debug_info
|
354
|
-
size = output.worker_queue.size
|
355
|
-
total += size
|
356
|
-
plugin_info << size
|
357
|
-
output_worker_queues << plugin_info
|
358
|
-
end
|
359
|
-
data["output_worker_queues"] = output_worker_queues unless output_worker_queues.empty?
|
360
|
-
data["total"] = total
|
361
|
-
data
|
362
|
-
end
|
363
447
|
|
364
|
-
|
365
|
-
|
366
|
-
.reject {|t| t["blocked_on"] } # known begnin blocking statuses
|
367
|
-
.each {|t| t.delete("backtrace") }
|
368
|
-
.each {|t| t.delete("blocked_on") }
|
369
|
-
.each {|t| t.delete("status") }
|
370
|
-
end
|
448
|
+
@flushing.set(false)
|
449
|
+
end # flush_filters_to_output!
|
371
450
|
|
372
|
-
def
|
373
|
-
input_threads = @input_threads.select {|t| t.alive? }
|
374
|
-
|
375
|
-
|
376
|
-
output_worker_threads = @outputs.flat_map {|output| output.worker_threads }.map {|t| thread_info(t) }
|
377
|
-
input_threads + filter_threads + output_threads + output_worker_threads
|
451
|
+
def plugin_threads_info
|
452
|
+
input_threads = @input_threads.select {|t| t.alive? }
|
453
|
+
worker_threads = @worker_threads.select {|t| t.alive? }
|
454
|
+
(input_threads + worker_threads).map {|t| LogStash::Util.thread_info(t) }
|
378
455
|
end
|
379
456
|
|
380
|
-
def
|
381
|
-
|
457
|
+
def stalling_threads_info
|
458
|
+
plugin_threads_info
|
459
|
+
.reject {|t| t["blocked_on"] } # known benign blocking statuses
|
460
|
+
.each {|t| t.delete("backtrace") }
|
461
|
+
.each {|t| t.delete("blocked_on") }
|
462
|
+
.each {|t| t.delete("status") }
|
382
463
|
end
|
383
|
-
end
|
464
|
+
end end
|