logstash-core 1.5.0.rc2.snapshot-java → 1.5.0.rc3-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.

@@ -16,6 +16,13 @@ require "logstash/json"
16
16
  class LogStash::ShutdownEvent; end
17
17
  class LogStash::FlushEvent; end
18
18
 
19
+ module LogStash
20
+ FLUSH = LogStash::FlushEvent.new
21
+
22
+ # LogStash::SHUTDOWN is used by plugins
23
+ SHUTDOWN = LogStash::ShutdownEvent.new
24
+ end
25
+
19
26
  # the logstash event object.
20
27
  #
21
28
  # An event is simply a tuple of (timestamp, data).
@@ -48,25 +55,26 @@ class LogStash::Event
48
55
  TIMESTAMP_FAILURE_TAG = "_timestampparsefailure"
49
56
  TIMESTAMP_FAILURE_FIELD = "_@timestamp"
50
57
 
58
+ METADATA = "@metadata".freeze
59
+ METADATA_BRACKETS = "[#{METADATA}]".freeze
60
+
51
61
  # Floats outside of these upper and lower bounds are forcibly converted
52
62
  # to scientific notation by Float#to_s
53
63
  MIN_FLOAT_BEFORE_SCI_NOT = 0.0001
54
64
  MAX_FLOAT_BEFORE_SCI_NOT = 1000000000000000.0
55
65
 
66
+ LOGGER = Cabin::Channel.get(LogStash)
67
+
56
68
  public
57
69
  def initialize(data = {})
58
- @logger = Cabin::Channel.get(LogStash)
59
70
  @cancelled = false
60
71
  @data = data
61
72
  @accessors = LogStash::Util::Accessors.new(data)
62
73
  @data[VERSION] ||= VERSION_ONE
63
- @data[TIMESTAMP] = init_timestamp(@data[TIMESTAMP])
74
+ ts = @data[TIMESTAMP]
75
+ @data[TIMESTAMP] = ts ? init_timestamp(ts) : LogStash::Timestamp.now
64
76
 
65
- @metadata = if @data.include?("@metadata")
66
- @data.delete("@metadata")
67
- else
68
- {}
69
- end
77
+ @metadata = @data.delete(METADATA) || {}
70
78
  @metadata_accessors = LogStash::Util::Accessors.new(@metadata)
71
79
  end # def initialize
72
80
 
@@ -113,9 +121,6 @@ class LogStash::Event
113
121
  raise DeprecatedMethod
114
122
  end # def unix_timestamp
115
123
 
116
- # field-related access
117
- METADATA = "@metadata".freeze
118
- METADATA_BRACKETS = "[#{METADATA}]".freeze
119
124
  public
120
125
  def [](fieldref)
121
126
  if fieldref.start_with?(METADATA_BRACKETS)
@@ -171,8 +176,14 @@ class LogStash::Event
171
176
  end
172
177
 
173
178
  public
174
- def include?(key)
175
- return !self[key].nil?
179
+ def include?(fieldref)
180
+ if fieldref.start_with?(METADATA_BRACKETS)
181
+ @metadata_accessors.include?(fieldref[METADATA_BRACKETS.length .. -1])
182
+ elsif fieldref == METADATA
183
+ true
184
+ else
185
+ @accessors.include?(fieldref)
186
+ end
176
187
  end # def include?
177
188
 
178
189
  # Append an event to this one.
@@ -224,6 +235,10 @@ class LogStash::Event
224
235
  # Take the inside of the %{ ... }
225
236
  key = tok[2 ... -1]
226
237
 
238
+ if key[0] == "+" && !@data.has_key?(TIMESTAMP)
239
+ raise LogStash::Error, "Unable to format \"#{key}\" in string \"#{format}\", #{TIMESTAMP} field not found"
240
+ end
241
+
227
242
  if key == "+%s"
228
243
  # Got %{+%s}, support for unix epoch time
