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

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


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

@@ -61,7 +61,7 @@ class OpenSSL::SSL::SSLContext
61
61
  # For more details see: https://github.com/elastic/logstash/issues/3657
62
62
  remove_const(:DEFAULT_PARAMS) if const_defined?(:DEFAULT_PARAMS)
63
63
  DEFAULT_PARAMS = {
64
- :ssl_version => "SSLv23",
64
+ :ssl_version => "TLS",
65
65
  :ciphers => MOZILLA_INTERMEDIATE_CIPHERS,
66
66
  :options => __default_options # Not a constant because it's computed at start-time.
67
67
  }
@@ -1,6 +1,7 @@
1
1
  # encoding: utf-8
2
- require "thread" #
2
+ require "thread"
3
3
  require "stud/interval"
4
+ require "concurrent"
4
5
  require "logstash/namespace"
5
6
  require "logstash/errors"
6
7
  require "logstash/event"
@@ -9,17 +10,24 @@ require "logstash/filters/base"
9
10
  require "logstash/inputs/base"
10
11
  require "logstash/outputs/base"
11
12
  require "logstash/util/reporter"
13
+ require "logstash/config/cpu_core_strategy"
14
+ require "logstash/util/defaults_printer"
12
15
 
13
16
  class LogStash::Pipeline
17
+ attr_reader :inputs, :filters, :outputs, :input_to_filter, :filter_to_output
14
18
 
15
19
  def initialize(configstr)
16
20
  @logger = Cabin::Channel.get(LogStash)
21
+
22
+ @inputs = nil
23
+ @filters = nil
24
+ @outputs = nil
25
+
17
26
  grammar = LogStashConfigParser.new
18
27
  @config = grammar.parse(configstr)
19
28
  if @config.nil?
20
29
  raise LogStash::ConfigurationError, grammar.failure_reason
21
30
  end
22
-
23
31
  # This will compile the config to ruby and evaluate the resulting code.
24
32
  # The code will initialize all the plugins and define the
25
33
  # filter and output methods.
@@ -34,36 +42,27 @@ class LogStash::Pipeline
34
42
  end
35
43
 
36
44
  @input_to_filter = SizedQueue.new(20)
45
+ # if no filters, pipe inputs directly to outputs
46
+ @filter_to_output = filters? ? SizedQueue.new(20) : @input_to_filter
37
47
 
38
- # If no filters, pipe inputs directly to outputs
39
- if !filters?
40
- @filter_to_output = @input_to_filter
41
- else
42
- @filter_to_output = SizedQueue.new(20)
43
- end
44
48
  @settings = {
45
- "filter-workers" => 1,
49
+ "filter-workers" => LogStash::Config::CpuCoreStrategy.fifty_percent
46
50
  }
47
51
 
48
- @run_mutex = Mutex.new
49
- @ready = false
50
- @started = false
52
+ # @ready requires thread safety since it is typically polled from outside the pipeline thread
53
+ @ready = Concurrent::AtomicBoolean.new(false)
51
54
  @input_threads = []
52
55
  end # def initialize
53
56
 
54
57
  def ready?
55
- @run_mutex.synchronize{@ready}
56
- end
57
-
58
- def started?
59
- @run_mutex.synchronize{@started}
58
+ @ready.value
60
59
  end
61
60
 
62
61
  def configure(setting, value)
63
- if setting == "filter-workers"
62
+ if setting == "filter-workers" && value > 1
64
63
  # Abort if we have any filters that aren't threadsafe
65
- if value > 1 && @filters.any? { |f| !f.threadsafe? }
66
- plugins = @filters.select { |f| !f.threadsafe? }.collect { |f| f.class.config_name }
64
+ plugins = @filters.select { |f| !f.threadsafe? }.collect { |f| f.class.config_name }
65
+ if !plugins.size.zero?
67
66
  raise LogStash::ConfigurationError, "Cannot use more than 1 filter worker because the following plugins don't work with more than one worker: #{plugins.join(", ")}"
68
67
  end
69
68
  end
@@ -75,14 +74,17 @@ class LogStash::Pipeline
75
74
  end
76
75
 
77
76
  def run
78
- @run_mutex.synchronize{@started = true}
77
+ @logger.terminal(LogStash::Util::DefaultsPrinter.print(@settings))
79
78
 
