rollbar 2.19.1 → 2.27.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (86) hide show
  1. checksums.yaml +5 -5
  2. data/.github/pull_request_template.md +25 -0
  3. data/.rubocop.yml +168 -0
  4. data/.travis.yml +63 -34
  5. data/Appraisals +10 -10
  6. data/Gemfile +35 -14
  7. data/README.md +5 -2
  8. data/Rakefile +0 -0
  9. data/data/rollbar.snippet.js +1 -1
  10. data/docs/configuration.md +17 -0
  11. data/gemfiles/rails30.gemfile +10 -10
  12. data/gemfiles/rails31.gemfile +10 -9
  13. data/gemfiles/rails32.gemfile +10 -9
  14. data/gemfiles/rails40.gemfile +10 -9
  15. data/gemfiles/rails41.gemfile +10 -9
  16. data/gemfiles/rails42.gemfile +12 -9
  17. data/gemfiles/rails50.gemfile +21 -14
  18. data/gemfiles/rails51.gemfile +21 -14
  19. data/gemfiles/rails52.gemfile +15 -12
  20. data/gemfiles/rails60.gemfile +67 -0
  21. data/lib/generators/rollbar/rollbar_generator.rb +1 -1
  22. data/lib/rails/rollbar_runner.rb +4 -2
  23. data/lib/rollbar/capistrano.rb +1 -1
  24. data/lib/rollbar/capistrano3.rb +6 -3
  25. data/lib/rollbar/capistrano_tasks.rb +29 -21
  26. data/lib/rollbar/configuration.rb +86 -16
  27. data/lib/rollbar/delay/girl_friday.rb +4 -8
  28. data/lib/rollbar/delay/resque.rb +3 -6
  29. data/lib/rollbar/delay/sidekiq.rb +4 -10
  30. data/lib/rollbar/delay/sucker_punch.rb +16 -19
  31. data/lib/rollbar/delay/thread.rb +16 -2
  32. data/lib/rollbar/deploy.rb +52 -29
  33. data/lib/rollbar/encoding/encoder.rb +17 -10
  34. data/lib/rollbar/exception_reporter.rb +19 -5
  35. data/lib/rollbar/item/backtrace.rb +13 -3
  36. data/lib/rollbar/item/frame.rb +9 -1
  37. data/lib/rollbar/item/locals.rb +100 -0
  38. data/lib/rollbar/item.rb +56 -17
  39. data/lib/rollbar/json.rb +6 -51
  40. data/lib/rollbar/language_support.rb +4 -20
  41. data/lib/rollbar/lazy_store.rb +5 -5
  42. data/lib/rollbar/logger.rb +1 -0
  43. data/lib/rollbar/logger_proxy.rb +6 -2
  44. data/lib/rollbar/middleware/js.rb +28 -18
  45. data/lib/rollbar/middleware/rack.rb +4 -1
  46. data/lib/rollbar/middleware/rails/rollbar.rb +10 -1
  47. data/lib/rollbar/notifier/trace_with_bindings.rb +65 -0
  48. data/lib/rollbar/notifier.rb +225 -89
  49. data/lib/rollbar/plugin.rb +54 -6
  50. data/lib/rollbar/plugins/active_job.rb +6 -2
  51. data/lib/rollbar/plugins/basic_socket.rb +21 -6
  52. data/lib/rollbar/plugins/delayed_job/job_data.rb +3 -3
  53. data/lib/rollbar/plugins/delayed_job/plugin.rb +13 -3
  54. data/lib/rollbar/plugins/error_context.rb +11 -0
  55. data/lib/rollbar/plugins/goalie.rb +11 -3
  56. data/lib/rollbar/plugins/rails/controller_methods.rb +15 -3
  57. data/lib/rollbar/plugins/rake.rb +2 -2
  58. data/lib/rollbar/plugins/sidekiq/plugin.rb +5 -4
  59. data/lib/rollbar/plugins.rb +7 -1
  60. data/lib/rollbar/rake_tasks.rb +4 -148
  61. data/lib/rollbar/request_data_extractor.rb +31 -21
  62. data/lib/rollbar/rollbar_test.rb +36 -0
  63. data/lib/rollbar/scrubbers/params.rb +19 -18
  64. data/lib/rollbar/scrubbers/url.rb +18 -9
  65. data/lib/rollbar/scrubbers.rb +3 -3
  66. data/lib/rollbar/truncation/frames_strategy.rb +1 -1
  67. data/lib/rollbar/truncation/min_body_strategy.rb +2 -3
  68. data/lib/rollbar/truncation/mixin.rb +1 -1
  69. data/lib/rollbar/truncation/remove_any_key_strategy.rb +123 -0
  70. data/lib/rollbar/truncation/remove_extra_strategy.rb +35 -0
  71. data/lib/rollbar/truncation/remove_request_strategy.rb +21 -0
  72. data/lib/rollbar/truncation/strings_strategy.rb +6 -5
  73. data/lib/rollbar/truncation.rb +9 -2
  74. data/lib/rollbar/util/hash.rb +15 -0
  75. data/lib/rollbar/util/ip_anonymizer.rb +8 -7
  76. data/lib/rollbar/util/ip_obfuscator.rb +1 -1
  77. data/lib/rollbar/util.rb +6 -2
  78. data/lib/rollbar/version.rb +1 -1
  79. data/lib/rollbar.rb +2 -3
  80. data/lib/tasks/benchmark.rake +103 -0
  81. data/rollbar.gemspec +13 -5
  82. data/spec/support/rollbar_api.rb +67 -0
  83. metadata +21 -23
  84. data/gemfiles/ruby_1_8_and_1_9_2.gemfile +0 -49
  85. data/lib/rollbar/json/default.rb +0 -11
  86. data/lib/rollbar/json/oj.rb +0 -16
