rollbar 2.22.1 → 3.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (92) hide show
  1. checksums.yaml +4 -4
  2. data/.github/pull_request_template.md +34 -0
  3. data/.github/workflows/ci.yml +104 -0
  4. data/.rubocop.yml +185 -33
  5. data/Gemfile +26 -28
  6. data/README.md +32 -8
  7. data/data/rollbar.snippet.js +1 -1
  8. data/docs/configuration.md +8 -0
  9. data/gemfiles/rails30.gemfile +17 -35
  10. data/gemfiles/rails31.gemfile +20 -37
  11. data/gemfiles/rails32.gemfile +13 -31
  12. data/gemfiles/rails40.gemfile +12 -32
  13. data/gemfiles/rails41.gemfile +11 -31
  14. data/gemfiles/rails42.gemfile +12 -32
  15. data/gemfiles/rails50.gemfile +16 -30
  16. data/gemfiles/rails51.gemfile +16 -30
  17. data/gemfiles/rails52.gemfile +10 -19
  18. data/gemfiles/rails60.gemfile +10 -25
  19. data/gemfiles/rails61.gemfile +52 -0
  20. data/gemfiles/rails70.gemfile +52 -0
  21. data/lib/generators/rollbar/rollbar_generator.rb +18 -14
  22. data/lib/rails/rollbar_runner.rb +11 -20
  23. data/lib/rollbar/capistrano.rb +17 -9
  24. data/lib/rollbar/capistrano3.rb +8 -2
  25. data/lib/rollbar/capistrano_tasks.rb +44 -8
  26. data/lib/rollbar/configuration.rb +138 -84
  27. data/lib/rollbar/delay/girl_friday.rb +3 -7
  28. data/lib/rollbar/delay/resque.rb +2 -3
  29. data/lib/rollbar/delay/shoryuken.rb +4 -3
  30. data/lib/rollbar/delay/sidekiq.rb +5 -5
  31. data/lib/rollbar/delay/sucker_punch.rb +4 -6
  32. data/lib/rollbar/delay/thread.rb +17 -2
  33. data/lib/rollbar/deploy.rb +8 -7
  34. data/lib/rollbar/encoding/encoder.rb +17 -6
  35. data/lib/rollbar/encoding.rb +2 -7
  36. data/lib/rollbar/exception_reporter.rb +17 -8
  37. data/lib/rollbar/item/backtrace.rb +22 -10
  38. data/lib/rollbar/item/frame.rb +8 -5
  39. data/lib/rollbar/item/locals.rb +49 -2
  40. data/lib/rollbar/item.rb +80 -50
  41. data/lib/rollbar/json.rb +2 -1
  42. data/lib/rollbar/language_support.rb +0 -6
  43. data/lib/rollbar/lazy_store.rb +3 -7
  44. data/lib/rollbar/logger.rb +2 -0
  45. data/lib/rollbar/logger_proxy.rb +3 -1
  46. data/lib/rollbar/middleware/js/json_value.rb +15 -5
  47. data/lib/rollbar/middleware/js.rb +70 -38
  48. data/lib/rollbar/middleware/rack/builder.rb +3 -3
  49. data/lib/rollbar/middleware/rack/test_session.rb +3 -3
  50. data/lib/rollbar/middleware/rack.rb +4 -4
  51. data/lib/rollbar/middleware/rails/rollbar.rb +9 -6
  52. data/lib/rollbar/middleware/rails/show_exceptions.rb +8 -4
  53. data/lib/rollbar/notifier/trace_with_bindings.rb +13 -3
  54. data/lib/rollbar/notifier.rb +309 -172
  55. data/lib/rollbar/plugin.rb +8 -8
  56. data/lib/rollbar/plugins/active_job.rb +20 -3
  57. data/lib/rollbar/plugins/delayed_job/plugin.rb +19 -2
  58. data/lib/rollbar/plugins/error_context.rb +11 -0
  59. data/lib/rollbar/plugins/goalie.rb +27 -16
  60. data/lib/rollbar/plugins/rails/controller_methods.rb +18 -14
  61. data/lib/rollbar/plugins/rails/railtie30.rb +2 -1
  62. data/lib/rollbar/plugins/rails/railtie32.rb +2 -1
  63. data/lib/rollbar/plugins/rails/railtie_mixin.rb +2 -2
  64. data/lib/rollbar/plugins/rails.rb +5 -2
  65. data/lib/rollbar/plugins/rake.rb +2 -1
  66. data/lib/rollbar/plugins/sidekiq/plugin.rb +39 -21
  67. data/lib/rollbar/plugins/sidekiq.rb +1 -1
  68. data/lib/rollbar/plugins/thread.rb +8 -7
  69. data/lib/rollbar/plugins/validations.rb +3 -1
  70. data/lib/rollbar/rake_tasks.rb +1 -2
  71. data/lib/rollbar/request_data_extractor.rb +53 -19
  72. data/lib/rollbar/rollbar_test.rb +9 -118
  73. data/lib/rollbar/scrubbers/params.rb +13 -7
  74. data/lib/rollbar/scrubbers/url.rb +56 -17
  75. data/lib/rollbar/scrubbers.rb +2 -6
  76. data/lib/rollbar/truncation/frames_strategy.rb +1 -1
  77. data/lib/rollbar/truncation/mixin.rb +1 -1
  78. data/lib/rollbar/truncation/remove_any_key_strategy.rb +4 -1
  79. data/lib/rollbar/truncation/remove_extra_strategy.rb +3 -1
  80. data/lib/rollbar/truncation/strings_strategy.rb +4 -2
  81. data/lib/rollbar/util/hash.rb +14 -7
  82. data/lib/rollbar/util/ip_anonymizer.rb +1 -1
  83. data/lib/rollbar/util.rb +23 -13
  84. data/lib/rollbar/version.rb +1 -1
  85. data/lib/rollbar.rb +12 -7
  86. data/lib/tasks/benchmark.rake +2 -1
  87. data/rollbar.gemspec +6 -3
  88. data/spec/support/rollbar_api.rb +67 -0
  89. metadata +19 -12
  90. data/.travis.yml +0 -281
  91. data/lib/rollbar/encoding/legacy_encoder.rb +0 -20
  92. /data/lib/generators/rollbar/templates/{initializer.rb → initializer.erb} +0 -0