80
- # synchronize @input_threads between run and shutdown
81
- @run_mutex.synchronize{start_inputs}
82
- start_filters if filters?
83
- start_outputs
84
-
85
- @run_mutex.synchronize{@ready = true}
79
+ begin
80
+ start_inputs
81
+ start_filters if filters?
82
+ start_outputs
83
+ ensure
84
+ # it is important to garantee @ready to be true after the startup sequence has been completed
85
+ # to potentially unblock the shutdown method which may be waiting on @ready to proceed
86
+ @ready.make_true
87
+ end
86
88
 
87
89
  @logger.info("Pipeline started")
88
90
  @logger.terminal("Logstash startup completed")
@@ -107,12 +109,6 @@ class LogStash::Pipeline
107
109
 
108
110
  def wait_inputs
109
111
  @input_threads.each(&:join)
110
- rescue Interrupt
111
- # rbx does weird things during do SIGINT that I haven't debugged
112
- # so we catch Interrupt here and signal a shutdown. For some reason the
113
- # signal handler isn't invoked it seems? I dunno, haven't looked much into
114
- # it.
115
- shutdown
116
112
  end
117
113
 
118
114
  def shutdown_filters
@@ -153,10 +149,17 @@ class LogStash::Pipeline
153
149
 
154
150
  def start_filters
155
151
  @filters.each(&:register)
156
- @filter_threads = @settings["filter-workers"].times.collect do
152
+ to_start = @settings["filter-workers"]
153
+ @filter_threads = to_start.times.collect do
157
154
  Thread.new { filterworker }
158
155
  end
159
-
156
+ actually_started = @filter_threads.select(&:alive?).size
157
+ msg = "Worker threads expected: #{to_start}, worker threads started: #{actually_started}"
158
+ if actually_started < to_start
159
+ @logger.warn(msg)
160
+ else
161
+ @logger.info(msg)
162
+ end
160
163
  @flusher_thread = Thread.new { Stud.interval(5) { @input_to_filter.push(LogStash::FLUSH) } }
161
164
  end
162
165
 
@@ -175,9 +178,16 @@ class LogStash::Pipeline
175
178
  LogStash::Util::set_thread_name("<#{plugin.class.config_name}")
176
179
  begin
177
180
  plugin.run(@input_to_filter)
178
- rescue LogStash::ShutdownSignal
179
- # ignore and quit
180
181
  rescue => e
182
+ # if plugin is stopping, ignore uncatched exceptions and exit worker
183
+ if plugin.stop?
184
+ @logger.debug("Input plugin raised exception during shutdown, ignoring it.",
185
+ :plugin => plugin.class.config_name, :exception => e,
186
+ :backtrace => e.backtrace)
187
+ return
188
+ end
189
+
190
+ # otherwise, report error and restart
181
191
  if @logger.debug?