@@ -8,6 +8,7 @@ require 'rollbar/delay/girl_friday'
8
8
  require 'rollbar/delay/thread'
9
9
  require 'rollbar/logger_proxy'
10
10
  require 'rollbar/item'
11
+ require 'rollbar/notifier/trace_with_bindings'
11
12
  require 'ostruct'
12
13
 
13
14
  module Rollbar
@@ -18,7 +19,9 @@ module Rollbar
18
19
  attr_accessor :last_report
19
20
  attr_accessor :scope_object
20
21
 
21
- @file_semaphore = Mutex.new
22
+ MUTEX = Mutex.new
23
+ EXTENSION_REGEXP = /.rollbar\z/.freeze
24
+ FAILSAFE_STRING_LENGTH = 10_000
22
25
 
23
26
  def initialize(parent_notifier = nil, payload_options = nil, scope = nil)
24
27
  if parent_notifier
@@ -41,21 +44,21 @@ module Rollbar
41
44
  # Similar to configure below, but used only internally within the gem
42
45
  # to configure it without initializing any of the third party hooks
43
46
  def preconfigure
44
- yield(configuration)
47
+ yield(configuration.configured_options)
45
48
  end
46
49
 
47
50
  # Configures the notifier instance
48
51
  def configure
49
52
  configuration.enabled = true if configuration.enabled.nil?
50
53
 
51
- yield(configuration)
54
+ yield(configuration.configured_options)
52
55
  end
53
56
 
54
57
  def reconfigure
55
58
  self.configuration = Configuration.new
56
59
  configuration.enabled = true
57
60
 
58
- yield(configuration)
61
+ yield(configuration.configured_options)
59
62
  end
60
63
 
61
64
  def unconfigure
@@ -95,17 +98,17 @@ module Rollbar
95
98
  # @yield Block which exceptions won't be reported.
96
99
  def silenced
97
100
  yield
98
- rescue => e
101
+ rescue StandardError => e
99
102
  e.instance_variable_set(:@_rollbar_do_not_report, true)
100
103
  raise
101
104
  end