229
244
  next @data[TIMESTAMP].to_i
@@ -263,12 +278,12 @@ class LogStash::Event
263
278
 
264
279
  def init_timestamp(o)
265
280
  begin
266
- timestamp = o ? LogStash::Timestamp.coerce(o) : LogStash::Timestamp.now
281
+ timestamp = LogStash::Timestamp.coerce(o)
267
282
  return timestamp if timestamp
268
283
 
269
- @logger.warn("Unrecognized #{TIMESTAMP} value, setting current time to #{TIMESTAMP}, original in #{TIMESTAMP_FAILURE_FIELD}field", :value => o.inspect)
284
+ LOGGER.warn("Unrecognized #{TIMESTAMP} value, setting current time to #{TIMESTAMP}, original in #{TIMESTAMP_FAILURE_FIELD}field", :value => o.inspect)
270
285
  rescue LogStash::TimestampParserError => e
271
- @logger.warn("Error parsing #{TIMESTAMP} string, setting current time to #{TIMESTAMP}, original in #{TIMESTAMP_FAILURE_FIELD} field", :value => o.inspect, :exception => e.message)
286
+ LOGGER.warn("Error parsing #{TIMESTAMP} string, setting current time to #{TIMESTAMP}, original in #{TIMESTAMP_FAILURE_FIELD} field", :value => o.inspect, :exception => e.message)
272
287
  end
273
288
 
274
289
  @data["tags"] ||= []
@@ -280,11 +295,7 @@ class LogStash::Event
280
295
 
281
296
  public
282
297
  def to_hash_with_metadata
283
- if @metadata.nil?
284
- to_hash
285
- else
286
- to_hash.merge("@metadata" => @metadata)
287
- end
298
+ @metadata.empty? ? to_hash : to_hash.merge(METADATA => @metadata)
288
299
  end
289
300
 
290
301
  public
@@ -1,5 +1,6 @@
1
1
  # encoding: utf-8
2
2
  require "logstash/namespace"
3
+ require "logstash/event"
3
4
  require "logstash/logging"
4
5
  require "logstash/plugin"
5
6
  require "logstash/config/mixin"
@@ -1,3 +1,4 @@
1
+ require "logstash/util"
1
2
  module LogStash
2
3
 
3
4
  class GemfileError < StandardError; end
@@ -17,6 +18,7 @@ module LogStash
17
18
 
18
19
  def load
19
20
  @gemset ||= DSL.parse(@io.read)
21
+ backup
20
22
  self
21
23
  end
22
24
 
@@ -51,6 +53,23 @@ module LogStash
51
53
  def remove(name)
52
54
  @gemset.remove_gem(name)
53
55
  end
56
+
57
+ def backup
58
+ @original_backup = @gemset.copy
59
+ end
60
+
61
+ def restore
62
+ @gemset = @original_backup
63
+ end
64
+
65
+ def restore!
66
+ restore
67
+ save
68
+ end
69
+
70
+ def locally_installed_gems
71
+ @gemset.gems.select { |gem| gem.options.include?(:path) }
72
+ end
54
73
  end
55
74
 
56
75
  class Gemset
@@ -101,7 +120,6 @@ module LogStash
101
120
  def copy
102
121
  Marshal.load(Marshal.dump(self))
103
122
  end
104
-
105
123
  private
106
124
 
107
125
  def sources_to_s
@@ -6,27 +6,11 @@ require "java"
6
6
  # not test for is_a?(Array) or is_a?(Hash) and we do not want to include tests for
7
7
  # both classes everywhere. see LogStash::JSon.
8
8
 
9
- class Java::JavaUtil::ArrayList
10
- # have ArrayList objects report is_a?(Array) == true
11
- def is_a?(clazz)
12
- return true if clazz == Array
13
- super
14
- end
15
- end
16
-
17
- class Java::JavaUtil::LinkedHashMap
18
- # have LinkedHashMap objects report is_a?(Array) == true
19
- def is_a?(clazz)
20
- return true if clazz == Hash
21
- super
22
- end
23
- end
24
-
25
9
  class Array
