rollbar 1.0.1 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
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