102
105
 
103
106
  # Sends a report to Rollbar.
104
107
  #
105
- # Accepts any number of arguments. The last String argument will become
106
- # the message or description of the report. The last Exception argument
107
- # will become the associated exception for the report. The last hash
108
- # argument will be used as the extra data for the report.
108
+ # Accepts a level string plus any number of arguments. The last String
109
+ # argument will become the message or description of the report. The last
110
+ # Exception argument will become the associated exception for the report.
111
+ # The last hash argument will be used as the extra data for the report.
109
112
  #
110
113
  # If the extra hash contains a symbol key :custom_data_method_context
111
114
  # the value of the key will be used as the context for
@@ -116,17 +119,17 @@ module Rollbar
116
119
  # begin
117
120
  # foo = bar
118
121
  # rescue => e
119
- # Rollbar.log(e)
122
+ # Rollbar.log('error', e)
120
123
  # end
121
124
  #
122
125
  # @example
123
- # Rollbar.log('This is a simple log message')
126
+ # Rollbar.log('info', 'This is a simple log message')
124
127
  #
125
128
  # @example
126
- # Rollbar.log(e, 'This is a description of the exception')
129
+ # Rollbar.log('error', e, 'This is a description of the exception')
127
130
  #
128
131
  def log(level, *args)
129
- return 'disabled' unless configuration.enabled
132
+ return 'disabled' unless enabled?
130
133
 
131
134
  message, exception, extra, context = extract_arguments(args)
132
135
  use_exception_level_filters = use_exception_level_filters?(extra)
@@ -146,13 +149,25 @@ module Rollbar
146
149
  level = lookup_exception_level(level, exception,
147
150
  use_exception_level_filters)
148
151
 
149
- begin
150
- report(level, message, exception, extra, context)
151
- rescue StandardError, SystemStackError => e
152
- report_internal_error(e)
152
+ ret = report_with_rescue(level, message, exception, extra, context)
153
153
 
154
- 'error'
155
- end
154
+ raise(exception) if configuration.raise_on_error && exception
155
+
156
+ ret
157
+ end
158
+
159
+ def report_with_rescue(level, message, exception, extra, context)
160
+ report(level, message, exception, extra, context)
161
+ rescue StandardError, SystemStackError => e
162
+ original_error = {
163
+ :message => message,
164
+ :exception => exception,
165
+ :configuration => configuration
166
+ }
167
+
168
+ report_internal_error(e, original_error)
169
+
170
+ 'error'
156
171
  end
157
172
 
158
173
  # See log() above
@@ -185,21 +200,28 @@ module Rollbar
185
200
  log('critical', *args)
186
201
  end
187
202
 
203
+ def enabled?
204
+ # Require access_token so we don't try to send events when unconfigured.
205
+ configuration.enabled && configuration.access_token && !configuration.access_token.empty?
206
+ end
207
+
188
208
  def process_item(item)
189
209
  if configuration.write_to_file
190
210
  if configuration.use_async
191
- @file_semaphore.synchronize do
192
- write_item(item)
211
+ MUTEX.synchronize do
212
+ do_write_item(item)
193
213
  end
194
214
  else
195
- write_item(item)
215
+ do_write_item(item)
196
216
  end
197
217
  else
198
218
  send_item(item)
199
219
  end
200
- rescue => e
220
+ rescue StandardError => e
201
221
  log_error("[Rollbar] Error processing the item: #{e.class}, #{e.message}. Item: #{item.payload.inspect}")
202
- raise e
222
+ raise e unless via_failsafe?(item)
223
+
224
+ log_error('[Rollbar] Item has already failed. Not re-raising')
203
225
  end
204
226
 
205
227
  # We will reraise exceptions in this method so async queues
@@ -226,17 +248,20 @@ module Rollbar
226
248
  # Using Rollbar.silenced we avoid the above behavior but Sidekiq
227
249
  # will have a chance to retry the original job.
228
250
  def process_from_async_handler(payload)