26
10
  # enable class equivalence between Array and ArrayList
27
11
  # so that ArrayList will work with case o when Array ...
28
12
  def self.===(other)
29
- return true if other.is_a?(Java::JavaUtil::ArrayList)
13
+ return true if other.is_a?(Java::JavaUtil::Collection)
30
14
  super
31
15
  end
32
16
  end
@@ -35,7 +19,77 @@ class Hash
35
19
  # enable class equivalence between Hash and LinkedHashMap
36
20
  # so that LinkedHashMap will work with case o when Hash ...
37
21
  def self.===(other)
38
- return true if other.is_a?(Java::JavaUtil::LinkedHashMap)
22
+ return true if other.is_a?(Java::JavaUtil::Map)
39
23
  super
40
24
  end
41
25
  end
26
+
27
+ # map_mixin to patch LinkedHashMap and HashMap. it must be done directly on the classes,
28
+ # using a module mixin does not work, and injecting in the Map interface does not work either
29
+ # but injecting in the class works.
30
+
31
+ map_mixin = lambda do
32
+ # this is a temporary fix to solve a bug in JRuby where classes implementing the Map interface, like LinkedHashMap
33
+ # have a bug in the has_key? method that is implemented in the Enumerable module that is somehow mixed in the Map interface.
34
+ # this bug makes has_key? (and all its aliases) return false for a key that has a nil value.
35
+ # Only LinkedHashMap is patched here because patching the Map interface is not working.
36
+ # TODO find proper fix, and submit upstream
37
+ # releavant JRuby files:
38
+ # https://github.com/jruby/jruby/blob/master/core/src/main/ruby/jruby/java/java_ext/java.util.rb
39
+ # https://github.com/jruby/jruby/blob/master/core/src/main/java/org/jruby/java/proxies/MapJavaProxy.java
40
+ def has_key?(key)
41
+ self.containsKey(key)
42
+ end
43
+ alias_method :include?, :has_key?
44
+ alias_method :member?, :has_key?
45
+ alias_method :key?, :has_key?
46
+
47
+ # Java 8 Map implements a merge method with a different signature from
48
+ # the Ruby Hash#merge. see https://github.com/jruby/jruby/issues/1249
49
+ # this can be removed when fixed upstream
50
+ if ENV_JAVA['java.specification.version'] >= '1.8'
51
+ def merge(other)
52
+ dup.merge!(other)
53
+ end
54
+ end
55
+ end
56
+
57
+ Java::JavaUtil::LinkedHashMap.module_exec(&map_mixin)
58
+ Java::JavaUtil::HashMap.module_exec(&map_mixin)
59
+
60
+ module java::util::Map
61
+ # have Map objects like LinkedHashMap objects report is_a?(Array) == true
62
+ def is_a?(clazz)
63
+ return true if clazz == Hash
64
+ super
65
+ end
66
+ end
67
+
68
+ module java::util::Collection
69
+ # have Collections objects like ArrayList report is_a?(Array) == true
70
+ def is_a?(clazz)
71
+ return true if clazz == Array
72
+ super
73
+ end
74
+
75
+ # support the Ruby Array delete method on a Java Collection
76
+ def delete(o)
77
+ self.removeAll([o]) ? o : block_given? ? yield : nil
78
+ end
79
+
80
+ # support the Ruby intersection method on Java Collection
81
+ def &(other)
82
+ # transform self into a LinkedHashSet to remove duplicates and preserve order as defined by the Ruby Array intersection contract
83
+ duped = Java::JavaUtil::LinkedHashSet.new(self)
84
+ duped.retainAll(other)
85
+ duped
86
+ end
87
+
88
+ # support the Ruby union method on Java Collection
89
+ def |(other)
90
+ # transform self into a LinkedHashSet to remove duplicates and preserve order as defined by the Ruby Array union contract
91
+ duped = Java::JavaUtil::LinkedHashSet.new(self)
92
+ duped.addAll(other)
93
+ duped
94
+ end
95
+ end
@@ -12,6 +12,4 @@ module LogStash
12
12
  module Util; end
