fluentd 0.14.3 → 0.14.4

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 fluentd might be problematic. Click here for more details.

Files changed (60) hide show
  1. checksums.yaml +4 -4
  2. data/ChangeLog +59 -1131
  3. data/Rakefile +15 -0
  4. data/appveyor.yml +2 -2
  5. data/example/multi_filters.conf +61 -0
  6. data/fluentd.gemspec +19 -16
  7. data/lib/fluent/agent.rb +3 -0
  8. data/lib/fluent/command/debug.rb +4 -4
  9. data/lib/fluent/compat/handle_tag_and_time_mixin.rb +2 -0
  10. data/lib/fluent/compat/output.rb +5 -2
  11. data/lib/fluent/config/configure_proxy.rb +26 -5
  12. data/lib/fluent/config/error.rb +3 -0
  13. data/lib/fluent/config/section.rb +15 -0
  14. data/lib/fluent/event_router.rb +77 -4
  15. data/lib/fluent/plugin/base.rb +12 -3
  16. data/lib/fluent/plugin/filter.rb +48 -6
  17. data/lib/fluent/plugin/filter_record_transformer.rb +3 -1
  18. data/lib/fluent/plugin/filter_stdout.rb +0 -4
  19. data/lib/fluent/plugin/in_debug_agent.rb +5 -5
  20. data/lib/fluent/plugin/in_dummy.rb +9 -1
  21. data/lib/fluent/plugin/in_forward.rb +32 -14
  22. data/lib/fluent/plugin/in_monitor_agent.rb +31 -77
  23. data/lib/fluent/plugin/in_tail.rb +37 -9
  24. data/lib/fluent/plugin/out_forward.rb +2 -13
  25. data/lib/fluent/plugin/output.rb +16 -1
  26. data/lib/fluent/plugin/storage_local.rb +16 -0
  27. data/lib/fluent/plugin_helper/timer.rb +6 -1
  28. data/lib/fluent/root_agent.rb +3 -0
  29. data/lib/fluent/supervisor.rb +62 -9
  30. data/lib/fluent/test/base.rb +3 -0
  31. data/lib/fluent/test/driver/base.rb +5 -0
  32. data/lib/fluent/test/formatter_test.rb +2 -0
  33. data/lib/fluent/test/parser_test.rb +2 -0
  34. data/lib/fluent/version.rb +1 -1
  35. data/lib/fluent/winsvc.rb +1 -2
  36. data/test/compat/test_calls_super.rb +2 -0
  37. data/test/config/test_configurable.rb +88 -0
  38. data/test/config/test_types.rb +7 -3
  39. data/test/plugin/test_filter.rb +121 -23
  40. data/test/plugin/test_filter_record_transformer.rb +22 -6
  41. data/test/plugin/test_in_debug_agent.rb +2 -2
  42. data/test/plugin/test_in_forward.rb +54 -6
  43. data/test/plugin/test_in_monitor_agent.rb +329 -0
  44. data/test/plugin/test_in_tail.rb +73 -0
  45. data/test/plugin/test_out_forward.rb +1 -0
  46. data/test/plugin/test_output.rb +53 -0
  47. data/test/plugin/test_output_as_buffered.rb +13 -0
  48. data/test/plugin/test_output_as_buffered_overflow.rb +3 -0
  49. data/test/plugin/test_output_as_buffered_retries.rb +11 -0
  50. data/test/plugin/test_output_as_buffered_secondary.rb +12 -0
  51. data/test/plugin/test_output_as_standard.rb +12 -0
  52. data/test/plugin_helper/test_compat_parameters.rb +2 -0
  53. data/test/plugin_helper/test_timer.rb +31 -0
  54. data/test/test_event_router.rb +87 -0
  55. data/test/test_filter.rb +48 -6
  56. data/test/test_log.rb +5 -2
  57. data/test/test_output.rb +41 -1
  58. data/test/test_plugin_classes.rb +17 -9
  59. data/test/test_root_agent.rb +146 -0
  60. metadata +51 -67
@@ -30,22 +30,64 @@ module Fluent
30
30
 
31
31
  helpers :event_emitter
32
32
 
33
+ attr_reader :has_filter_with_time
34
+
35
+ def initialize
36
+ super
37
+ @has_filter_with_time = has_filter_with_time?
38
+ end
39
+
33
40
  def filter(tag, time, record)