182
192
  @logger.error(I18n.t("logstash.pipeline.worker-error-debug",
183
193
  :plugin => plugin.inspect, :error => e.to_s,
@@ -187,28 +197,18 @@ class LogStash::Pipeline
187
197
  @logger.error(I18n.t("logstash.pipeline.worker-error",
188
198
  :plugin => plugin.inspect, :error => e))
189
199
  end
190
- puts e.backtrace if @logger.debug?
191
- # input teardown must be synchronized since is can be called concurrently by
192
- # the input worker thread and from the pipeline thread shutdown method.
193
- # this means that input teardown methods must support multiple calls.
194
- @run_mutex.synchronize{plugin.teardown}
195
- sleep 1
196
- retry
197
- end
198
- ensure
199
- begin
200
- # input teardown must be synchronized since is can be called concurrently by
201
- # the input worker thread and from the pipeline thread shutdown method.
202
- # this means that input teardown methods must support multiple calls.
203
- @run_mutex.synchronize{plugin.teardown}
204
- rescue LogStash::ShutdownSignal
205
- # teardown could receive the ShutdownSignal, retry it
200
+
201
+ # Assuming the failure that caused this exception is transient,
202
+ # let's sleep for a bit and execute #run again
203
+ sleep(1)
206
204
  retry
205
+ ensure
206
+ plugin.do_close
207
207
  end
208
208
  end # def inputworker
209
209
 
210
210
  def filterworker
211
- LogStash::Util::set_thread_name("|worker")
211
+ LogStash::Util.set_thread_name("|worker")
212
212
  begin
213
213
  while true
214
214
  event = @input_to_filter.pop
@@ -227,65 +227,50 @@ class LogStash::Pipeline
227
227
  break
228
228
  end
229
229
  end
230
- rescue => e
231
- @logger.error("Exception in filterworker", "exception" => e, "backtrace" => e.backtrace)
230
+ rescue Exception => e
231
+ # Plugins authors should manage their own exceptions in the plugin code
232
+ # but if an exception is raised up to the worker thread they are considered
233
+ # fatal and logstash will not recover from this situation.
234
+ #
235
+ # Users need to check their configuration or see if there is a bug in the
236
+ # plugin.
237
+ @logger.error("Exception in filterworker, the pipeline stopped processing new events, please check your filter configuration and restart Logstash.",
238
+ "exception" => e, "backtrace" => e.backtrace)
239
+ raise
240
+ ensure
241
+ @filters.each(&:do_close)
232
242
  end
233
-
234
- @filters.each(&:teardown)
235
243
  end # def filterworker
236
244
 
237
245
  def outputworker
238
- LogStash::Util::set_thread_name(">output")
246
+ LogStash::Util.set_thread_name(">output")
239
247
  @outputs.each(&:worker_setup)
240
248
 
241
249
  while true
242
250
  event = @filter_to_output.pop
243
251
  break if event == LogStash::SHUTDOWN
244
252
  output_func(event)
245
- end # while true
246
-
253
+ end
254
+ ensure
247
255
  @outputs.each do |output|
248
- output.worker_plugins.each(&:teardown)
256
+ output.worker_plugins.each(&:do_close)
249
257
  end
250
258
  end # def outputworker
251
259
 
252
- # Shutdown this pipeline.
253
- #
254
- # This method is intended to be called from another thread
255
- def shutdown
256
- InflightEventsReporter.logger = @logger
257
- InflightEventsReporter.start(@input_to_filter, @filter_to_output, @outputs)
258
- @input_threads.each do |thread|
259
- # Interrupt all inputs
260
- @logger.info("Sending shutdown signal to input thread", :thread => thread)
261
-
262
- # synchronize both ShutdownSignal and teardown below. by synchronizing both
263
- # we will avoid potentially sending a shutdown signal when the inputworker is
264
- # executing the teardown method.
265
- @run_mutex.synchronize do
266
- thread.raise(LogStash::ShutdownSignal)
267
- begin
268
- thread.wakeup # in case it's in blocked IO or sleeping
269
- rescue ThreadError
270
- end
271
- end
272
- end
260
+ # initiate the pipeline shutdown sequence
261
+ # this method is intended to be called from outside the pipeline thread
262
+ # @param before_stop [Proc] code block called before performing stop operation on input plugins
263
+ def shutdown(&before_stop)
264
+ # shutdown can only start once the pipeline has completed its startup.
265
+ # avoid potential race conditoon between the startup sequence and this
266
+ # shutdown method which can be called from another thread at any time
267
+ sleep(0.1) while !ready?
273
268
 
274
- # sometimes an input is stuck in a blocking I/O so we need to tell it to teardown directly
275
- @inputs.each do |input|
276
- begin
277
- # input teardown must be synchronized since is can be called concurrently by
278
- # the input worker thread and from the pipeline thread shutdown method.
279
- # this means that input teardown methods must support multiple calls.
280
- @run_mutex.synchronize{input.teardown}
281
- rescue LogStash::ShutdownSignal
282
- # teardown could receive the ShutdownSignal, retry it
283
- retry
284
- end
285
- end
269
+ # TODO: should we also check against calling shutdown multiple times concurently?
270
+
271
+ before_stop.call if block_given?
286
272
 
287
- # No need to send the ShutdownEvent to the filters/outputs nor to wait for
288
- # the inputs to finish, because in the #run method we wait for that anyway.
273
+ @inputs.each(&:do_stop)
289
274
  end # def shutdown
290
275
 
291
276
  def plugin(plugin_type, name, *args)
@@ -3,6 +3,7 @@ require "logstash/namespace"
3
3
  require "logstash/logging"
4
4
  require "logstash/config/mixin"
5
5
  require "cabin"
6
+ require "concurrent"
6
7
 
7
8
  class LogStash::Plugin
8
9
  attr_accessor :params
@@ -27,72 +28,21 @@ class LogStash::Plugin
27
28
  @logger = Cabin::Channel.get(LogStash)
28
29
  end
29
30
 
30
- # This method is called when someone or something wants this plugin to shut
31
- # down. When you successfully shutdown, you must call 'finished'
32
- # You must also call 'super' in any subclasses.
31
+ # close is called during shutdown, after the plugin worker
32
+ # main task terminates
33
33
  public
34
- def shutdown(queue)
35
- # By default, shutdown is assumed a no-op for all plugins.
36
- # If you need to take special efforts to shutdown (like waiting for
37
- # an operation to complete, etc)
38
- teardown
39
- @logger.info("Received shutdown signal", :plugin => self)
40
-
41
- @shutdown_queue = queue
42
- if @plugin_state == :finished
43
- finished
44
- else
45
- @plugin_state = :terminating
46
- end
47
- end # def shutdown
48
-
49
- # You should call this method when you (the plugin) are done with work
50
- # forever.
51
- public
52
- def finished
53
- # TODO(sissel): I'm not sure what I had planned for this shutdown_queue
54
- # thing
55
- if @shutdown_queue
56
- @logger.info("Sending shutdown event to agent queue", :plugin => self)
57
- @shutdown_queue << self
58
- end
59
-
60
- if @plugin_state != :finished
61
- @logger.info("Plugin is finished", :plugin => self)
62
- @plugin_state = :finished
63
- end
64
- end # def finished
65
-
66
- # Subclasses should implement this teardown method if you need to perform any
67
- # special tasks during shutdown (like flushing, etc.)
68
- public
69
- def teardown
70
- # nothing by default
71
- finished
34
+ def do_close
35
+ @logger.debug("closing", :plugin => self)
36
+ close
72
37
  end
73
38
 
74
- # This method is called when a SIGHUP triggers a reload operation
39
+ # Subclasses should implement this close method if you need to perform any
40
+ # special tasks during shutdown (like flushing, etc.)
75
41
  public
76
- def reload
77
- # Do nothing by default
42
+ def close
43
+ # ..
78
44
  end
79
45
 
80
- public
81
- def finished?
82
- return @plugin_state == :finished
83
- end # def finished?
84
-
85
- public
86
- def running?
87
- return @plugin_state != :finished
88
- end # def finished?
89
-
90
- public
91
- def terminating?
92
- return @plugin_state == :terminating
93
- end # def terminating?
94
-
95
- public
96
46
  def to_s
97
47
  return "#{self.class.name}: #{@params}"
98
48
  end
@@ -46,7 +46,7 @@ module LogStash
46
46
  position = match.offset(0).last
47
47
  end
48
48
 
49
- if position < template.size - 1
49
+ if position < template.size
50
50
  nodes << StaticNode.new(template[position..-1])
51
51
  end
52
52
 
@@ -0,0 +1,31 @@
1
+ # encoding: utf-8
2
+ require "logstash/namespace"
3
+ require "logstash/util"
4
+ require "logstash/util/worker_threads_default_printer"
5
+
6
+
7
+ # This class exists to format the settings for defaults used
8
+ module LogStash module Util class DefaultsPrinter
9
+ def self.print(settings)
10
+ new(settings).print
11
+ end
12
+
13
+ def initialize(settings)
14
+ @settings = settings
15
+ @printers = [workers]
16
+ end
17
+
18
+ def print
19
+ collector = []
20
+ @printers.each do |printer|
21
+ printer.visit(collector)
22
+ end
23
+ "Default settings used: " + collector.join(', ')
24
+ end
25
+
26
+ private
27
+
28
+ def workers
29
+ WorkerThreadsDefaultPrinter.new(@settings)
30
+ end
31
+ end end end
@@ -0,0 +1,17 @@
1
+ # encoding: utf-8
2
+ require "logstash/namespace"
3
+ require "logstash/util"
4
+
5
+ # This class exists to format the settings for default worker threads
6
+ module LogStash module Util class WorkerThreadsDefaultPrinter
7
+
8
+ def initialize(settings)
9
+ @setting = settings.fetch('filter-workers', 1)
10
+ end
11
+
12
+ def visit(collector)
13
+ collector.push "Filter workers: #{@setting}"
14
+ end
15
+
16
+ end end end
17
+
@@ -1,6 +1,6 @@
1
1
  # encoding: utf-8
2
2
  # The version of logstash.
3
- LOGSTASH_VERSION = "2.0.0-beta1"
3
+ LOGSTASH_VERSION = "2.0.0-beta2"
4
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.