13
13
  module PluginMixins; end
14
14
  module PluginManager; end
15
-
16
- SHUTDOWN = :shutdown
17
15
  end # module LogStash
@@ -69,7 +69,7 @@ class LogStash::Outputs::Base < LogStash::Plugin
69
69
  else
70
70
  define_singleton_method(:handle, method(:handle_worker))
71
71
  @worker_queue = SizedQueue.new(20)
72
- @worker_plugins = @workers.times.map { self.class.new(params.merge("workers" => 1, "codec" => @codec.clone)) }
72
+ @worker_plugins = @workers.times.map { self.class.new(@original_params.merge("workers" => 1)) }
73
73
  @worker_plugins.map.with_index do |plugin, i|
74
74
  Thread.new(original_params, @worker_queue) do |params, queue|
75
75
  LogStash::Util::set_thread_name(">#{self.class.config_name}.#{i}")
@@ -0,0 +1,20 @@
1
+ # Bundler monkey patches
2
+ module ::Bundler
3
+ # Patch bundler to write a .lock file specific to the version of ruby.
4
+ # This keeps MRI/JRuby/RBX from conflicting over the Gemfile.lock updates
5
+ module SharedHelpers
6
+ def default_lockfile
7
+ ruby = "#{LogStash::Environment.ruby_engine}-#{LogStash::Environment.ruby_abi_version}"
8
+ Pathname.new("#{default_gemfile}.#{ruby}.lock")
9
+ end
10
+ end
11
+
12
+ # Add the Bundler.reset! method which has been added in master but is not in 1.7.9.
13
+ class << self
14
+ unless self.method_defined?("reset!")
15
+ def reset!
16
+ @definition = nil
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,37 @@
1
+ # monkey patch RubyGems to silence ffi warnings:
2
+ #
3
+ # WARN: Unresolved specs during Gem::Specification.reset:
4
+ # ffi (>= 0)
5
+ # WARN: Clearing out unresolved specs.
6
+ # Please report a bug if this causes problems.
7
+ #
8
+ # see https://github.com/elasticsearch/logstash/issues/2556 and https://github.com/rubygems/rubygems/issues/1070
9
+ #
10
+ # this code is from Rubygems v2.1.9 in JRuby 1.7.17. Per tickets this issue should be solved at JRuby >= 1.7.20.
11
+ #
12
+ # this method implementation works for Rubygems version 2.1.0 and up, verified up to 2.4.6
13
+ if ::Gem::Version.new(::Gem::VERSION) >= ::Gem::Version.new("2.1.0") && ::Gem::Version.new(::Gem::VERSION) < ::Gem::Version.new("2.5.0")
14
+ class ::Gem::Specification
15
+ def self.reset
16
+ @@dirs = nil
17
+ ::Gem.pre_reset_hooks.each { |hook| hook.call }
18
+ @@all = nil
19
+ @@stubs = nil
20
+ _clear_load_cache
21
+ unresolved = unresolved_deps
22
+ unless unresolved.empty?
23
+ unless (unresolved.size == 1 && unresolved["ffi"])
24
+ w = "W" + "ARN"
25
+ warn "#{w}: Unresolved specs during Gem::Specification.reset:"
26
+ unresolved.values.each do |dep|
27
+ warn " #{dep}"
28
+ end
29
+ warn "#{w}: Clearing out unresolved specs."
30
+ warn "Please report a bug if this causes problems."
31
+ end
32
+ unresolved.clear
33
+ end
34
+ ::Gem.post_reset_hooks.each { |hook| hook.call }
35
+ end
36
+ end
37
+ end
@@ -11,8 +11,6 @@ require "logstash/outputs/base"
11
11
 