34
41
  raise NotImplementedError, "BUG: filter plugins MUST implement this method"
35
42
  end
36
43
 
44
+ def filter_with_time(tag, time, record)
45
+ raise NotImplementedError, "BUG: filter plugins MUST implement this method"
46
+ end
47
+
37
48
  def filter_stream(tag, es)
38
49
  new_es = MultiEventStream.new
39
- es.each do |time, record|
40
- begin
41
- filtered_record = filter(tag, time, record)
42
- new_es.add(time, filtered_record) if filtered_record
43
- rescue => e
44
- router.emit_error_event(tag, time, record, e)
50
+ if @has_filter_with_time
51
+ es.each do |time, record|
52
+ begin
53
+ filtered_time, filtered_record = filter_with_time(tag, time, record)
54
+ new_es.add(filtered_time, filtered_record) if filtered_time && filtered_record
55
+ rescue => e
56
+ router.emit_error_event(tag, time, record, e)
57
+ end
58
+ end
59
+ else
60
+ es.each do |time, record|
61
+ begin
62
+ filtered_record = filter(tag, time, record)
63
+ new_es.add(time, filtered_record) if filtered_record
64
+ rescue => e
65
+ router.emit_error_event(tag, time, record, e)
66
+ end
45
67
  end
46
68
  end
47
69
  new_es
48
70
  end
71
+
72
+ private
73
+
74
+ def has_filter_with_time?
75
+ implmented_methods = self.class.instance_methods(false)
76
+ # Plugins that override `filter_stream` don't need check,
77
+ # because they may not call `filter` or `filter_with_time`
78
+ # for example fluentd/lib/fluent/plugin/filter_record_transformer.rb
79
+ return nil if implmented_methods.include?(:filter_stream)
80
+ case
81
+ when [:filter, :filter_with_time].all? { |e| implmented_methods.include?(e) }
82
+ raise "BUG: Filter plugins MUST be implemented either `filter` or `filter_with_time`"
83
+ when implmented_methods.include?(:filter)
84
+ false
85
+ when implmented_methods.include?(:filter_with_time)
86
+ true
87
+ else
88
+ raise NotImplementedError, "BUG: Filter plugins MUST be implmented either `filter` or `filter_with_time`"
89
+ end
90
+ end
49
91
  end
50
92
  end
51
93
  end
@@ -204,7 +204,9 @@ module Fluent::Plugin
204
204
  end
205
205
  elsif value.kind_of?(Hash) # record, etc
206
206
  value.each do |k, v|
