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

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