12
12
  class LogStash::Pipeline
13
13
 
14
- FLUSH_EVENT = LogStash::FlushEvent.new
15
-
16
14
  def initialize(configstr)
17
15
  @logger = Cabin::Channel.get(LogStash)
18
16
  grammar = LogStashConfigParser.new
@@ -45,14 +43,19 @@ class LogStash::Pipeline
45
43
  @settings = {
46
44
  "filter-workers" => 1,
47
45
  }
46
+
47
+ @run_mutex = Mutex.new
48
+ @ready = false
49
+ @started = false
50
+ @input_threads = []
48
51
  end # def initialize
49
52
 
50
53
  def ready?
51
- return @ready
54
+ @run_mutex.synchronize{@ready}
52
55
  end
53
56
 
54
57
  def started?
55
- return @started
58
+ @run_mutex.synchronize{@started}
56
59
  end
57
60
 
58
61
  def configure(setting, value)
@@ -71,14 +74,14 @@ class LogStash::Pipeline
71
74
  end
72
75
 
73
76
  def run
74
- @started = true
75
- @input_threads = []
77
+ @run_mutex.synchronize{@started = true}
76
78
 
77
- start_inputs
79
+ # synchronize @input_threads between run and shutdown
80
+ @run_mutex.synchronize{start_inputs}
78
81
  start_filters if filters?
79
82
  start_outputs
80
83
 
81
- @ready = true
84
+ @run_mutex.synchronize{@ready = true}
82
85
 
83
86
  @logger.info("Pipeline started")
84
87
  @logger.terminal("Logstash startup completed")
@@ -113,7 +116,7 @@ class LogStash::Pipeline
113
116
 
114
117
  def shutdown_filters
115
118
  @flusher_lock.synchronize { @flusher_thread.kill }
116
- @input_to_filter.push(LogStash::ShutdownEvent.new)
119
+ @input_to_filter.push(LogStash::SHUTDOWN)
117
120
  end
118
121
 
119
122
  def wait_filters
@@ -122,7 +125,7 @@ class LogStash::Pipeline
122
125
 
123
126
  def shutdown_outputs
124
127
  # nothing, filters will do this
125
- @filter_to_output.push(LogStash::ShutdownEvent.new)
128
+ @filter_to_output.push(LogStash::SHUTDOWN)
126
129
  end
127
130
 
128
131
  def wait_outputs
@@ -134,7 +137,7 @@ class LogStash::Pipeline
134
137
  moreinputs = []
135
138
  @inputs.each do |input|
136
139
  if input.threadable && input.threads > 1
137
- (input.threads-1).times do |i|
140
+ (input.threads - 1).times do |i|
138
141
  moreinputs << input.clone
139
142
  end
140
143
  end
@@ -154,7 +157,7 @@ class LogStash::Pipeline
154
157
  end
155
158
 
156
159
  @flusher_lock = Mutex.new
157
- @flusher_thread = Thread.new { Stud.interval(5) { @flusher_lock.synchronize { @input_to_filter.push(FLUSH_EVENT) } } }
160
+ @flusher_thread = Thread.new { Stud.interval(5) { @flusher_lock.synchronize { @input_to_filter.push(LogStash::FLUSH) } } }
158
161
  end
159
162
 
160
163
  def start_outputs
@@ -173,7 +176,7 @@ class LogStash::Pipeline
173
176
  begin
174
177
  plugin.run(@input_to_filter)
175
178
  rescue LogStash::ShutdownSignal
176
- return
179
+ # ignore and quit
177
180
  rescue => e
178
181
  if @logger.debug?
179
182
  @logger.error(I18n.t("logstash.pipeline.worker-error-debug",
@@ -185,14 +188,23 @@ class LogStash::Pipeline
185
188
  :plugin => plugin.inspect, :error => e))
186
189
  end
187
190
  puts e.backtrace if @logger.debug?