@@ -15,11 +15,11 @@ module Rollbar
15
15
  # The notifier class. It has the core functionality
16
16
  # for sending reports to the API.
17
17
  class Notifier
18
- attr_accessor :configuration
19
- attr_accessor :last_report
20
- attr_accessor :scope_object
18
+ attr_accessor :configuration, :last_report, :scope_object
21
19
 
22
- @file_semaphore = Mutex.new
20
+ MUTEX = Mutex.new
21
+ EXTENSION_REGEXP = /.rollbar\z/.freeze
22
+ FAILSAFE_STRING_LENGTH = 10_000
23
23
 
24
24
  def initialize(parent_notifier = nil, payload_options = nil, scope = nil)
25
25
  if parent_notifier
@@ -32,7 +32,9 @@ module Rollbar
32
32
  self.scope_object = ::Rollbar::LazyStore.new(scope)
33
33
  end
34
34
 
35
- Rollbar::Util.deep_merge(configuration.payload_options, payload_options) if payload_options
35
+ return unless payload_options
36
+
37
+ Rollbar::Util.deep_merge(configuration.payload_options, payload_options)
36
38
  end
37
39
 
38
40
  def reset!
@@ -103,10 +105,10 @@ module Rollbar
103
105
 
104
106
  # Sends a report to Rollbar.
105
107
  #
106
- # Accepts any number of arguments. The last String argument will become
107
- # the message or description of the report. The last Exception argument
108
- # will become the associated exception for the report. The last hash
109
- # 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.
110
112
  #
111
113
  # If the extra hash contains a symbol key :custom_data_method_context
112
114
  # the value of the key will be used as the context for
@@ -117,14 +119,14 @@ module Rollbar
117
119
  # begin
118
120
  # foo = bar
119
121
  # rescue => e
120
- # Rollbar.log(e)
122
+ # Rollbar.log('error', e)
121
123
  # end
122
124
  #
123
125
  # @example
124
- # Rollbar.log('This is a simple log message')
126
+ # Rollbar.log('info', 'This is a simple log message')
125
127
  #
126
128
  # @example
127
- # Rollbar.log(e, 'This is a description of the exception')
129
+ # Rollbar.log('error', e, 'This is a description of the exception')
128
130
  #
129
131
  def log(level, *args)
130
132
  return 'disabled' unless enabled?