229
- payload = Rollbar::JSON.load(payload) if payload.is_a?(String)
230
-
231
- item = Item.build_with(payload,
232
- :notifier => self,
233
- :configuration => configuration,
234
- :logger => logger)
235
-
236
251
  Rollbar.silenced do
237
252
  begin
238
- process_item(item)
239
- rescue => e
253
+ if payload.is_a?(String)
254
+ # The final payload has already been built.
255
+ send_body(payload)
256
+ else
257
+ item = Item.build_with(payload,
258
+ :notifier => self,
259
+ :configuration => configuration,
260
+ :logger => logger)
261
+
262
+ process_item(item)
263
+ end
264
+ rescue StandardError => e
240
265
  report_internal_error(e)
241
266
 
242
267
  raise
@@ -244,35 +269,34 @@ module Rollbar
244
269
  end
245
270
  end
246
271
 
247
- def send_failsafe(message, exception, uuid = nil, host = nil)
248
- exception_reason = failsafe_reason(message, exception)
249
-
250
- log_error "[Rollbar] Sending failsafe response due to #{exception_reason}"
251
-
252
- body = failsafe_body(exception_reason)
253
-
254
- failsafe_data = {
272
+ def failsafe_initial_data(exception_reason)
273
+ {
255
274
  :level => 'error',
256
275
  :environment => configuration.environment.to_s,
257
276
  :body => {
258
277
  :message => {
259
- :body => body
278
+ :body => failsafe_body(exception_reason)
260
279
  }
261
280
  },
262
281
  :notifier => {
263
282
  :name => 'rollbar-gem',
264
283
  :version => VERSION
265
284
  },
266
- :custom => {
267
- :orig_uuid => uuid,
268
- :orig_host => host
269
- },
270
285
  :internal => true,
271
- :failsafe => true
286
+ 'failsafe' => true
272
287
  }
288
+ end
289
+
290
+ def send_failsafe(message, exception, original_error = nil)
291
+ exception_reason = failsafe_reason(message, exception)
292
+
293
+ log_error "[Rollbar] Sending failsafe response due to #{exception_reason}"
294
+
295
+ failsafe_data = failsafe_initial_data(exception_reason)
296
+
297
+ failsafe_add_original_error_data(failsafe_data[:notifier], original_error)
273
298
 
274
299
  failsafe_payload = {
275
- 'access_token' => configuration.access_token,
276
300
  'data' => failsafe_data
277
301
  }
278
302
 
@@ -281,16 +305,69 @@ module Rollbar
281
305
  :notifier => self,
282
306
  :configuration => configuration,
283
307
  :logger => logger)
284
- schedule_item(item)
285
- rescue => e
308
+
309
+ process_item(item)
310
+ log_and_return_item_data(item)
311
+ rescue StandardError => e
286
312
  log_error "[Rollbar] Error sending failsafe : #{e}"
287
313
  end
288
314
 
289
315
  failsafe_payload
290
316
  end
291
317
 