188
- plugin.teardown
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}
189
195
  sleep 1
190
196
  retry
191
197
  end
192
- rescue LogStash::ShutdownSignal
193
- # nothing
194
198
  ensure
195
- plugin.teardown
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
206
+ retry
207
+ end
196
208
  end # def inputworker
197
209
 
198
210
  def filterworker
@@ -203,11 +215,8 @@ class LogStash::Pipeline
203
215
 
204
216
  case event
205
217
  when LogStash::Event
206
- # use events array to guarantee ordering of origin vs created events
207
- # where created events are emitted by filters like split or metrics
208
- events = []
209
- filter(event) { |newevent| events << newevent }
210
- events.each { |event| @filter_to_output.push(event) }
218
+ # filter_func returns all filtered events, including cancelled ones
219
+ filter_func(event).each { |e| @filter_to_output.push(e) unless e.cancelled? }
211
220
  when LogStash::FlushEvent
212
221
  # handle filter flushing here so that non threadsafe filters (thus only running one filterworker)
213
222
  # don't have to deal with thread safety implementing the flush method
@@ -231,8 +240,8 @@ class LogStash::Pipeline
231
240
 
232
241
  while true
233
242
  event = @filter_to_output.pop
234
- break if event.is_a?(LogStash::ShutdownEvent)
235
- output(event)
243
+ break if event == LogStash::SHUTDOWN
244
+ output_func(event)
236
245
  end # while true
237
246
 
238
247
  @outputs.each do |output|
@@ -246,18 +255,30 @@ class LogStash::Pipeline
246
255
  def shutdown
247
256
  @input_threads.each do |thread|
248
257
  # Interrupt all inputs
249
- @logger.info("Sending shutdown signal to input thread",
250
- :thread => thread)
251
- thread.raise(LogStash::ShutdownSignal)
252
- begin
253
- thread.wakeup # in case it's in blocked IO or sleeping
254
- rescue ThreadError
258
+ @logger.info("Sending shutdown signal to input thread", :thread => thread)
259
+
260
+ # synchronize both ShutdownSignal and teardown below. by synchronizing both
261
+ # we will avoid potentially sending a shutdown signal when the inputworker is
262
+ # executing the teardown method.
263
+ @run_mutex.synchronize do
264
+ thread.raise(LogStash::ShutdownSignal)
265
+ begin
266
+ thread.wakeup # in case it's in blocked IO or sleeping
267
+ rescue ThreadError
268
+ end
255
269
  end
270
+ end
256
271
 
257
- # Sometimes an input is stuck in a blocking I/O
258
- # so we need to tell it to teardown directly
259
- @inputs.each do |input|
260
- input.teardown
272
+ # sometimes an input is stuck in a blocking I/O so we need to tell it to teardown directly
273
+ @inputs.each do |input|
274
+ begin
275
+ # input teardown must be synchronized since is can be called concurrently by
276
+ # the input worker thread and from the pipeline thread shutdown method.
277
+ # this means that input teardown methods must support multiple calls.
278
+ @run_mutex.synchronize{input.teardown}
279
+ rescue LogStash::ShutdownSignal
280
+ # teardown could receive the ShutdownSignal, retry it
281
+ retry
261
282
  end
262
283
  end
263
284
 
@@ -271,12 +292,11 @@ class LogStash::Pipeline
271
292
  return klass.new(*args)
272
293
  end
273
294
 
295
+ # for backward compatibility in devutils for the rspec helpers, this method is not used
296
+ # in the pipeline anymore.
274
297
  def filter(event, &block)
275
- @filter_func.call(event, &block)
276
- end
277
-
278
- def output(event)
279
- @output_func.call(event)
298
+ # filter_func returns all filtered events, including cancelled ones
299
+ filter_func(event).each { |e| block.call(e) }
280
300
  end
281
301
 
282
302
  # perform filters flush and yeild flushed event to the passed block