rollbar 1.0.1 → 1.1.0

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 26233ef7ffa02228b4777c5ef26655f9208541e9
4
- data.tar.gz: 14feffe767b5d9764afc4508ed139a9d2394ada3
3
+ metadata.gz: 6ba9f625302b093e422f24c2c3a35a7e6388c099
4
+ data.tar.gz: ef53f6cd97ed94d5db499b77c496a81f6c6eb6f8
5
5
  SHA512:
6
- metadata.gz: c38f8b6ea8dbdafe38b68cbc5b1902b21e7a55f29ebc154fe23d4a9a2a50d4bba01e623b917f4ba6e3ee17611cbb4d55b07035e40d83ae35eaa90b7ed4fab326
7
- data.tar.gz: 08a2f63d80dd45c48eb297e6cfbfc0f551004ad7c4280b2b6a71bb79b3887c814910307c83b742f5ad420586e795f95cf52e4bf7f1a00561e54d3cb4ef671c7e
6
+ metadata.gz: 5752f3f6282c97ffce3142bd39089946eb79f0f7a9a864f9be56f74ec563ebc49b0839a92eb4388ef5d0445cc6f1fb80ae7ffc28860811e6884c5abf50257457
7
+ data.tar.gz: c00267db9b445b402c2a84b3b608a3b3c23c17371611467ed79a173a1b6eb45a02f2e6d496c2b8131da45750cccb14723c55d61810ed6c65a82f1fa97d41231b
@@ -1,5 +1,11 @@
1
1
  # Change Log
2
2
 