318
+ def failsafe_add_original_error_data(payload_notifier, original_error)
319
+ return unless original_error
320
+
321
+ payload_notifier[:diagnostic] ||= {}
322
+
323
+ add_original_host(payload_notifier[:diagnostic], original_error)
324
+ add_original_uuid(payload_notifier[:diagnostic], original_error)
325
+ add_original_message(payload_notifier[:diagnostic], original_error)
326
+ add_original_error(payload_notifier[:diagnostic], original_error)
327
+ add_configured_options(payload_notifier, original_error)
328
+ end
329
+
330
+ def add_original_message(diagnostic, original_error)
331
+ diagnostic[:original_message] = original_error[:message].truncate(FAILSAFE_STRING_LENGTH) if original_error[:message]
332
+
333
+ rescue StandardError => e
334
+ diagnostic[:original_message] = "Failed: #{e.message}"
335
+ end
336
+
337
+ def add_original_error(diagnostic, original_error)
338
+ if original_error[:exception]
339
+ backtrace = original_error[:exception].backtrace
340
+ message = original_error[:exception].message
341
+ diagnostic[:original_error] = {
342
+ :message => message && message.truncate(FAILSAFE_STRING_LENGTH),
343
+ :stack => backtrace && backtrace.join(', ').truncate(FAILSAFE_STRING_LENGTH)
344
+ }
345
+ end
346
+
347
+ rescue StandardError => e
348
+ diagnostic[:original_error] = "Failed: #{e.message}"
349
+ end
350
+
351
+ def add_configured_options(payload_notifier, original_error)
352
+ if original_error[:configuration]
353
+ configured = original_error[:configuration].configured_options.configured
354
+ payload_notifier[:configured_options] = ::JSON.generate(configured).truncate(FAILSAFE_STRING_LENGTH)
355
+ end
356
+
357
+ rescue StandardError => e
358
+ payload_notifier[:configured_options] = "Failed: #{e.message}"
359
+ end
360
+
361
+ def add_original_host(diagnostic, original_error)
362
+ diagnostic[:original_host] = original_error[:host] if original_error[:host]
363
+ end
364
+
365
+ def add_original_uuid(diagnostic, original_error)
366
+ diagnostic[:original_uuid] = original_error[:uuid] if original_error[:uuid]
367
+ end
368
+
292
369
  ## Logging
293
- %w(debug info warn error).each do |level|
370
+ %w[debug info warn error].each do |level|
294
371
  define_method(:"log_#{level}") do |message|
295
372
  logger.send(level, message)
296
373
  end
@@ -300,6 +377,30 @@ module Rollbar
300
377
  @logger ||= LoggerProxy.new(configuration.logger)
301
378
  end
302
379
 
380
+ def trace_with_bindings
381
+ @trace_with_bindings ||= TraceWithBindings.new
382
+ end
383
+
384
+ def exception_bindings
385
+ trace_with_bindings.exception_frames
386
+ end
387
+
388
+ def current_bindings
389
+ trace_with_bindings.frames
390
+ end
391
+
392
+ def enable_locals?
393
+ configuration.locals[:enabled] && [:app, :all].include?(configuration.send_extra_frame_data)
394
+ end
395
+
396
+ def enable_locals
397
+ trace_with_bindings.enable if enable_locals?
398
+ end
399
+
400
+ def disable_locals
401
+ trace_with_bindings.disable if enable_locals?
402
+ end
403
+
303
404
  private
304
405
 
305
406
  def use_exception_level_filters?(options)
@@ -326,7 +427,7 @@ module Rollbar
326
427
  return 'ignored' if status == 'ignored'
327
428
  rescue Rollbar::Ignore
328
429
  raise
329
- rescue => e
430
+ rescue StandardError => e
330
431
  log_error("[Rollbar] Error calling the `before_process` hook: #{e}")
331
432
 
332
433
  break
@@ -399,43 +500,52 @@ module Rollbar
399
500
 
400
501
  return 'ignored' if item.ignored?
401
502
 
402
- schedule_item(item)
503
+ schedule_item(item) if configuration.transmit
504
+
505
+ log_and_return_item_data(item)
506
+ end
403
507
 
508
+ def log_and_return_item_data(item)
404
509
  data = item['data']
405
510
  log_instance_link(data)
406
511
  Rollbar.last_report = data
512
+ log_data(data) if configuration.log_payload
407
513
 
408
514
  data
409
515
  end
410
516
 
517
+ def log_data(data)
518
+ log_info "[Rollbar] Data: #{data}"
519
+ end
520
+
411
521
  # Reports an internal error in the Rollbar library. This will be reported within the configured
412
522
  # Rollbar project. We'll first attempt to provide a report including the exception traceback.
413
523
  # If that fails, we'll fall back to a more static failsafe response.
414
- def report_internal_error(exception)
524
+ def report_internal_error(exception, original_error = nil)
415
525
  log_error '[Rollbar] Reporting internal error encountered while sending data to Rollbar.'