@@ -132,17 +134,8 @@ module Rollbar
132
134
  message, exception, extra, context = extract_arguments(args)
133
135
  use_exception_level_filters = use_exception_level_filters?(extra)
134
136
 
135
- return 'ignored' if ignored?(exception, use_exception_level_filters)
136
-
137
- begin
138
- status = call_before_process(:level => level,
139
- :exception => exception,
140
- :message => message,
141
- :extra => extra)
142
- return 'ignored' if status == 'ignored'
143
- rescue Rollbar::Ignore
144
- return 'ignored'
145
- end
137
+ return 'ignored' if ignored?(exception, use_exception_level_filters) ||
138
+ ignore_before_process?(level, exception, message, extra)
146
139
 
147
140
  level = lookup_exception_level(level, exception,
148
141
  use_exception_level_filters)
@@ -154,10 +147,27 @@ module Rollbar
154
147
  ret
155
148
  end
156
149
 
150
+ def ignore_before_process?(level, exception, message, extra)
151
+ status = call_before_process(:level => level,
152
+ :exception => exception,
153
+ :message => message,
154
+ :extra => extra)
155
+
156
+ status == 'ignored'
157
+ rescue Rollbar::Ignore
158
+ true
159
+ end
160
+
157
161
  def report_with_rescue(level, message, exception, extra, context)
158
162
  report(level, message, exception, extra, context)
159
163
  rescue StandardError, SystemStackError => e
160
- report_internal_error(e)
164
+ original_error = {
165
+ :message => message,
166
+ :exception => exception,
167
+ :configuration => configuration
168
+ }
169
+
170
+ report_internal_error(e, original_error)
161
171
 
162
172
  'error'
163
173
  end
@@ -194,24 +204,23 @@ module Rollbar
194
204
 
195
205
  def enabled?
196
206
  # Require access_token so we don't try to send events when unconfigured.
197
- configuration.enabled && configuration.access_token && !configuration.access_token.empty?
207
+ configuration.enabled &&
208
+ configuration.access_token &&
209
+ !configuration.access_token.empty?
198
210
  end
199
211
 
200
212
  def process_item(item)
201
- if configuration.write_to_file
202
- if configuration.use_async
203
- @file_semaphore.synchronize do
204
- write_item(item)
205
- end
206
- else
207
- write_item(item)
208
- end
209
- else
210
- send_item(item)
211
- end
213
+ return send_item(item) unless configuration.write_to_file
214
+
215
+ return do_write_item(item) unless configuration.use_async
216
+
217
+ MUTEX.synchronize { do_write_item(item) }
212
218
  rescue StandardError => e
213
- log_error("[Rollbar] Error processing the item: #{e.class}, #{e.message}. Item: #{item.payload.inspect}")
214
- raise e
219
+ log_error '[Rollbar] Error processing the item: ' \
220
+ "#{e.class}, #{e.message}. Item: #{item.payload.inspect}"
221
+ raise e unless via_failsafe?(item)
222
+
223
+ log_error('[Rollbar] Item has already failed. Not re-raising')
215
224
  end
216
225
 
217
226
  # We will reraise exceptions in this method so async queues
@@ -238,16 +247,16 @@ module Rollbar
238
247
  # Using Rollbar.silenced we avoid the above behavior but Sidekiq
239
248
  # will have a chance to retry the original job.
240
249
  def process_from_async_handler(payload)
241
- payload = Rollbar::JSON.load(payload) if payload.is_a?(String)
242
-
243
- item = Item.build_with(payload,
244
- :notifier => self,
245
- :configuration => configuration,
246
- :logger => logger)
247
-
248
250
  Rollbar.silenced do
249
251
  begin
250
- process_item(item)
252
+ if payload.is_a?(String)
253
+ # The final payload has already been built.
254
+ send_body(payload)
255
+ else
256
+ item = build_item_with_payload(payload)
257
+
258
+ process_item(item)
259
+ end
251
260
  rescue StandardError => e
252
261
  report_internal_error(e)
253
262
 
@@ -256,51 +265,108 @@ module Rollbar
256
265
  end
257
266
  end
258
267
 
259
- def send_failsafe(message, exception, uuid = nil, host = nil)
260
- exception_reason = failsafe_reason(message, exception)
261
-
262
- log_error "[Rollbar] Sending failsafe response due to #{exception_reason}"
263
-
264
- body = failsafe_body(exception_reason)
268
+ def build_item_with_payload(payload)
269
+ Item.build_with(payload, :notifier => self,
270
+ :configuration => configuration,
271
+ :logger => logger)
272
+ end
265
273
 