3
+ **1.1.0**
4
+ - New feature: `failover_handlers`. You can specify a list of async handlers, which will be tried in sequence upon failure. See [#135](https://github.com/rollbar/rollbar-gem/pull/135).
5
+ - Support nested exceptions for Ruby 2.1. See [#136](https://github.com/rollbar/rollbar-gem/pull/136)
6
+ - Fix handling of utf8 sequences in payload symbols. See [#131](https://github.com/rollbar/rollbar-gem/pull/131). Thanks [@kroky](https://github.com/kroky) for the fix and [@jondeandres](https://github.com/jondeandres) for reviewing.
7
+ - Fix logic bugs in assignments for `scrub_fields` and `scrub_headers`. See [#137](https://github.com/rollbar/rollbar-gem/pull/137)
8
+
3
9
  **1.0.1**
4
10
  - Use the payload's access token for the X-Rollbar-Access-Token header, instead of the configured access token. Fixes an issue where payloads would be reported into the wrong project when sent via Resque. See [#128](https://github.com/rollbar/rollbar-gem/pull/128). Thanks to [@jondeandres](https://github.com/jondeandres) for the fix.
5
11
 
data/README.md CHANGED
@@ -9,7 +9,7 @@ Ruby gem for reporting exceptions, errors, and log messages to [Rollbar](https:/
9
9
 
10
10
  Add this line to your application's Gemfile:
11
11
 
12
- gem 'rollbar', '~> 1.0.0'
12
+ gem 'rollbar', '~> 1.1.0'
13
13
 
14
14
  And then execute:
15
15
 
@@ -239,7 +239,7 @@ config.delayed_job_enabled = false
239
239
 
240
240
  ## Asynchronous reporting
241
241
 
242
- By default, all messages are reported synchronously. You can enable asynchronous reporting with [girl_friday](https://github.com/mperham/girl_friday) or [sucker_punch](https://github.com/brandonhilkert/sucker_punch) or [Sidekiq](https://github.com/mperham/sidekiq).
242
+ By default, all messages are reported synchronously. You can enable asynchronous reporting with [girl_friday](https://github.com/mperham/girl_friday), [sucker_punch](https://github.com/brandonhilkert/sucker_punch), [Sidekiq](https://github.com/mperham/sidekiq), [Resque](https://github.com/resque/resque) or using threading.
243
243
 
244
244
  ### Using girl_friday
245
245
 
@@ -285,11 +285,40 @@ Start Sidekiq from the root directory of your Rails app and declare the name of
285
285
  $ bundle exec sidekiq -q rollbar
286
286
  ```
287
287
 
288
+ ### Using Resque
289
+
290
+ Add the following in ```config/initializers/rollbar.rb```:
291
+
292
+ ```ruby
293
+ config.use_resque
294
+ ```
295
+
296
+ You can also supply a custom Resque queue:
297
+
298
+ ```ruby
299
+ config.use_resque :queue => 'my_queue'
300
+ ```
301
+
302
+ Now you can just start a new Resque worker processing jobs in that queue:
303
+
304
+ ```bash
305
+ $ QUEUE=my_queue bundle exec resque:work
306
+ ```
307
+
308
+ ### Using threading
309
+
310
+ Add the following in ```config/initializers/rollbar.rb```:
311
+
312
+ ```ruby
313
+ config.use_thread
314
+ ```
315
+
288
316
  ### Using another handler
289
317
 
290
- You can supply your own handler using ```config.async_handler```. The handler should schedule the payload for later processing (i.e. with a delayed_job, in a resque queue, etc.) and should itself return immediately. For example:
318
+ You can supply your own handler using ```config.async_handler```. The object to set for `async_handler` should respond to `#call` and receive the payload. The handler should schedule the payload for later processing (i.e. with a delayed_job, in a resque queue, etc.) and should itself return immediately. For example:
291
319
 
292
320
  ```ruby
321
+ config.use_async
293
322
  config.async_handler = Proc.new { |payload|
294
323
  Thread.new { Rollbar.process_payload(payload) }
295
324
  }
@@ -297,6 +326,21 @@ config.async_handler = Proc.new { |payload|
297
326
 
298
327
  Make sure you pass ```payload``` to ```Rollbar.process_payload``` in your own implementation.
299
328
 
329
+ ## Failover handlers
330
+
331
+ If you are using `async_handler` to process asynchronous the error it's possible that the handler fails before it calls `Rollbar.process_payload`. For example, for the Resque handler, the Redis connection could fail so the job is finally not processed.
332
+
333
+ To ensure that the error is sent you can define a chain of failover handlers that Rollbar will use to send the payload in case that the primary handler fails. The failover handlers, as for `async_handler`, are just objects responding to `#call`.
334
+
335
+ To configure the failover handlers you can add the following:
336
+
337
+ ```ruby
338
+ config.use_resque
339
+ config.failover_handlers = [Rollbar::Delay::GirlFriday, Rollbar::Delay::Thread]
340
+ ```
341
+
342
+ With the configuration above Resque will be your primary asynchronous handler but if it fails queueing the job Rollbar will use GirlFriday at first, and just a thread in case that GirlFriday fails too.
343
+
300
344
  ## Using with rollbar-agent
301
345
 
302
346
  For even more asynchrony, you can configure the gem to write to a file instead of sending the payload to Rollbar servers directly. [rollbar-agent](https://github.com/rollbar/rollbar-agent) can then be hooked up to this file to actually send the payload across. To enable, add the following in ```config/initializers/rollbar.rb```:
@@ -376,7 +420,7 @@ If you're using [Goalie](https://github.com/obvio171/goalie) for custom error pa
376
420
 
377
421
  ## Using with Resque
378
422
 
379
- Check out [resque-rollbar](https://github.com/CrowdFlower/resque-rollbar) for using Rollbar as a failure backend for Resque.
423
+ Check out [resque-rollbar](https://github.com/dimko/resque-rollbar) for using Rollbar as a failure backend for Resque.
380
424
 
381
425
 
382
426
  ## Using with Zeus
data/THANKS.md CHANGED
@@ -18,9 +18,11 @@ Huge thanks to the following contributors (by github username). For the most up-
18
18
  - [ixti](https://github.com/ixti)
19
19
  - [jeremyvdw](https://github.com/jeremyvdw)
20
20
  - [johnknott](https://github.com/johnknott)
21
+ - [jondeandres](https://github.com/jondeandres)
21
22
  - [JoshuaOSHickman](https://github.com/JoshuaOSHickman)
22
23
  - [juggler](https://github.com/juggler)
23
24
  - [kavu](https://github.com/kavu)
25
+ - [kroky](https://github.com/kroky)
24
26
  - [lanej](https://github.com/lanej)
25
27
  - [magnolia-fan](https://github.com/magnolia-fan)
26
28
  - [mauricio](https://github.com/mauricio)
@@ -16,6 +16,8 @@ require 'rollbar/exception_reporter'
16
16
  require 'rollbar/active_record_extension' if defined?(ActiveRecord)
17
17
  require 'rollbar/util'
18
18
  require 'rollbar/railtie' if defined?(Rails)
19
+ require 'rollbar/delay/girl_friday'
20
+ require 'rollbar/delay/thread'
19
21
 
20
22
  unless ''.respond_to? :encode
21
23
  require 'iconv'
@@ -28,6 +30,8 @@ module Rollbar
28
30
  attr_writer :configuration
29
31
  attr_accessor :last_report
30
32
 
33
+ @file_semaphore = Mutex.new
34
+
31
35
  # Similar to configure below, but used only internally within the gem
32
36
  # to configure it without initializing any of the third party hooks
33
37
  def preconfigure
@@ -46,9 +50,8 @@ module Rollbar
46
50
  # end
47
51
  def configure
48
52
  # if configuration.enabled has not been set yet (is still 'nil'), set to true.
49
- if configuration.enabled.nil?
50
- configuration.enabled = true
51
- end
53
+ configuration.enabled = true if configuration.enabled.nil?
54
+
52
55
  yield(configuration)
53
56
 
54
57
  require_hooks
@@ -225,6 +228,7 @@ module Rollbar
225
228
  puts "[Rollbar] #{message}"
226
229
  end
227
230
  end
231
+
228
232
  def log_debug(message)
229
233
  begin
230
234
  logger.debug message
@@ -234,6 +238,12 @@ module Rollbar
234
238
  end
235
239
  end
236
240
 
241
+ def default_async_handler
242
+ return Rollbar::Delay::GirlFriday if defined?(GirlFriday)
243
+
244
+ Rollbar::Delay::Thread
245
+ end
246
+
237
247
  private
238
248
 
239
249
  def attach_request_data(payload, request_data)
@@ -307,6 +317,37 @@ module Rollbar
307
317
 
308
318
  data[:level] = force_level if force_level
309
319
 
320
+ traces = trace_chain(exception)
321
+
322
+ if traces.size > 1
323
+ body = {
324
+ :trace_chain => traces
325
+ }
326
+ elsif traces.size == 1
327
+ body = {
328
+ :trace => traces[0]
329
+ }
330
+ end
331
+
332
+ data[:body] = body
333
+
334
+ data[:server] = server_data
335
+
336
+ data
337
+ end
338
+
339
+ def trace_chain(exception)
340
+ traces = [trace_data(exception)]
341
+
342
+ while exception.respond_to?(:cause) && (cause = exception.cause)
343
+ traces << trace_data(cause)
344
+ exception = cause
345
+ end
346
+
347
+ traces
348
+ end
349
+
350
+ def trace_data(exception)
310
351
  # parse backtrace
311
352
  if exception.backtrace.respond_to?( :map )
312
353
  frames = exception.backtrace.map { |frame|
@@ -324,19 +365,13 @@ module Rollbar
324
365
  frames = []
325
366
  end
326
367
 
327
- data[:body] = {
328
- :trace => {
329
- :frames => frames,
330
- :exception => {
331
- :class => exception.class.name,
332
- :message => exception.message
333
- }
368
+ {
369
+ :frames => frames,
370
+ :exception => {
371
+ :class => exception.class.name,
372
+ :message => exception.message
334
373
  }
335
374
  }
336
-
337
- data[:server] = server_data
338
-
339
- data
340
375
  end
341
376
 
342
377
  def logger
@@ -425,29 +460,45 @@ module Rollbar
425
460
  end
426
461
 
427
462
  def schedule_payload(payload)
428
- if payload.nil?
429
- return
430
- end
463
+ return if payload.nil?
431
464
 
432
465
  log_info '[Rollbar] Scheduling payload'
433
466
 
434
467
  if configuration.use_async
435
- unless configuration.async_handler
436
- configuration.async_handler = method(:default_async_handler)
437
- end
438
-
439
- if configuration.write_to_file
440
- unless @file_semaphore
441
- @file_semaphore = Mutex.new
442
- end
443
- end
444
-
445
- configuration.async_handler.call(payload)
468
+ process_async_payload(payload)
446
469
  else
447
470
  process_payload(payload)
448
471
  end
449
472
  end
450
473
 
474
+ def process_async_payload(payload)
475
+ configuration.async_handler ||= default_async_handler
476
+ configuration.async_handler.call(payload)
477
+ rescue
478
+ if configuration.failover_handlers.empty?
479
+ log_error '[Rollbar] Async handler failed, and there are no failover handlers configured. See the docs for "failover_handlers"'
480
+ return
481
+ end
482
+
483
+ async_failover(payload)
484
+ end
485
+
486
+ def async_failover(payload)
487
+ log_warning '[Rollbar] Primary async handler failed. Trying failovers...'
488
+
489
+ failover_handlers = configuration.failover_handlers
490
+
491
+ failover_handlers.each do |handler|
492
+ begin
493
+ handler.call(payload)
494
+ rescue
495
+ next unless handler == failover_handlers.last
496
+
497
+ log_error "[Rollbar] All failover handlers failed while processing payload: #{MultiJson.dump(payload)}"
498
+ end
499
+ end
500
+ end
501
+
451
502
  def build_payload(data)
452
503
  payload = {
453
504
  'access_token' => configuration.access_token,
@@ -534,21 +585,6 @@ module Rollbar
534
585
  data
535
586
  end
536
587
 
537
- def default_async_handler(payload)
538
- if defined?(GirlFriday)
539
- unless @queue
540
- @queue = GirlFriday::WorkQueue.new(nil, :size => 5) do |payload|
541
- process_payload(payload)
542
- end
543
- end
544
-
545
- @queue.push(payload)
546
- else
547
- log_warning '[Rollbar] girl_friday not found to handle async call, falling back to Thread'
548
- Thread.new { process_payload(payload) }
549
- end
550
- end
551
-
552
588
  # Reports an internal error in the Rollbar library. This will be reported within the configured
553
589
  # Rollbar project. We'll first attempt to provide a report including the exception traceback.
554
590
  # If that fails, we'll fall back to a more static failsafe response.
@@ -617,19 +653,23 @@ module Rollbar
617
653
  end
618
654
 
619
655
  def enforce_valid_utf8(payload)
620
- normalizer = Proc.new do |value|
621
- if value.is_a?(String)
622
- if value.respond_to? :encode
623
- value.encode('UTF-8', 'binary', :invalid => :replace, :undef => :replace, :replace => '')
624
- else
625
- ::Iconv.conv('UTF-8//IGNORE', 'UTF-8', value)
626
- end
656
+ normalizer = lambda do |object|
657
+ is_symbol = object.is_a?(Symbol)
658
+
659
+ return object unless object == object.to_s || is_symbol
660
+
661
+ value = object.to_s
662
+
663
+ if value.respond_to? :encode
664
+ encoded_value = value.encode('UTF-8', 'binary', :invalid => :replace, :undef => :replace, :replace => '')
627
665
  else
628
- value
666
+ encoded_value = ::Iconv.conv('UTF-8//IGNORE', 'UTF-8', value)
629
667
  end
668
+
669
+ is_symbol ? encoded_value.to_sym : encoded_value
630
670
  end
631
671
 
632
- Rollbar::Util::iterate_and_update(payload, normalizer)
672
+ Rollbar::Util.iterate_and_update(payload, normalizer)
633
673
  end
634
674
 
635
675
  def truncate_payload(payload, byte_threshold)
@@ -15,6 +15,7 @@ module Rollbar
15
15
  attr_accessor :endpoint
16
16
  attr_accessor :environment
17
17
  attr_accessor :exception_level_filters
18
+ attr_accessor :failover_handlers
18
19
  attr_accessor :filepath
19
20
  attr_accessor :framework
20
21
  attr_accessor :ignored_person_ids
@@ -53,6 +54,7 @@ module Rollbar
53
54
  'AbstractController::ActionNotFound' => 'warning',
54
55
  'ActionController::RoutingError' => 'warning'
55
56
  }
57
+ @failover_handlers = []
56
58
  @framework = 'Plain'
57
59
  @ignored_person_ids = []
58
60
  @person_method = 'current_user'
@@ -77,6 +79,15 @@ module Rollbar
77
79
  @async_handler = Rollbar::Delay::Sidekiq.new(options)
78
80
  end
79
81
 
82
+ def use_resque(options = {})
83
+ require 'rollbar/delay/resque' if defined?(Resque)
84
+
85
+ Rollbar::Delay::Resque::Job.queue = options[:queue] if options[:queue]
86
+
87
+ @use_async = true
88
+ @async_handler = Rollbar::Delay::Resque
89
+ end
90
+
80
91
  def use_sidekiq=(value)
81
92
  deprecation_message = "#use_sidekiq=(value) has been deprecated in favor of #use_sidekiq(options = {}). Please update your rollbar configuration."
82
93
  defined?(ActiveSupport) ? ActiveSupport::Deprecation.warn(deprecation_message) : puts(deprecation_message)
@@ -84,6 +95,12 @@ module Rollbar
84
95
  value.is_a?(Hash) ? use_sidekiq(value) : use_sidekiq
85
96
  end
86
97
 
98
+ def use_thread
99
+ require 'rollbar/delay/thread'
100
+ @use_async = true
101
+ @async_handler = Rollbar::Delay::Thread
102
+ end
103
+
87
104
  def use_sucker_punch
88
105
  require 'rollbar/delay/sucker_punch' if defined?(SuckerPunch)
89
106
  @use_async = true
@@ -0,0 +1,26 @@
1
+ module Rollbar
2
+ module Delay
3
+ class GirlFriday
4
+
5
+ class << self
6
+ attr_accessor :queue
7
+
8
+ def call(payload)
9
+ new.call(payload)
10
+ end
11
+ end
12
+
13
+ def queue_class
14
+ ::GirlFriday::WorkQueue
15
+ end
16
+
17
+ def call(payload)
18
+ self.class.queue = queue_class.new(nil, :size => 5) do |payload|
19
+ Rollbar.process_payload(payload)
20
+ end
21
+
22
+ self.class.queue.push(payload)
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,31 @@
1
+ require 'resque'
2
+
3
+ module Rollbar
4
+ module Delay
5
+ class Resque
6
+ def self.call(payload)
7
+ new.call(payload)
8
+ end
9
+
10
+ def call(payload)
11
+ ::Resque.enqueue(Job, payload)
12
+ end
13
+
14
+ class Job
15
+ class << self
16
+ attr_accessor :queue
17
+ end
18
+
19
+ self.queue = :default
20
+
21
+ def self.perform(payload)
22
+ new.perform(payload)
23
+ end
24
+
25
+ def perform(payload)
26
+ Rollbar.process_payload(payload)
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,13 @@
1
+ module Rollbar
2
+ module Delay
3
+ class Thread
4
+ def self.call(payload)
5
+ new.call(payload)
6
+ end
7
+
8
+ def call(payload)
9
+ ::Thread.new { Rollbar.process_payload(payload) }
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,27 @@
1
+ require 'rollbar'
2
+ require 'rollbar/exception_reporter'
3
+
4
+ module Rollbar
5
+ module Middleware
6
+ class Sinatra
7
+ include ::Rollbar::ExceptionReporter
8
+
9
+ def initialize(app)
10
+ @app = app
11
+ end
12
+
13
+ def call(env)
14
+ response = @app.call(env)
15
+ report_exception_to_rollbar(env, framework_error(env)) if framework_error(env)
16
+ response
17
+ rescue ::Exception => exception
18
+ report_exception_to_rollbar(env, exception)
19
+ raise
20
+ end
21
+
22
+ def framework_error(env)
23
+ env['sinatra.error']
24
+ end
25
+ end
26
+ end
27
+ end
@@ -12,13 +12,13 @@ module Rollbar
12
12
  controller = env['action_controller.instance']
13
13
  person_data = controller.rollbar_person_data rescue {}
14
14
  end
15
-
15
+
16
16
  person_data
17
17
  end
18
18
 
19
19
  def extract_request_data_from_rack(env)
20
20
  rack_req = Rack::Request.new(env)
21
-
21
+
22
22
  sensitive_params = sensitive_params_list(env)
23
23
  request_params = rollbar_filtered_params(sensitive_params, rollbar_request_params(env))
24
24
  get_params = rollbar_filtered_params(sensitive_params, rollbar_get_params(rack_req))
@@ -26,9 +26,9 @@ module Rollbar
26
26
  cookies = rollbar_filtered_params(sensitive_params, rollbar_request_cookies(rack_req))
27
27
  session = rollbar_filtered_params(sensitive_params, env['rack.session.options'])
28
28
  route_params = rollbar_filtered_params(sensitive_params, rollbar_route_params(env))
29
-
29
+
30
30
  params = request_params.merge(get_params).merge(post_params)
31
-
31
+
32
32
  data = {
33
33
  :params => params,
34
34
  :url => rollbar_url(env),
@@ -39,11 +39,11 @@ module Rollbar
39
39
  :method => rollbar_request_method(env),
40
40
  :route => route_params,
41
41
  }
42
-
42
+
43
43
  if env["action_dispatch.request_id"]
44
44
  data[:request_id] = env["action_dispatch.request_id"]
45
45
  end
46
-
46
+
47
47
  data
48
48
  end
49
49
 
@@ -68,13 +68,13 @@ module Rollbar
68
68
 
69
69
  def rollbar_url(env)
70
70
  scheme = env['HTTP_X_FORWARDED_PROTO'] || env['rack.url_scheme']
71
-
71
+
72
72
  host = env['HTTP_X_FORWARDED_HOST'] || env['HTTP_HOST'] || env['SERVER_NAME']
73
73
  path = env['ORIGINAL_FULLPATH'] || env['REQUEST_URI']
74
74
  unless path.nil? || path.empty?
75
75
  path = '/' + path.to_s if path.to_s.slice(0, 1) != '/'
76
76
  end
77
-
77
+
78
78
  port = env['HTTP_X_FORWARDED_PORT']
79
79
  if port && !(scheme.downcase == 'http' && port.to_i == 80) && \
80
80
  !(scheme.downcase == 'https' && port.to_i == 443) && \
@@ -88,7 +88,7 @@ module Rollbar
88
88
  def rollbar_user_ip(env)
89
89
  (env['action_dispatch.remote_ip'] || env['HTTP_X_REAL_IP'] || env['HTTP_X_FORWARDED_FOR'] || env['REMOTE_ADDR']).to_s
90
90
  end
91
-
91
+
92
92
  def rollbar_get_params(rack_req)
93
93
  rack_req.GET
94
94
  rescue
@@ -104,7 +104,7 @@ module Rollbar
104
104
  def rollbar_request_params(env)
105
105
  env['action_dispatch.request.parameters'] || {}
106
106
  end
107
-
107
+
108
108
  def rollbar_route_params(env)
109
109
  begin
110
110
  route = ::Rails.application.routes.recognize_path(env['PATH_INFO'])
@@ -117,7 +117,7 @@ module Rollbar
117
117
  {}
118
118
  end
119
119
  end
120
-
120
+
121
121
  def rollbar_request_cookies(rack_req)
122
122
  rack_req.cookies
123
123
  rescue
@@ -126,7 +126,7 @@ module Rollbar
126
126
 
127
127
  def rollbar_filtered_params(sensitive_params, params)
128
128
  @sensitive_params_regexp ||= Regexp.new(sensitive_params.map{ |val| Regexp.escape(val.to_s).to_s }.join('|'), true)
129
-
129
+
130
130
  if params.nil?
131
131
  {}
132
132
  else
@@ -152,11 +152,11 @@ module Rollbar
152
152
  end
153
153
 
154
154
  def sensitive_params_list(env)
155
- Rollbar.configuration.scrub_fields |= Array(env['action_dispatch.parameter_filter'])
155
+ Array(Rollbar.configuration.scrub_fields) | Array(env['action_dispatch.parameter_filter'])
156
156
  end
157
157
 
158
158
  def sensitive_headers_list
159
- Rollbar.configuration.scrub_headers |= []
159
+ Rollbar.configuration.scrub_headers || []
160
160
  end
161
161
 
162
162
  def rollbar_scrubbed(value)
@@ -1,3 +1,3 @@
1
1
  module Rollbar
2
- VERSION = "1.0.1"
2
+ VERSION = "1.1.0"
3
3
  end
@@ -24,4 +24,6 @@ Gem::Specification.new do |gem|
24
24
  gem.add_development_dependency 'sucker_punch', '>= 1.0.0' if RUBY_VERSION != '1.8.7'
25
25
  gem.add_development_dependency 'sidekiq', '>= 2.13.0' if RUBY_VERSION != '1.8.7'
26
26
  gem.add_development_dependency 'genspec', '>= 0.2.7'
27
+ gem.add_development_dependency 'sinatra'
28
+ gem.add_development_dependency 'resque'
27
29
  end
@@ -22,7 +22,7 @@ describe HomeController do
22
22
  expect{ get 'current_user', nil, :cookie => '8%B' }.to raise_exception
23
23
 
24
24
  Rollbar.last_report.should_not be_nil
25
-
25
+
26
26
  exception_info = Rollbar.last_report[:body][:trace][:exception]
27
27
  exception_info[:class].should == 'ArgumentError'
28
28
  exception_info[:message].should == 'invalid %-encoding (8%B)'
@@ -44,11 +44,13 @@ describe HomeController do
44
44
  end
45
45
 
46
46
  it "should report uncaught exceptions" do
47
- expect{ get 'current_user' }.to raise_exception
47
+ expect { get 'current_user' }.to raise_exception
48
+
49
+ body = Rollbar.last_report[:body]
50
+ trace = body[:trace] && body[:trace] || body[:trace_chain][0]
48
51
 
49
- exception_info = Rollbar.last_report[:body][:trace][:exception]
50
- exception_info[:class].should == 'NoMethodError'
51
- # exception_info[:message].should == 'undefined method `-\' for "1":String'
52
+ trace[:exception][:class].should == 'NoMethodError'
53
+ trace[:exception][:message].should == 'undefined method `-\' for "1":String'
52
54
  end
53
55
  end
54
56
  end
@@ -0,0 +1,24 @@
1
+ require 'spec_helper'
2
+ require 'rollbar/configuration'
3
+
4
+ describe Rollbar::Configuration do
5
+
6
+ describe '#use_thread' do
7
+ it 'enables async and sets a Thread as handler' do
8
+ subject.use_thread
9
+
10
+ expect(subject.use_async).to be_eql(true)
11
+ expect(subject.async_handler).to be_eql(Rollbar::Delay::Thread)
12
+ end
13
+ end
14
+
15
+ describe '#use_resque' do
16
+ it 'enables async and sets Resque as the handler' do
17
+ require 'resque'
18
+ subject.use_resque(:queue => 'errors')
19
+
20
+ expect(subject.use_async).to be_eql(true)
21
+ expect(subject.async_handler).to be_eql(Rollbar::Delay::Resque)
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,19 @@
1
+ require 'spec_helper'
2
+
3
+ # require girl_friday in the test instead in the implementation
4
+ # just to let the user decide to load it or not
5
+ require 'girl_friday'
6
+ require 'rollbar/delay/girl_friday'
7
+
8
+ describe Rollbar::Delay::GirlFriday do
9
+ describe '.call' do
10
+ let(:payload) do
11
+ { :key => 'value' }
12
+ end
13
+
14
+ it 'push the payload into the queue' do
15
+ expect_any_instance_of(::GirlFriday::WorkQueue).to receive(:push).with(payload)
16
+ described_class.call(payload)
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,21 @@
1
+ require 'spec_helper'
2
+ require 'rollbar/delay/resque'
3
+
4
+ describe Rollbar::Delay::Resque do
5
+ describe '.call' do
6
+ let(:payload) do
7
+ { :key => 'value' }
8
+ end
9
+
10
+ before do
11
+ allow(Resque).to receive(:inline?).and_return(true)
12
+ end
13
+
14
+ it 'process the payload' do
15
+ loaded_hash = MultiJson.load(MultiJson.dump(payload))
16
+
17
+ expect(Rollbar).to receive(:process_payload).with(loaded_hash)
18
+ described_class.call(payload)
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,14 @@
1
+ require 'spec_helper'
2
+
3
+ describe Rollbar::Delay::Thread do
4
+ describe '.call' do
5
+ let(:payload) { { :key => 'value' } }
6
+
7
+ it 'process the payload in a new thread' do
8
+ expect(Rollbar).to receive(:process_payload).with(payload)
9
+
10
+ th = described_class.call(payload)
11
+ th.join
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,88 @@
1
+ require 'spec_helper'
2
+ require 'rollbar/middleware/sinatra'
3
+ require 'sinatra/base'
4
+ require 'rack/test'
5
+
6
+ class SinatraDummy < Sinatra::Base
7
+ class DummyError < StandardError; end
8
+
9
+ use Rollbar::Middleware::Sinatra
10
+
11
+ get '/foo' do
12
+ raise DummyError.new
13
+ end
14
+
15
+ get '/bar' do
16
+ 'this will not crash'
17
+ end
18
+ end
19
+
20
+ describe Rollbar::Middleware::Sinatra do
21
+ include Rack::Test::Methods
22
+
23
+ def app
24
+ SinatraDummy
25
+ end
26
+
27
+ let(:expected_report_args) do
28
+ [exception, kind_of(Hash), kind_of(Hash)]
29
+ end
30
+
31
+ describe '#call' do
32
+ context 'for a crashing endpoint' do
33
+ # this is the default for test mode in Sinatra
34
+ context 'with raise_errors? == true' do
35
+ let(:exception) { kind_of(SinatraDummy::DummyError) }
36
+
37
+ before do
38
+ allow(app.settings).to receive(:raise_errors?).and_return(true)
39
+ end
40
+
41
+ it 'reports the error to Rollbar API and raises error' do
42
+ expect(Rollbar).to receive(:report_exception).with(*expected_report_args)
43
+
44
+ expect do
45
+ get '/foo'
46
+ end.to raise_error(SinatraDummy::DummyError)
47
+ end
48
+ end
49
+
50
+ context 'with raise_errors? == false' do
51
+ let(:exception) { kind_of(SinatraDummy::DummyError) }
52
+
53
+ before do
54
+ allow(app.settings).to receive(:raise_errors?).and_return(false)
55
+ end
56
+
57
+ it 'reports the error to Rollbar, but nothing is raised' do
58
+ expect(Rollbar).to receive(:report_exception).with(*expected_report_args)
59
+ get '/foo'
60
+ end
61
+ end
62
+ end
63
+
64
+ context 'for a NOT crashing endpoint' do
65
+ it 'doesnt report any error to Rollbar API' do
66
+ expect(Rollbar).not_to receive(:report_exception)
67
+ get '/bar'
68
+ end
69
+ end
70
+
71
+ context 'if the middleware itself fails' do
72
+ let(:exception) { Exception.new }
73
+
74
+ before do
75
+ allow_any_instance_of(described_class).to receive(:framework_error).and_raise(exception)
76
+ allow(app.settings).to receive(:raise_errors?).and_return(false)
77
+ end
78
+
79
+ it 'reports the report error' do
80
+ expect(Rollbar).to receive(:report_exception).with(*expected_report_args)
81
+
82
+ expect do
83
+ get '/foo'
84
+ end.to raise_error(exception)
85
+ end
86
+ end
87
+ end
88
+ end
@@ -12,6 +12,7 @@ rescue LoadError
12
12
  end
13
13
 
14
14
  describe Rollbar do
15
+ let(:configuration) { Rollbar.configuration }
15
16
 
16
17
  describe '.report_exception' do
17
18
  before(:each) do
@@ -443,14 +444,14 @@ describe Rollbar do
443
444
 
444
445
  Rollbar.configure do |config|
445
446
  config.use_async = true
446
- GirlFriday::WorkQueue::immediate!
447
+ GirlFriday::WorkQueue.immediate!
447
448
  end
448
449
 
449
450
  Rollbar.report_exception(@exception)
450
451
 
451
452
  Rollbar.configure do |config|
452
453
  config.use_async = false
453
- GirlFriday::WorkQueue::queue!
454
+ GirlFriday::WorkQueue.queue!
454
455
  end
455
456
  end
456
457
 
@@ -471,7 +472,7 @@ describe Rollbar do
471
472
 
472
473
  Rollbar.configure do |config|
473
474
  config.use_async = false
474
- config.async_handler = Rollbar.method(:default_async_handler)
475
+ config.async_handler = Rollbar.default_async_handler
475
476
  end
476
477
  end
477
478
 
@@ -498,6 +499,84 @@ describe Rollbar do
498
499
 
499
500
  Rollbar.report_exception(@exception)
500
501
  end
502
+
503
+ context 'with async failover handlers' do
504
+ before do
505
+ Rollbar.reconfigure do |config|
506
+ config.use_async = true
507
+ config.async_handler = async_handler
508
+ config.failover_handlers = handlers
509
+ config.logger = logger_mock
510
+ end
511
+ end
512
+
513
+ let(:exception) { StandardError.new('the error') }
514
+
515
+ context 'if the async handler doesnt fail' do
516
+ let(:async_handler) { proc { |_| 'success' } }
517
+ let(:handler) { proc { |_| 'success' } }
518
+ let(:handlers) { [handler] }
519
+
520
+ it 'doesnt call any failover handler' do
521
+ expect(handler).not_to receive(:call)
522
+
523
+ Rollbar.report_exception(exception)
524
+ end
525
+ end
526
+
527
+ context 'if the async handler fails' do
528
+ let(:async_handler) { proc { |_| fail 'this handler will crash' } }
529
+
530
+ context 'if any failover handlers is configured' do
531
+ let(:handlers) { [] }
532
+ let(:log_message) do
533
+ '[Rollbar] Async handler failed, and there are no failover handlers configured. See the docs for "failover_handlers"'
534
+ end
535
+
536
+ it 'logs the error but doesnt try to report an internal error' do
537
+ expect(logger_mock).to receive(:error).with(log_message)
538
+
539
+ Rollbar.report_exception(exception)
540
+ end
541
+ end
542
+
543
+ context 'if the first failover handler success' do
544
+ let(:handler) { proc { |_| 'success' } }
545
+ let(:handlers) { [handler] }
546
+
547
+ it 'calls the failover handler and doesnt report internal error' do
548
+ expect(Rollbar).not_to receive(:report_internal_error)
549
+ expect(handler).to receive(:call)
550
+
551
+ Rollbar.report_exception(exception)
552
+ end
553
+ end
554
+
555
+ context 'with two handlers, the first failing' do
556
+ let(:handler1) { proc { |_| fail 'this handler fails' } }
557
+ let(:handler2) { proc { |_| 'success' } }
558
+ let(:handlers) { [handler1, handler2] }
559
+
560
+ it 'calls the second handler and doesnt report internal error' do
561
+ expect(handler2).to receive(:call)
562
+
563
+ Rollbar.report_exception(exception)
564
+ end
565
+ end
566
+
567
+ context 'with two handlers, both failing' do
568
+ let(:handler1) { proc { |_| fail 'this handler fails' } }
569
+ let(:handler2) { proc { |_| fail 'this will also fail' } }
570
+ let(:handlers) { [handler1, handler2] }
571
+
572
+ it 'reports internal error' do
573
+ expect(logger_mock).to receive(:error)
574
+
575
+ Rollbar.report_exception(exception)
576
+ end
577
+ end
578
+ end
579
+ end
501
580
  end
502
581
 
503
582
  describe "#use_sucker_punch", :if => defined?(SuckerPunch) do
@@ -514,7 +593,7 @@ describe Rollbar do
514
593
 
515
594
  Rollbar.configure do |config|
516
595
  config.use_async = false
517
- config.async_handler = Rollbar.method(:default_async_handler)
596
+ config.async_handler = Rollbar.default_async_handler
518
597
  end
519
598
  end
520
599
  end
@@ -539,7 +618,7 @@ describe Rollbar do
539
618
 
540
619
  Rollbar.configure do |config|
541
620
  config.use_async = false
542
- config.async_handler = Rollbar.method(:default_async_handler)
621
+ config.async_handler = Rollbar.default_async_handler
543
622
  end
544
623
  end
545
624
  end
@@ -592,7 +671,7 @@ describe Rollbar do
592
671
  end
593
672
  end
594
673
 
595
- context 'exception_data' do
674
+ describe '.exception_data' do
596
675
  before(:each) do
597
676
  configure
598
677
  begin
@@ -647,6 +726,51 @@ describe Rollbar do
647
726
  end
648
727
  end
649
728
 
729
+ context 'with nested exceptions' do
730
+ let(:crashing_code) do
731
+ proc do
732
+ begin
733
+ begin
734
+ fail CauseException.new('the cause')
735
+ rescue
736
+ fail StandardError.new('the error')
737
+ end
738
+ rescue => e
739
+ e
740
+ end
741
+ end
742
+ end
743
+ let(:rescued_exception) { crashing_code.call }
744
+
745
+ if Exception.instance_methods.include?(:cause)
746
+ it 'sends the two exceptions in the trace_chain attribute' do
747
+ data = Rollbar.send(:exception_data, rescued_exception)
748
+ body = data[:body]
749
+
750
+ body[:trace].should be_nil
751
+ body[:trace_chain].should be_kind_of(Array)
752
+
753
+ chain = body[:trace_chain]
754
+ chain[0][:exception][:class].should match(/StandardError/)
755
+ chain[0][:exception][:message].should match(/the error/)
756
+
757
+ chain[1][:exception][:class].should match(/CauseException/)
758
+ chain[1][:exception][:message].should match(/the cause/)
759
+ end
760
+
761
+ else
762
+ it 'sends only the last exception in the trace attribute' do
763
+ data = Rollbar.send(:exception_data, rescued_exception)
764
+ body = data[:body]
765
+
766
+ body[:trace].should be_kind_of(Hash)
767
+ body[:trace_chain].should be_nil
768
+
769
+ body[:trace][:exception][:class].should match(/StandardError/)
770
+ body[:trace][:exception][:message].should match(/the error/)
771
+ end
772
+ end
773
+ end
650
774
  end
651
775
 
652
776
  context 'logger' do
@@ -739,13 +863,16 @@ describe Rollbar do
739
863
 
740
864
  context 'enforce_valid_utf8' do
741
865
  it 'should replace invalid utf8 values' do
866
+ bad_key = "inner \x92bad key"
867
+ bad_key.force_encoding('ASCII-8BIT') if bad_key.respond_to?('force_encoding')
868
+
742
869
  payload = {
743
870
  :bad_value => "bad value 1\255",
744
871
  :bad_value_2 => "bad\255 value 2",
745
872
  "bad\255 key" => "good value",
746
873
  :hash => {
747
874
  :inner_bad_value => "\255\255bad value 3",
748
- "inner \255bad key" => 'inner good value',
875
+ bad_key.to_sym => 'inner good value',
749
876
  "bad array key\255" => [
750
877
  'good array value 1',
751
878
  "bad\255 array value 1\255",
@@ -764,7 +891,7 @@ describe Rollbar do
764
891
  payload_copy["bad key"].should == "good value"
765
892
  payload_copy.keys.should_not include("bad\456 key")
766
893
  payload_copy[:hash][:inner_bad_value].should == "bad value 3"
767
- payload_copy[:hash]["inner bad key"].should == 'inner good value'
894
+ payload_copy[:hash][:"inner bad key"].should == 'inner good value'
768
895
  payload_copy[:hash]["bad array key"].should == [
769
896
  'good array value 1',
770
897
  'bad array value 1',
@@ -1,6 +1,6 @@
1
1
  require 'rubygems'
2
2
 
3
- ENV['RAILS_ENV'] = 'test'
3
+ ENV['RAILS_ENV'] = ENV['RACK_ENV'] = 'test'
4
4
  require File.expand_path('../dummyapp/config/environment', __FILE__)
5
5
  require 'rspec/rails'
6
6
  require 'database_cleaner'
@@ -0,0 +1 @@
1
+ class CauseException < StandardError; end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rollbar
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.1
4
+ version: 1.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Rollbar, Inc.
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-09-04 00:00:00.000000000 Z
11
+ date: 2014-10-08 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: multi_json
@@ -122,6 +122,34 @@ dependencies:
122
122
  - - '>='
123
123
  - !ruby/object:Gem::Version
124
124
  version: 0.2.7
125
+ - !ruby/object:Gem::Dependency
126
+ name: sinatra
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - '>='
130
+ - !ruby/object:Gem::Version
131
+ version: '0'
132
+ type: :development
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - '>='
137
+ - !ruby/object:Gem::Version
138
+ version: '0'
139
+ - !ruby/object:Gem::Dependency
140
+ name: resque
141
+ requirement: !ruby/object:Gem::Requirement
142
+ requirements:
143
+ - - '>='
144
+ - !ruby/object:Gem::Version
145
+ version: '0'
146
+ type: :development
147
+ prerelease: false
148
+ version_requirements: !ruby/object:Gem::Requirement
149
+ requirements:
150
+ - - '>='
151
+ - !ruby/object:Gem::Version
152
+ version: '0'
125
153
  description: Rails plugin to catch and send exceptions to Rollbar
126
154
  email:
127
155
  - support@rollbar.com
@@ -152,8 +180,11 @@ files:
152
180
  - lib/rollbar/capistrano.rb
153
181
  - lib/rollbar/capistrano3.rb
154
182
  - lib/rollbar/configuration.rb
183
+ - lib/rollbar/delay/girl_friday.rb
184
+ - lib/rollbar/delay/resque.rb
155
185
  - lib/rollbar/delay/sidekiq.rb
156
186
  - lib/rollbar/delay/sucker_punch.rb
187
+ - lib/rollbar/delay/thread.rb
157
188
  - lib/rollbar/delayed_job.rb
158
189
  - lib/rollbar/exception_reporter.rb
159
190
  - lib/rollbar/goalie.rb
@@ -161,6 +192,7 @@ files:
161
192
  - lib/rollbar/middleware/rack/test_session.rb
162
193
  - lib/rollbar/middleware/rails/rollbar_request_store.rb
163
194
  - lib/rollbar/middleware/rails/show_exceptions.rb
195
+ - lib/rollbar/middleware/sinatra.rb
164
196
  - lib/rollbar/rack.rb
165
197
  - lib/rollbar/rails.rb
166
198
  - lib/rollbar/rails/controller_methods.rb
@@ -228,8 +260,14 @@ files:
228
260
  - spec/dummyapp/script/rails
229
261
  - spec/generators/rollbar/rollbar_generator_spec.rb
230
262
  - spec/requests/home_spec.rb
263
+ - spec/rollbar/configuration_spec.rb
264
+ - spec/rollbar/delay/girl_friday_spec.rb
265
+ - spec/rollbar/delay/resque_spec.rb
266
+ - spec/rollbar/delay/thread_spec.rb
267
+ - spec/rollbar/middleware/sinatra_spec.rb
231
268
  - spec/rollbar_spec.rb
232
269
  - spec/spec_helper.rb
270
+ - spec/support/cause_exception.rb
233
271
  homepage: https://github.com/rollbar/rollbar-gem
234
272
  licenses:
235
273
  - MIT
@@ -310,5 +348,11 @@ test_files:
310
348
  - spec/dummyapp/script/rails
311
349
  - spec/generators/rollbar/rollbar_generator_spec.rb
312
350
  - spec/requests/home_spec.rb
351
+ - spec/rollbar/configuration_spec.rb
352
+ - spec/rollbar/delay/girl_friday_spec.rb
353
+ - spec/rollbar/delay/resque_spec.rb
354
+ - spec/rollbar/delay/thread_spec.rb
355
+ - spec/rollbar/middleware/sinatra_spec.rb
313
356
  - spec/rollbar_spec.rb
314
357
  - spec/spec_helper.rb
358
+ - spec/support/cause_exception.rb