416
526
 
417
527
  configuration.execute_hook(:on_report_internal_error, exception)
418
528
 
419
529
  begin
420
530
  item = build_item('error', nil, exception, { :internal => true }, nil)
421
- rescue => e
422
- send_failsafe('build_item in exception_data', e)
531
+ rescue StandardError => e
532
+ send_failsafe('build_item in exception_data', e, original_error)
423
533
  log_error "[Rollbar] Exception: #{exception}"
424
534
  return
425
535
  end
426
536
 
427
537
  begin
428
538
  process_item(item)
429
- rescue => e
430
- send_failsafe('error in process_item', e)
539
+ rescue StandardError => e
540
+ send_failsafe('error in process_item', e, original_error)
431
541
  log_error "[Rollbar] Item: #{item}"
432
542
  return
433
543
  end
434
544
 
435
545
  begin
436
546
  log_instance_link(item['data'])
437
- rescue => e
438
- send_failsafe('error logging instance link', e)
547
+ rescue StandardError => e
548
+ send_failsafe('error logging instance link', e, original_error)
439
549
  log_error "[Rollbar] Item: #{item}"
440
550
  return
441
551
  end
@@ -464,14 +574,18 @@ module Rollbar
464
574
 
465
575
  ## Delivery functions
466
576
 
467
- def send_item_using_eventmachine(item, uri)
468
- body = item.dump
469
- return unless body
577
+ def send_using_eventmachine(body)
578
+ uri = URI.parse(configuration.endpoint)
470
579
 
471
- headers = { 'X-Rollbar-Access-Token' => item['access_token'] }
580
+ headers = { 'X-Rollbar-Access-Token' => configuration.access_token }
472
581
  options = http_proxy_for_em(uri)
473
582
  req = EventMachine::HttpRequest.new(uri.to_s, options).post(:body => body, :head => headers)
474
583
 
584
+ eventmachine_callback(req)
585
+ eventmachine_errback(req)
586
+ end
587
+
588
+ def eventmachine_callback(req)
475
589
  req.callback do
476
590
  if req.response_header.status == 200
477
591
  log_info '[Rollbar] Success'
@@ -480,7 +594,9 @@ module Rollbar
480
594
  log_info "[Rollbar] Response: #{req.response}"
481
595
  end
482
596
  end
597
+ end
483
598
 
599
+ def eventmachine_errback(req)
484
600
  req.errback do
485
601
  log_warning "[Rollbar] Call to API failed, status code: #{req.response_header.status}"
486
602
  log_info "[Rollbar] Error's response: #{req.response}"
@@ -493,14 +609,20 @@ module Rollbar
493
609
  body = item.dump
494
610
  return unless body
495
611
 
496
- uri = URI.parse(configuration.endpoint)
497
-
498
612
  if configuration.use_eventmachine
499
- send_item_using_eventmachine(item, uri)
613
+ send_using_eventmachine(body)
500
614
  return
501
615
  end
502
616
 
503
- handle_response(do_post(uri, body, item['access_token']))
617
+ send_body(body)
618
+ end
619
+
620
+ def send_body(body)
621
+ log_info '[Rollbar] Sending json'
622
+
623
+ uri = URI.parse(configuration.endpoint)
624
+
625
+ handle_response(do_post(uri, body, configuration.access_token))
504
626
  end
505
627
 
506
628
  def do_post(uri, body, access_token)
@@ -512,8 +634,6 @@ module Rollbar
512
634
 
513
635
  if uri.scheme == 'https'
514
636
  http.use_ssl = true
515
- # This is needed to have 1.8.7 passing tests
516
- http.ca_file = ENV['ROLLBAR_SSL_CERT_FILE'] if ENV.key?('ROLLBAR_SSL_CERT_FILE')
517
637
  http.verify_mode = ssl_verify_mode
518
638
  end
519
639
 
@@ -594,7 +714,7 @@ module Rollbar
594
714
  end