266
- failsafe_data = {
274
+ def failsafe_initial_data(exception_reason)
275
+ {
267
276
  :level => 'error',
268
277
  :environment => configuration.environment.to_s,
269
278
  :body => {
270
279
  :message => {
271
- :body => body
280
+ :body => failsafe_body(exception_reason)
272
281
  }
273
282
  },
274
283
  :notifier => {
275
284
  :name => 'rollbar-gem',
276
285
  :version => VERSION
277
286
  },
278
- :custom => {
279
- :orig_uuid => uuid,
280
- :orig_host => host
281
- },
282
287
  :internal => true,
283
- :failsafe => true
288
+ 'failsafe' => true
284
289
  }
290
+ end
291
+
292
+ def send_failsafe(message, exception, original_error = nil)
293
+ exception_reason = failsafe_reason(message, exception)
294
+
295
+ log_error "[Rollbar] Sending failsafe response due to #{exception_reason}"
296
+
297
+ failsafe_data = failsafe_initial_data(exception_reason)
298
+
299
+ failsafe_add_original_error_data(failsafe_data[:notifier], original_error)
285
300
 
286
301
  failsafe_payload = {
287
- 'access_token' => configuration.access_token,
288
302
  'data' => failsafe_data
289
303
  }
290
304
 
291
- begin
292
- item = Item.build_with(failsafe_payload,
293
- :notifier => self,
294
- :configuration => configuration,
295
- :logger => logger)
296
- schedule_item(item)
297
- rescue StandardError => e
298
- log_error "[Rollbar] Error sending failsafe : #{e}"
299
- end
305
+ process_failsafe_item(failsafe_payload)
300
306
 
301
307
  failsafe_payload
302
308
  end
303
309
 
310
+ def process_failsafe_item(failsafe_payload)
311
+ item = build_item_with_payload(failsafe_payload)
312
+ process_item(item)
313
+ log_and_return_item_data(item)
314
+ rescue StandardError => e
315
+ log_error "[Rollbar] Error sending failsafe : #{e}"
316
+ end
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
+ if original_error[:message]
332
+ diagnostic[:original_message] =
333
+ original_error[:message].truncate(FAILSAFE_STRING_LENGTH)
334
+ end
335
+ rescue StandardError => e
336
+ diagnostic[:original_message] = "Failed: #{e.message}"
337
+ end
338
+
339
+ def add_original_error(diagnostic, original_error)
340
+ if original_error[:exception]
341
+ backtrace = original_error[:exception].backtrace
342
+ message = original_error[:exception].message
343
+ diagnostic[:original_error] = {
344
+ :message => message && message.truncate(FAILSAFE_STRING_LENGTH),
345
+ :stack => backtrace && backtrace.join(', ').truncate(FAILSAFE_STRING_LENGTH)
346
+ }
347
+ end
348
+ rescue StandardError => e
349
+ diagnostic[:original_error] = "Failed: #{e.message}"
350
+ end
351
+
352
+ def add_configured_options(payload_notifier, original_error)
353
+ if original_error[:configuration]
354
+ configured = original_error[:configuration].configured_options.configured
355
+ payload_notifier[:configured_options] =
356
+ ::JSON.generate(configured).truncate(FAILSAFE_STRING_LENGTH)
357
+ end
358
+ rescue StandardError => e
359
+ payload_notifier[:configured_options] = "Failed: #{e.message}"
360
+ end
361
+
362
+ def add_original_host(diagnostic, original_error)
363
+ diagnostic[:original_host] = original_error[:host] if original_error[:host]
364
+ end
365
+
366
+ def add_original_uuid(diagnostic, original_error)
367
+ diagnostic[:original_uuid] = original_error[:uuid] if original_error[:uuid]
368
+ end
369
+
304
370
  ## Logging
305
371
  %w[debug info warn error].each do |level|
306
372
  define_method(:"log_#{level}") do |message|
@@ -325,7 +391,8 @@ module Rollbar
325
391
  end
326
392
 
327
393
  def enable_locals?
328
- configuration.locals[:enabled] && [:app, :all].include?(configuration.send_extra_frame_data)
394
+ configuration.locals[:enabled] &&
395
+ [:app, :all].include?(configuration.send_extra_frame_data)
329
396
  end
330
397
 
331
398
  def enable_locals
@@ -347,13 +414,7 @@ module Rollbar
347
414
  end
348
415
 
349
416
  def call_before_process(options)
350
- options = {
351
- :level => options[:level],
352
- :scope => scope_object,
353
- :exception => options[:exception],
354
- :message => options[:message],
355
- :extra => options[:extra]
356
- }
417
+ options = options_for_handler(options)
357
418
  handlers = configuration.before_process
358
419
 
359
420
  handlers.each do |handler|
@@ -370,24 +431,30 @@ module Rollbar
370
431
  end
371
432
  end
372
433
 
434
+ def options_for_handler(options)
435
+ {
436
+ :level => options[:level],
437
+ :scope => scope_object,
438
+ :exception => options[:exception],
439
+ :message => options[:message],
440
+ :extra => options[:extra]
441
+ }
442
+ end
443
+
373
444
  def extract_arguments(args)
374
- message = nil
375
- exception = nil
376
- extra = nil
377
- context = nil
445
+ message = exception = extra = context = nil
378
446
 
379
447
  args.each do |arg|
380
448
  if arg.is_a?(String)
381
449
  message = arg
382
450
  elsif arg.is_a?(Exception)
383
451
  exception = arg
384
- elsif RUBY_PLATFORM == 'java' && arg.is_a?(java.lang.Throwable)
452
+ elsif java_exception?(arg)
385
453
  exception = arg
386
454
  elsif arg.is_a?(Hash)
387
455
  extra = arg
388
456
 
389
- context = extra[:custom_data_method_context]
390
- extra.delete :custom_data_method_context
457
+ context = extra.delete :custom_data_method_context
391
458
 
392
459
  extra = nil if extra.empty?
393
460
  end
@@ -396,6 +463,10 @@ module Rollbar
396
463
  [message, exception, extra, context]
397
464
  end
398
465
 
466
+ def java_exception?(obj)
467
+ RUBY_PLATFORM == 'java' && obj.is_a?(java.lang.Throwable)
468
+ end
469
+
399
470
  def lookup_exception_level(orig_level, exception, use_exception_level_filters)
400
471
  return orig_level unless use_exception_level_filters
401
472
 
@@ -426,7 +497,9 @@ module Rollbar
426
497
 
427
498
  def report(level, message, exception, extra, context)
428
499
  unless message || exception || extra
429
- log_error '[Rollbar] Tried to send a report with no message, exception or extra data.'
500
+ log_error(
501
+ '[Rollbar] Tried to send a report with no message, exception or extra data.'
502
+ )
430
503
 
431
504
  return 'error'
432
505
  end
@@ -453,37 +526,48 @@ module Rollbar
453
526
  log_info "[Rollbar] Data: #{data}"
454
527
  end
455
528
 
456
- # Reports an internal error in the Rollbar library. This will be reported within the configured
457
- # Rollbar project. We'll first attempt to provide a report including the exception traceback.
458
- # If that fails, we'll fall back to a more static failsafe response.
459
- def report_internal_error(exception)
460
- log_error '[Rollbar] Reporting internal error encountered while sending data to Rollbar.'
529
+ # Reports an internal error in the Rollbar library. This will be reported
530
+ # within the configured Rollbar project. We'll first attempt to provide a
531
+ # report including the exception traceback. If that fails, we'll fall back
532
+ # to a more static failsafe response.
533
+ def report_internal_error(exception, original_error = nil)
534
+ return if skip_reporting_internal_error(exception)
535
+
536
+ failsafe_message = ''
537
+ log_error(
538
+ '[Rollbar] Reporting internal error encountered while sending data to Rollbar.'
539
+ )
461
540
 
462
541
  configuration.execute_hook(:on_report_internal_error, exception)
463
542
 
464
- begin
465
- item = build_item('error', nil, exception, { :internal => true }, nil)
466
- rescue StandardError => e
467
- send_failsafe('build_item in exception_data', e)
468
- log_error "[Rollbar] Exception: #{exception}"
469
- return
470
- end
543
+ failsafe_message = 'build_item in exception_data'
544
+ item = build_item('error', nil, exception, { :internal => true }, nil)
471
545
 
472
- begin
473
- process_item(item)
474
- rescue StandardError => e
475
- send_failsafe('error in process_item', e)
476
- log_error "[Rollbar] Item: #{item}"
477
- return
478
- end
546
+ failsafe_message = 'error in process_item'
547
+ process_item(item)
479
548
 
480
- begin
481
- log_instance_link(item['data'])
482
- rescue StandardError => e
483
- send_failsafe('error logging instance link', e)
484
- log_error "[Rollbar] Item: #{item}"
485
- return
549
+ failsafe_message = 'error logging instance link'
550
+ log_instance_link(item['data'])
551
+ rescue StandardError => e
552
+ send_failsafe(failsafe_message, e, original_error)
553
+ log_error(item ? "[Rollbar] Item: #{item}" : "[Rollbar] Exception: #{exception}")
554
+ end
555
+
556
+ def skip_reporting_internal_error(exception)
557
+ return true if configuration.ignore_internal_errors == true
558
+
559
+ configuration.ignore_internal_errors.each do |error_name|
560
+ begin
561
+ error_cls = error_name.split('::').reduce(Module, :const_get)
562
+ return true if exception.class <= error_cls
563
+ rescue NameError
564
+ # Ignore errors and continue matching.
565
+ # It's possible for a class name in the list to not be resolvable,
566
+ # and this is ok.
567
+ end
486
568
  end
569
+
570
+ false
487
571
  end
488
572
 
489
573
  ## Payload building functions
@@ -509,25 +593,35 @@ module Rollbar
509
593
 
510
594
  ## Delivery functions
511
595
 
512
- def send_item_using_eventmachine(item, uri)
513
- body = item.dump
514
- return unless body
596
+ def send_using_eventmachine(body)
597
+ uri = URI.parse(configuration.endpoint)
515
598
 
516
- headers = { 'X-Rollbar-Access-Token' => item['access_token'] }
599
+ headers = { 'X-Rollbar-Access-Token' => configuration.access_token }
517
600
  options = http_proxy_for_em(uri)
518
- req = EventMachine::HttpRequest.new(uri.to_s, options).post(:body => body, :head => headers)
601
+ req = EventMachine::HttpRequest.new(uri.to_s, options).post(:body => body,
602
+ :head => headers)
603
+
604
+ eventmachine_callback(req)
605
+ eventmachine_errback(req)
606
+ end
519
607
 
608
+ def eventmachine_callback(req)
520
609
  req.callback do
521
610
  if req.response_header.status == 200
522
611
  log_info '[Rollbar] Success'
523
612
  else
524
- log_warning "[Rollbar] Got unexpected status code from Rollbar.io api: #{req.response_header.status}"
613
+ log_warning '[Rollbar] Got unexpected status code from Rollbar.io api: ' \
614
+ "#{req.response_header.status}"
525
615
  log_info "[Rollbar] Response: #{req.response}"
526
616
  end
527
617
  end
618
+ end
528
619
 
620
+ def eventmachine_errback(req)
529
621
  req.errback do
530
- log_warning "[Rollbar] Call to API failed, status code: #{req.response_header.status}"
622
+ log_warning(
623
+ "[Rollbar] Call to API failed, status code: #{req.response_header.status}"
624
+ )
531
625
  log_info "[Rollbar] Error's response: #{req.response}"
532
626
  end
533
627
  end
@@ -538,40 +632,61 @@ module Rollbar
538
632
  body = item.dump
539
633
  return unless body
540
634
 
541
- uri = URI.parse(configuration.endpoint)
542
-
543
635
  if configuration.use_eventmachine
544
- send_item_using_eventmachine(item, uri)
636
+ send_using_eventmachine(body)
545
637
  return
546
638
  end
547
639
 
548
- handle_response(do_post(uri, body, item['access_token']))
640
+ send_body(body)
641
+ end
642
+
643
+ def send_body(body)
644
+ log_info '[Rollbar] Sending json'
645
+
646
+ uri = URI.parse(configuration.endpoint)
647
+
648
+ handle_response(do_post(uri, body, configuration.access_token))
549
649
  end
550
650
 
551
651
  def do_post(uri, body, access_token)
652
+ http = init_http(uri)
653
+
654
+ request = Net::HTTP::Post.new(uri.request_uri)
655
+
656
+ request.body = pack_ruby260_bytes(body)
657
+
658
+ # Ensure the payload token will be used if the option is set.
659
+ unless configuration.use_payload_access_token
660
+ request.add_field('X-Rollbar-Access-Token', access_token)
661
+ end
662
+
663
+ handle_net_retries { http.request(request) }
664
+ end
665
+
666
+ def init_http(uri)
552
667
  proxy = http_proxy(uri)
553
- http = Net::HTTP.new(uri.host, uri.port, proxy.host, proxy.port, proxy.user, proxy.password)
668
+ http = Net::HTTP.new(uri.host, uri.port, proxy.host, proxy.port, proxy.user,
669
+ proxy.password)
554
670
 
555
- http.open_timeout = configuration.open_timeout
556
- http.read_timeout = configuration.request_timeout
671
+ init_http_timeouts(http)
557
672
 
558
673
  if uri.scheme == 'https'
559
674
  http.use_ssl = true
560
675
  http.verify_mode = ssl_verify_mode
561
676
  end
562
677
 
563
- request = Net::HTTP::Post.new(uri.request_uri)
564
-
565
- request.body = pack_ruby260_bytes(body)
566
- request.add_field('X-Rollbar-Access-Token', access_token)
678
+ http
679
+ end
567
680
 
568
- handle_net_retries { http.request(request) }
681
+ def init_http_timeouts(http)
682
+ http.open_timeout = configuration.open_timeout
683
+ http.read_timeout = configuration.request_timeout
569
684
  end
570
685
 
571
686
  def pack_ruby260_bytes(body)
572
687
  # Ruby 2.6.0 shipped with a bug affecting multi-byte body for Net::HTTP.
573
688
  # Fix (committed one day after 2.6.0p0 shipped) is here:
574
- # https://github.com/ruby/ruby/commit/1680a13a926b17661329beec1ded6b32aad16c1b#diff-00a99d8c71daaf5fc60a050da41f7261
689
+ # ruby/ruby/commit/1680a13a926b17661329beec1ded6b32aad16c1b
575
690
  #
576
691
  # We work around this by repacking the body as single byte chars if needed.
577
692
  if RUBY_VERSION == '2.6.0' && multibyte?(body)
@@ -621,8 +736,6 @@ module Rollbar
621
736
  end
622
737
 
623
738
  def handle_net_retries
624
- return yield if skip_retries?
625
-
626
739
  retries = configuration.net_retries - 1
627
740
 
628
741
  begin
@@ -636,15 +749,13 @@ module Rollbar
636
749
  end
637
750
  end
638
751
 
639
- def skip_retries?
640
- Rollbar::LanguageSupport.ruby_19?
641
- end
642
-
643
752
  def handle_response(response)
644
753
  if response.code == '200'
645
754
  log_info '[Rollbar] Success'
646
755
  else
647
- log_warning "[Rollbar] Got unexpected status code from Rollbar api: #{response.code}"
756
+ log_warning(
757
+ "[Rollbar] Got unexpected status code from Rollbar api: #{response.code}"
758
+ )
648
759
  log_info "[Rollbar] Response: #{response.body}"
649
760
  configuration.execute_hook(:on_error_response, response)
650
761
  end
@@ -658,27 +769,20 @@ module Rollbar
658
769
  end
659
770
  end
660
771
 
661
- def write_item(item)
662
- if configuration.use_async
663
- @file_semaphore.synchronize do
664
- do_write_item(item)
665
- end
666
- else
667
- do_write_item(item)
668
- end
669
- end
670
-
671
772
  def do_write_item(item)
672
773
  log_info '[Rollbar] Writing item to file'
673
774
 
674
775
  body = item.dump
675
776
  return unless body
676
777
 
778
+ file_name = file_name_with_pid(configuration)
779
+
677
780
  begin
678
- @file ||= File.open(configuration.filepath, 'a')
781
+ @file ||= File.open(file_name, 'a')
679
782
 
680
783
  @file.puts(body)
681
784
  @file.flush
785
+ update_file(@file, file_name)
682
786
 
683
787
  log_info '[Rollbar] Success'
684
788
  rescue IOError => e
@@ -686,32 +790,54 @@ module Rollbar
686
790
  end
687
791
  end
688
792
 
793
+ def file_name_with_pid(configuration)
794
+ if configuration.files_with_pid_name_enabled
795
+ configuration.filepath.gsub(EXTENSION_REGEXP, "_#{Process.pid}\\0")
796
+ else
797
+ configuration.filepath
798
+ end
799
+ end
800
+
801
+ def update_file(file, file_name)
802
+ return unless configuration.files_processed_enabled
803
+
804
+ time_now = Time.now
805
+ if configuration.files_processed_duration > time_now - file.birthtime &&
806
+ file.size < configuration.files_processed_size
807
+ return
808
+ end
809
+
810
+ new_file_name = file_name.gsub(EXTENSION_REGEXP, "_processed_#{time_now.to_i}\\0")
811
+ File.rename(file, new_file_name)
812
+ file.close
813
+ @file = File.open(file_name, 'a')
814
+ end
815
+
689
816
  def failsafe_reason(message, exception)
690
- body = ''
817
+ return failsafe_exception_reason(message, exception) if exception
691
818
 
692
- if exception
693
- begin
694
- backtrace = exception.backtrace || []
695
- nearest_frame = backtrace[0]
819
+ message.to_s
820
+ rescue StandardError
821
+ log_error('[Rollbar] Error building failsafe message')
822
+ ''
823
+ end
696
824
 
697
- exception_info = exception.class.name
698
- # #to_s and #message defaults to class.to_s. Add message only if add valuable info.
699
- exception_info += %[: "#{exception.message}"] if exception.message != exception.class.to_s
700
- exception_info += " in #{nearest_frame}" if nearest_frame
825
+ def failsafe_exception_reason(message, exception)
826
+ backtrace = exception.backtrace || []
827
+ nearest_frame = backtrace[0]
701
828
 
702
- body += "#{exception_info}: #{message}"
703
- rescue StandardError
704
- log_error('[Rollbar] Error building failsafe exception message')
705
- end
706
- else
707
- begin
708
- body += message.to_s
709
- rescue StandardError
710
- log_error('[Rollbar] Error building failsafe message')
711
- end
829
+ exception_info = exception.class.name
830
+ # #to_s and #message defaults to class.to_s.
831
+ # Add message only if add valuable info.
832
+ if exception.message != exception.class.to_s
833
+ exception_info += %[: "#{exception.message}"]
712
834
  end
835
+ exception_info += " in #{nearest_frame}" if nearest_frame
713
836
 
714
- body
837
+ "#{exception_info}: #{message}"
838
+ rescue StandardError
839
+ log_error('[Rollbar] Error building failsafe exception message')
840
+ ''
715
841
  end
716
842
 
717
843
  def failsafe_body(reason)
@@ -737,11 +863,15 @@ module Rollbar
737
863
  end
738
864
 
739
865
  def process_async_item(item)
866
+ # Send async payloads as JSON string when async_json_payload is set.
867
+ payload = configuration.async_json_payload ? item.dump : item.payload
868
+
740
869
  configuration.async_handler ||= default_async_handler
741
- configuration.async_handler.call(item.payload)
870
+ configuration.async_handler.call(payload)
742
871
  rescue StandardError
743
872
  if configuration.failover_handlers.empty?
744
- log_error '[Rollbar] Async handler failed, and there are no failover handlers configured. See the docs for "failover_handlers"'
873
+ log_error '[Rollbar] Async handler failed, and there are no failover ' \
874
+ 'handlers configured. See the docs for "failover_handlers"'
745
875
  return
746
876
  end
747
877
 
@@ -759,7 +889,8 @@ module Rollbar
759
889
  rescue StandardError
760
890
  next unless handler == failover_handlers.last
761
891
 
762
- log_error "[Rollbar] All failover handlers failed while processing item: #{Rollbar::JSON.dump(item.payload)}"
892
+ log_error '[Rollbar] All failover handlers failed while processing ' \
893
+ "item: #{Rollbar::JSON.dump(item.payload)}"
763
894
  end
764
895
  end
765
896
  end
@@ -770,7 +901,13 @@ module Rollbar
770
901
  return unless data[:uuid]
771
902
 
772
903
  uuid_url = Util.uuid_rollbar_url(data, configuration)
773
- log_info "[Rollbar] Details: #{uuid_url} (only available if report was successful)"
904
+ log_info(
905
+ "[Rollbar] Details: #{uuid_url} (only available if report was successful)"
906
+ )
907
+ end
908
+
909
+ def via_failsafe?(item)
910
+ item.payload.fetch('data', {}).fetch('failsafe', false)
774
911
  end
775
912
  end
776
913
  end