logstash-core 2.0.0.beta1-java → 2.0.0.beta2-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.

@@ -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.