595
715
 
596
716
  def skip_retries?
597
- Rollbar::LanguageSupport.ruby_18? || Rollbar::LanguageSupport.ruby_19?
717
+ Rollbar::LanguageSupport.ruby_19?
598
718
  end
599
719
 
600
720
  def handle_response(response)
@@ -615,27 +735,24 @@ module Rollbar
615
735
  end
616
736
  end
617
737
 
618
- def write_item(item)
619
- if configuration.use_async
620
- @file_semaphore.synchronize do
621
- do_write_item(item)
622
- end
623
- else
624
- do_write_item(item)
625
- end
626
- end
627
-
628
738
  def do_write_item(item)
629
739
  log_info '[Rollbar] Writing item to file'
630
740
 
631
741
  body = item.dump
632
742
  return unless body
633
743
 
744
+ file_name = if configuration.files_with_pid_name_enabled
745
+ configuration.filepath.gsub(EXTENSION_REGEXP, "_#{Process.pid}\\0")
746
+ else
747
+ configuration.filepath
748
+ end
749
+
634
750
  begin
635
- @file ||= File.open(configuration.filepath, 'a')
751
+ @file ||= File.open(file_name, 'a')
636
752
 
637
753
  @file.puts(body)
638
754
  @file.flush
755
+ update_file(@file, file_name)
639
756
 
640
757
  log_info '[Rollbar] Success'
641
758
  rescue IOError => e
@@ -643,6 +760,18 @@ module Rollbar
643
760
  end
644
761
  end
645
762
 
763
+ def update_file(file, file_name)
764
+ return unless configuration.files_processed_enabled
765
+
766
+ time_now = Time.now
767
+ return if configuration.files_processed_duration > time_now - file.birthtime && file.size < configuration.files_processed_size
768
+
769
+ new_file_name = file_name.gsub(EXTENSION_REGEXP, "_processed_#{time_now.to_i}\\0")
770
+ File.rename(file, new_file_name)
771
+ file.close
772
+ @file = File.open(file_name, 'a')
773
+ end
774
+
646
775
  def failsafe_reason(message, exception)
647
776
  body = ''
648
777
 
@@ -653,17 +782,17 @@ module Rollbar
653
782
 
654
783
  exception_info = exception.class.name
655
784
  # #to_s and #message defaults to class.to_s. Add message only if add valuable info.
656
- exception_info += %(: "#{exception.message}") if exception.message != exception.class.to_s
785
+ exception_info += %[: "#{exception.message}"] if exception.message != exception.class.to_s
657
786
  exception_info += " in #{nearest_frame}" if nearest_frame
658
787
 
659
788
  body += "#{exception_info}: #{message}"
660
- rescue
789
+ rescue StandardError
661
790
  log_error('[Rollbar] Error building failsafe exception message')
662
791
  end
663
792
  else
664
793
  begin
665
794
  body += message.to_s
666
- rescue
795
+ rescue StandardError
667
796
  log_error('[Rollbar] Error building failsafe message')
668
797
  end
669
798
  end
@@ -694,9 +823,12 @@ module Rollbar
694
823
  end
695
824
 
696
825
  def process_async_item(item)
826
+ # Send async payloads as JSON string when async_json_payload is set.
827
+ payload = configuration.async_json_payload ? item.dump : item.payload
828
+
697
829
  configuration.async_handler ||= default_async_handler
698
- configuration.async_handler.call(item.payload)
699
- rescue
830
+ configuration.async_handler.call(payload)
831
+ rescue StandardError
700
832
  if configuration.failover_handlers.empty?
701
833
  log_error '[Rollbar] Async handler failed, and there are no failover handlers configured. See the docs for "failover_handlers"'
702
834
  return
@@ -713,7 +845,7 @@ module Rollbar
713
845
  failover_handlers.each do |handler|
714
846
  begin
715
847
  handler.call(item.payload)