207
- placeholders.store("${#{k}}", v) # foo
207
+ unless placeholder_values.has_key?(k) # prevent overwriting reserved keys such as tag
208
+ placeholders.store("${#{k}}", v) # foo
209
+ end
208
210
  placeholders.store(%Q[${#{key}["#{k}"]}], v) # record["foo"]
209
211
  end
210
212
  else # string, interger, float, and others?
@@ -34,11 +34,7 @@ module Fluent::Plugin
34
34
  def configure(conf)
35
35
  compat_parameters_convert(conf, :inject, :formatter)
36
36
  super
37
- end
38
-
39
- def start
40
37
  @formatter = formatter_create(conf: @config.elements('format').first, default_type: DEFAULT_FORMAT_TYPE)
41
- super
42
38
  end
43
39
 
44
40
  def filter_stream(tag, es)
@@ -14,11 +14,11 @@
14
14
  # limitations under the License.
15
15
  #
16
16
 
17
- require 'fluent/input'
17
+ require 'fluent/plugin/input'
18
18
 
19
- module Fluent
19
+ module Fluent::Plugin
20
20
  class DebugAgentInput < Input
21
- Plugin.register_input('debug_agent', self)
21
+ Fluent::Plugin.register_input('debug_agent', self)
22
22
 
23
23
  def initialize
24
24
  require 'drb/drb'
@@ -30,13 +30,13 @@ module Fluent
30
30
  config_param :port, :integer, default: 24230
31
31
  config_param :unix_path, :string, default: nil
32
32
  #config_param :unix_mode # TODO
33
- config_param :object, :string, default: 'Engine'
33
+ config_param :object, :string, default: 'Fluent::Engine'
34
34
 
35
35
  def configure(conf)
36
36
  super
37
37
  if @unix_path
38
38
  unless ::Fluent::FileUtil.writable?(@unix_path)
39
- raise ConfigError, "in_debug_agent: `#{@unix_path}` is not writable"
39
+ raise Fluent::ConfigError, "in_debug_agent: `#{@unix_path}` is not writable"
40
40
  end
41
41
  end
42
42
  end
@@ -30,6 +30,8 @@ module Fluent::Plugin
30
30
 
31
31
  desc "The value is the tag assigned to the generated events."
32
32
  config_param :tag, :string
33
+ desc "The number of events in event stream of each emits."
34
+ config_param :size, :integer, default: 1
33
35
  desc "It configures how many events to generate per second."
34
36
  config_param :rate, :integer, default: 1
35
37
  desc "If specified, each generated event has an auto-incremented key field."
@@ -97,7 +99,13 @@ module Fluent::Plugin
97
99
 
98
100
  def emit(num)
99
101
  begin
100
- num.times { router.emit(@tag, Fluent::Engine.now, generate()) }
102
+ if @size > 1
103
+ num.times do
104
+ router.emit_array(@tag, Array.new(@size) { [Fluent::Engine.now, generate] })
105
+ end
106
+ else
107
+ num.times { router.emit(@tag, Fluent::Engine.now, generate) }
108
+ end
101
109
  rescue => _
102
110
  # ignore all errors not to stop emits by emit errors
103
111
  end
@@ -49,6 +49,8 @@ module Fluent
49
49
  config_param :chunk_size_limit, :size, default: nil
50
50
  desc 'Skip an event if incoming event is invalid.'
51
51
  config_param :skip_invalid_event, :bool, default: false
52
+ desc "The field name of the client's hostname."
53
+ config_param :source_hostname_key, :string, default: nil
52
54
 
53
55
  def configure(conf)
54
56
  super
@@ -145,7 +147,7 @@ module Fluent
145
147
  # 3: object record
146
148
  # 4: object option (optional)
147
149
  # }
148
- def on_message(msg, chunk_size, source)
150
+ def on_message(msg, chunk_size, peeraddr)
149
151
  if msg.nil?
150
152
  # for future TCP heartbeat_request
151
153
  return
@@ -153,7 +155,7 @@ module Fluent
153
155
 
154
156
  # TODO: raise an exception if broken chunk is generated by recoverable situation
155
157
  unless msg.is_a?(Array)
156
- log.warn "incoming chunk is broken:", source: source, msg: msg
158
+ log.warn "incoming chunk is broken:", source: source_message(peeraddr), msg: msg
157
159
  return
158
160
  end
159
161
 
@@ -161,10 +163,10 @@ module Fluent
161
163
  entries = msg[1]
162
164
 
163
165
  if @chunk_size_limit && (chunk_size > @chunk_size_limit)
164
- log.warn "Input chunk size is larger than 'chunk_size_limit', dropped:", tag: tag, source: source, limit: @chunk_size_limit, size: chunk_size
166
+ log.warn "Input chunk size is larger than 'chunk_size_limit', dropped:", tag: tag, source: source_message(peeraddr), limit: @chunk_size_limit, size: chunk_size
165
167
  return
166
168
  elsif @chunk_size_warn_limit && (chunk_size > @chunk_size_warn_limit)
167
- log.warn "Input chunk size is larger than 'chunk_size_warn_limit':", tag: tag, source: source, limit: @chunk_size_warn_limit, size: chunk_size
169
+ log.warn "Input chunk size is larger than 'chunk_size_warn_limit':", tag: tag, source: source_message(peeraddr), limit: @chunk_size_warn_limit, size: chunk_size
168
170
  end
169
171
 
170
172
  if entries.class == String
@@ -172,13 +174,14 @@ module Fluent
172
174
  option = msg[2]
173
175
  size = (option && option['size']) || 0
174
176
  es = MessagePackEventStream.new(entries, nil, size.to_i)
175
- es = check_and_skip_invalid_event(tag, es, source) if @skip_invalid_event
177
+ es = check_and_skip_invalid_event(tag, es, peeraddr) if @skip_invalid_event
178
+ es = add_source_host(es, peeraddr[2]) if @source_hostname_key
176
179
  router.emit_stream(tag, es)
177
180
 
178
181
  elsif entries.class == Array
179
182
  # Forward
180
183
  es = if @skip_invalid_event
181
- check_and_skip_invalid_event(tag, entries, source)
184
+ check_and_skip_invalid_event(tag, entries, peeraddr)
182
185
  else
183
186
  es = MultiEventStream.new
184
187
  entries.each { |e|
@@ -190,6 +193,7 @@ module Fluent
190
193
  }
191
194
  es
192
195
  end
196
+ es = add_source_host(es, peeraddr[2]) if @source_hostname_key
193
197
  router.emit_stream(tag, es)
194
198
  option = msg[2]
195
199
 
@@ -198,11 +202,12 @@ module Fluent
198
202
  time = msg[1]
199
203
  record = msg[2]
200
204
  if @skip_invalid_event && invalid_event?(tag, time, record)
201
- log.warn "got invalid event and drop it:", source: source, tag: tag, time: time, record: record
205
+ log.warn "got invalid event and drop it:", source: source_message(peeraddr), tag: tag, time: time, record: record
202
206
  return msg[3] # retry never succeeded so return ack and drop incoming event.
203
207
  end
204
208
  return if record.nil?
205
209
  time = Engine.now if time.to_i == 0
210
+ record[@source_hostname_key] = peeraddr[2] if @source_hostname_key
206
211
  router.emit(tag, time, record)
207
212
  option = msg[3]
208
213
  end
@@ -215,11 +220,11 @@ module Fluent
215
220
  !((time.is_a?(Integer) || time.is_a?(::Fluent::EventTime)) && record.is_a?(Hash) && tag.is_a?(String))
216
221
  end
217
222
 
218
- def check_and_skip_invalid_event(tag, es, source)
223
+ def check_and_skip_invalid_event(tag, es, peeraddr)
219
224
  new_es = MultiEventStream.new
220
225
  es.each { |time, record|
221
226
  if invalid_event?(tag, time, record)
222
- log.warn "skip invalid event:", source: source, tag: tag, time: time, record: record
227
+ log.warn "skip invalid event:", source: source_message(peeraddr), tag: tag, time: time, record: record
223
228
  next
224
229
  end
225
230
  new_es.add(time, record)
@@ -227,16 +232,29 @@ module Fluent
227
232
  new_es
228
233
  end
229
234
 
235
+ def add_source_host(es, host)
236
+ new_es = MultiEventStream.new
237
+ es.each { |time, record|
238
+ record[@source_hostname_key] = host
239
+ new_es.add(time, record)
240
+ }
241
+ new_es
242
+ end
243
+
244
+ def source_message(peeraddr)
245
+ _, port, host, addr = peeraddr
246
+ "host: #{host}, addr: #{addr}, port: #{port}"
247
+ end
248
+
230
249
  class Handler < Coolio::Socket
231
250
  PEERADDR_FAILED = ["?", "?", "name resolusion failed", "?"]
232
251
 
233
252
  def initialize(io, linger_timeout, log, on_message)
234
253
  super(io)
235
254
 
255
+ @peeraddr = nil
236
256
  if io.is_a?(TCPSocket) # for unix domain socket support in the future
237
- _proto, port, host, addr = ( io.peeraddr rescue PEERADDR_FAILED )
238
- @source = "host: #{host}, addr: #{addr}, port: #{port}"
239
-
257
+ @peeraddr = (io.peeraddr rescue PEERADDR_FAILED)
240
258
  opt = [1, linger_timeout].pack('I!I!') # { int l_onoff; int l_linger; }
241
259
  io.setsockopt(Socket::SOL_SOCKET, Socket::SO_LINGER, opt)
242
260
  end
@@ -265,7 +283,7 @@ module Fluent
265
283
  @serializer = :to_json.to_proc
266
284
  @y = Yajl::Parser.new
267
285
  @y.on_parse_complete = lambda { |obj|
268
- option = @on_message.call(obj, @chunk_counter, @source)
286
+ option = @on_message.call(obj, @chunk_counter, @peeraddr)
269
287
  respond option
270
288
  @chunk_counter = 0
271
289
  }
@@ -293,7 +311,7 @@ module Fluent
293
311
  def on_read_msgpack(data)
294
312
  @chunk_counter += data.bytesize
295
313
  @u.feed_each(data) do |obj|
296
- option = @on_message.call(obj, @chunk_counter, @source)
314
+ option = @on_message.call(obj, @chunk_counter, @peeraddr)
297
315
  respond option if option
298
316
  @chunk_counter = 0
299
317
  end
@@ -20,13 +20,16 @@ require 'cgi'
20
20
 
21
21
  require 'cool.io'
22
22
 
23
- require 'fluent/input'
24
- require 'fluent/output'
25
- require 'fluent/filter'
23
+ require 'fluent/plugin/input'
24
+ require 'fluent/plugin/output'
25
+ require 'fluent/plugin/multi_output'
26
+ require 'fluent/plugin/filter'
26
27
 
27
- module Fluent
28
+ module Fluent::Plugin
28
29
  class MonitorAgentInput < Input
29
- Plugin.register_input('monitor_agent', self)
30
+ Fluent::Plugin.register_input('monitor_agent', self)
31
+
32
+ helpers :timer, :thread
30
33
 
31
34
  config_param :bind, :string, default: '0.0.0.0'
32
35
  config_param :port, :integer, default: 24220
@@ -142,7 +145,7 @@ module Fluent
142
145
 
143
146
  class LTSVMonitorServlet < MonitorServlet
144
147
  def process(req, res)
145
- list, opts = build_object(req, res)
148
+ list, _opts = build_object(req, res)
146
149
  return unless list
147
150
 
148
151
  normalized = JSON.parse(list.to_json)
@@ -204,36 +207,6 @@ module Fluent
204
207
  end
205
208
  end
206
209
 
207
- class TimerWatcher < Coolio::TimerWatcher
208
- def initialize(interval, log, &callback)
209
- @callback = callback
210
- @log = log
211
-
212
- # Avoid long shutdown time
213
- @num_call = 0
214
- if interval >= 10
215
- min_interval = 10
216
- @call_interval = interval / 10
217
- else
218
- min_interval = interval
219
- @call_interval = 0
220
- end
221
-
222
- super(min_interval, true)
223
- end
224
-
225
- def on_timer
226
- @num_call += 1
227
- if @num_call >= @call_interval
228
- @num_call = 0
229
- @callback.call
230
- end
231
- rescue => e
232
- @log.error e.to_s
233
- @log.error_backtrace
234
- end
235
- end
236
-
237
210
  def start
238
211
  super
239
212
 
@@ -248,76 +221,55 @@ module Fluent
248
221
  @srv.mount('/api/plugins.json', JSONMonitorServlet, self)
249
222
  @srv.mount('/api/config', LTSVConfigMonitorServlet, self)
250
223
  @srv.mount('/api/config.json', JSONConfigMonitorServlet, self)
251
- @thread = Thread.new {
224
+ thread_create :in_monitor_agent_servlet do
252
225
  @srv.start
253
- }
226
+ end
254
227
  if @tag
255
228
  log.debug "tag parameter is specified. Emit plugins info to '#{@tag}'"
256
229
 
257
- @loop = Coolio::Loop.new
258
230
  opts = {with_config: false}
259
- timer = TimerWatcher.new(@emit_interval, log) {
260
- es = MultiEventStream.new
261
- now = Engine.now
231
+ timer_execute(:in_monitor_agent_emit, @emit_interval, repeat: true) {
232
+ es = Fluent::MultiEventStream.new
233
+ now = Fluent::Engine.now
262
234
  plugins_info_all(opts).each { |record|
263
235
  es.add(now, record)
264
236
  }
265
237
  router.emit_stream(@tag, es)
266
238
  }
267
- @loop.attach(timer)
268
- @thread_for_emit = Thread.new(&method(:run))
269
239
  end
270
240
  end
271
241
 
272
- def run
273
- @loop.run
274
- rescue => e
275
- log.error "unexpected error", error: e.to_s
276
- log.error_backtrace
277
- end
278
-
279
242
  def shutdown
280
243
  if @srv
281
244
  @srv.shutdown
282
245
  @srv = nil
283
246
  end
284
- if @thread
285
- @thread.join
286
- @thread = nil
287
- end
288
- if @tag
289
- @loop.watchers.each { |w| w.detach }
290
- @loop.stop
291
- @loop = nil
292
- @thread_for_emit.join
293
- @thread_for_emit = nil
294
- end
295
247
 
296
248
  super
297
249
  end
298
250
 
299
251
  MONITOR_INFO = {
300
- 'output_plugin' => 'is_a?(::Fluent::Plugin::Output)',
301
- 'buffer_queue_length' => '@buffer.queue.size',
302
- 'buffer_total_queued_size' => '@buffer.stage_size + @buffer.queue_size',
303
- 'retry_count' => '@num_errors',
252
+ 'output_plugin' => ->(){ is_a?(::Fluent::Plugin::Output) },
253
+ 'buffer_queue_length' => ->(){ throw(:skip) unless instance_variable_defined?(:@buffer); @buffer.queue.size },
254
+ 'buffer_total_queued_size' => ->(){ throw(:skip) unless instance_variable_defined?(:@buffer); @buffer.stage_size },
255
+ 'retry_count' => ->(){ instance_variable_defined?(:@num_errors) ? @num_errors : nil },
304
256
  }
305
257
 
306
258
  def all_plugins
307
259
  array = []
308
260
 
309
261
  # get all input plugins
310
- array.concat Engine.root_agent.inputs
262
+ array.concat Fluent::Engine.root_agent.inputs
311
263
 
312
264
  # get all output plugins
313
- Engine.root_agent.outputs.each { |o|
265
+ Fluent::Engine.root_agent.outputs.each { |o|
314
266
  MonitorAgentInput.collect_children(o, array)
315
267
  }
316
268
  # get all filter plugins
317
- Engine.root_agent.filters.each { |f|
269
+ Fluent::Engine.root_agent.filters.each { |f|
318
270
  MonitorAgentInput.collect_children(f, array)
319
271
  }
320
- Engine.root_agent.labels.each { |name, l|
272
+ Fluent::Engine.root_agent.labels.each { |name, l|
321
273
  # TODO: Add label name to outputs / filters for identifing plugins
322
274
  l.outputs.each { |o| MonitorAgentInput.collect_children(o, array) }
323
275
  l.filters.each { |f| MonitorAgentInput.collect_children(f, array) }
@@ -330,7 +282,7 @@ module Fluent
330
282
  # from the plugin `pe` recursively
331
283
  def self.collect_children(pe, array=[])
332
284
  array << pe
333
- if pe.is_a?(MultiOutput) && pe.respond_to?(:outputs)
285
+ if pe.is_a?(Fluent::Plugin::MultiOutput) || pe.is_a?(Fluent::MultiOutput) && pe.respond_to?(:outputs)
334
286
  pe.outputs.each {|nop|
335
287
  collect_children(nop, array)
336
288
  }
@@ -341,10 +293,10 @@ module Fluent
341
293
  # try to match the tag and get the info from the matched output plugin
342
294
  # TODO: Support output in label
343
295
  def plugin_info_by_tag(tag, opts={})
344
- matches = Engine.root_agent.event_router.instance_variable_get(:@match_rules)
296
+ matches = Fluent::Engine.root_agent.event_router.instance_variable_get(:@match_rules)
345
297
  matches.each { |rule|
346
298
  if rule.match?(tag)
347
- if rule.collector.is_a?(Output)
299
+ if rule.collector.is_a?(Fluent::Plugin::Output) || rule.collector.is_a?(Fluent::Output)
348
300
  return get_monitor_info(rule.collector, opts)
349
301
  end
350
302
  end
@@ -381,8 +333,7 @@ module Fluent
381
333
  }
382
334
  end
383
335
 
384
- # TODO: use %i() after drop ruby v1.9.3 support.
385
- IGNORE_ATTRIBUTES = %W(@config_root_section @config @masked_config).map(&:to_sym)
336
+ IGNORE_ATTRIBUTES = %i(@config_root_section @config @masked_config)
386
337
 
387
338
  # get monitor info from the plugin `pe` and return a hash object
388
339
  def get_monitor_info(pe, opts={})
@@ -397,8 +348,11 @@ module Fluent
397
348
  # run MONITOR_INFO in plugins' instance context and store the info to obj
398
349
  MONITOR_INFO.each_pair {|key,code|
399
350
  begin
400
- obj[key] = pe.instance_eval(code)
401
- rescue
351
+ catch(:skip) do
352
+ obj[key] = pe.instance_exec(&code)
353
+ end
354
+ rescue => e
355
+ log.warn "unexpected error in monitoring plugins", key: key, plugin: pe.class, error: e
402
356
  end
403
357
  }
404
358