716
- rescue
848
+ rescue StandardError
717
849
  next unless handler == failover_handlers.last
718
850
 
719
851
  log_error "[Rollbar] All failover handlers failed while processing item: #{Rollbar::JSON.dump(item.payload)}"
@@ -721,7 +853,7 @@ module Rollbar
721
853
  end
722
854
  end
723
855
 
724
- alias_method :log_warning, :log_warn
856
+ alias log_warning log_warn
725
857
 
726
858
  def log_instance_link(data)
727
859
  return unless data[:uuid]
@@ -729,5 +861,9 @@ module Rollbar
729
861
  uuid_url = Util.uuid_rollbar_url(data, configuration)
730
862
  log_info "[Rollbar] Details: #{uuid_url} (only available if report was successful)"
731
863
  end
864
+
865
+ def via_failsafe?(item)
866
+ item.payload.fetch('data', {}).fetch('failsafe', false)
867
+ end
732
868
  end
733
869
  end
@@ -7,6 +7,8 @@ module Rollbar
7
7
  attr_reader :name
8
8
  attr_reader :dependencies
9
9
  attr_reader :callables
10
+ attr_reader :revert_callables
11
+ attr_accessor :on_demand
10
12
  attr_accessor :loaded
11
13
 
12
14
  private :loaded=
@@ -15,31 +17,73 @@ module Rollbar
15
17
  @name = name
16
18
  @dependencies = []
17
19
  @callables = []
20
+ @revert_callables = []
18
21
  @loaded = false
22
+ @on_demand = false
23
+ end
24
+
25
+ def load_on_demand
26
+ @on_demand = true
19
27
  end
20
28
 
21
29
  def configuration
22
30
  Rollbar.configuration
23
31
  end
24
32
 
33
+ def load_scoped!(transparent = false)
34
+ if transparent
35
+ load! if load?
36
+
37
+ result = yield
38
+
39
+ unload! if loaded
40
+ else
41
+ return unless load?
42
+
43
+ load!
44
+
45
+ result = yield
46
+
47
+ unload!
48
+ end
49
+
50
+ result
51
+ end
52
+
25
53
  def load!
26
54
  return unless load?
27
55
 
28
56
  begin
29
57
  callables.each(&:call)
30
- rescue => e
58
+ rescue StandardError => e
31
59
  log_loading_error(e)
32
60
  ensure
33
61
  self.loaded = true
34
62
  end
35
63
  end
36
64
 
65
+ def unload!
66
+ return unless loaded
67
+
68
+ begin
69
+ revert_callables.each(&:call)
70
+ rescue StandardError => e
71
+ log_unloading_error(e)
72
+ ensure
73
+ self.loaded = false
74
+ end
75
+ end
76
+
37
77
  def execute(&block)
38
78
  callables << block
39
79
  end
40
80
 
41
- def execute!(&block)
42
- block.call if load?
81
+ def execute!
82
+ yield if load?
83
+ end
84
+
85
+ def revert(&block)
86
+ revert_callables << block
43
87
  end
44
88
 
45
89
  private
@@ -61,7 +105,7 @@ module Rollbar
61
105
 
62
106
  def load?
63
107
  !loaded && dependencies_satisfy?
64
- rescue => e
108
+ rescue StandardError => e
65
109
  log_loading_error(e)
66
110
 
67
111
  false
@@ -71,8 +115,12 @@ module Rollbar
71
115
  dependencies.all?(&:call)
72
116
  end
73
117
 
74
- def log_loading_error(e)
75
- Rollbar.log_error("Error trying to load plugin '#{name}': #{e.class}, #{e.message}")
118
+ def log_loading_error(error)
119
+ Rollbar.log_error("Error trying to load plugin '#{name}': #{error.class}, #{error.message}")
120
+ end
121
+
122
+ def log_unloading_error(error)
123
+ Rollbar.log_error("Error trying to unload plugin '#{name}': #{error.class}, #{error.message}")
76
124
  end
77
125
  end
78
126
  end