bugsnag 5.5.0 → 6.0.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.
Files changed (67) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +33 -11
  3. data/CHANGELOG.md +23 -0
  4. data/Gemfile +20 -0
  5. data/Rakefile +19 -12
  6. data/UPGRADING.md +58 -0
  7. data/VERSION +1 -1
  8. data/bugsnag.gemspec +0 -9
  9. data/lib/bugsnag.rb +64 -86
  10. data/lib/bugsnag/configuration.rb +42 -26
  11. data/lib/bugsnag/delivery.rb +9 -0
  12. data/lib/bugsnag/delivery/synchronous.rb +3 -5
  13. data/lib/bugsnag/delivery/thread_queue.rb +4 -2
  14. data/lib/bugsnag/helpers.rb +5 -16
  15. data/lib/bugsnag/{delayed_job.rb → integrations/delayed_job.rb} +15 -7
  16. data/lib/bugsnag/{mailman.rb → integrations/mailman.rb} +13 -11
  17. data/lib/bugsnag/{que.rb → integrations/que.rb} +11 -11
  18. data/lib/bugsnag/{rack.rb → integrations/rack.rb} +22 -23
  19. data/lib/bugsnag/{rails → integrations/rails}/active_record_rescue.rb +9 -7
  20. data/lib/bugsnag/{rails → integrations/rails}/controller_methods.rb +0 -9
  21. data/lib/bugsnag/{railtie.rb → integrations/railtie.rb} +24 -21
  22. data/lib/bugsnag/{rake.rb → integrations/rake.rb} +12 -9
  23. data/lib/bugsnag/{resque.rb → integrations/resque.rb} +12 -9
  24. data/lib/bugsnag/integrations/shoryuken.rb +49 -0
  25. data/lib/bugsnag/{sidekiq.rb → integrations/sidekiq.rb} +13 -9
  26. data/lib/bugsnag/middleware/callbacks.rb +4 -8
  27. data/lib/bugsnag/middleware/classify_error.rb +7 -13
  28. data/lib/bugsnag/middleware/clearance_user.rb +8 -8
  29. data/lib/bugsnag/middleware/exception_meta_data.rb +34 -0
  30. data/lib/bugsnag/middleware/ignore_error_class.rb +21 -0
  31. data/lib/bugsnag/middleware/mailman.rb +4 -4
  32. data/lib/bugsnag/middleware/rack_request.rb +13 -13
  33. data/lib/bugsnag/middleware/rails3_request.rb +10 -10
  34. data/lib/bugsnag/middleware/rake.rb +5 -5
  35. data/lib/bugsnag/middleware/sidekiq.rb +5 -5
  36. data/lib/bugsnag/middleware/suggestion_data.rb +30 -0
  37. data/lib/bugsnag/middleware/warden_user.rb +6 -6
  38. data/lib/bugsnag/middleware_stack.rb +5 -5
  39. data/lib/bugsnag/report.rb +187 -0
  40. data/lib/bugsnag/stacktrace.rb +113 -0
  41. data/lib/bugsnag/tasks/bugsnag.rake +2 -70
  42. data/spec/cleaner_spec.rb +6 -0
  43. data/spec/configuration_spec.rb +1 -1
  44. data/spec/fixtures/middleware/internal_info_setter.rb +3 -3
  45. data/spec/fixtures/middleware/public_info_setter.rb +3 -3
  46. data/spec/fixtures/tasks/Rakefile +2 -3
  47. data/spec/integration_spec.rb +5 -20
  48. data/spec/{delayed_job_spec.rb → integrations/delayed_job_spec.rb} +0 -0
  49. data/spec/integrations/sidekiq_spec.rb +34 -0
  50. data/spec/middleware_spec.rb +108 -35
  51. data/spec/rack_spec.rb +1 -1
  52. data/spec/{notification_spec.rb → report_spec.rb} +226 -209
  53. data/spec/spec_helper.rb +18 -0
  54. data/spec/{code_spec.rb → stacktrace_spec.rb} +1 -1
  55. metadata +23 -139
  56. data/.document +0 -5
  57. data/lib/bugsnag/capistrano.rb +0 -7
  58. data/lib/bugsnag/capistrano2.rb +0 -32
  59. data/lib/bugsnag/delay/resque.rb +0 -21
  60. data/lib/bugsnag/deploy.rb +0 -35
  61. data/lib/bugsnag/middleware/rails2_request.rb +0 -52
  62. data/lib/bugsnag/notification.rb +0 -506
  63. data/lib/bugsnag/rails.rb +0 -70
  64. data/lib/bugsnag/rails/action_controller_rescue.rb +0 -74
  65. data/lib/bugsnag/shoryuken.rb +0 -41
  66. data/lib/bugsnag/tasks/bugsnag.cap +0 -48
  67. data/rails/init.rb +0 -7
@@ -4,25 +4,28 @@ Rake::TaskManager.record_task_metadata = true
4
4
 
5
5
  class Rake::Task
6
6
 
7
+ FRAMEWORK_ATTRIBUTES = {
8
+ :framework => "Rake"
9
+ }
10
+
7
11
  def execute_with_bugsnag(args=nil)
8
12
  Bugsnag.configuration.app_type ||= "rake"
9
13
  old_task = Bugsnag.configuration.request_data[:bugsnag_running_task]
10
- Bugsnag.set_request_data :bugsnag_running_task, self
14
+ Bugsnag.configuration.set_request_data :bugsnag_running_task, self
11
15
 
12
16
  execute_without_bugsnag(args)
13
17
 
14
18
  rescue Exception => ex
15
- Bugsnag.auto_notify(ex, {
16
- :severity_reason => {
17
- :type => Bugsnag::Notification::UNHANDLED_EXCEPTION_MIDDLEWARE,
18
- :attributes => {
19
- :framework => "Rake"
20
- }
19
+ Bugsnag.notify(ex, true) do |report|
20
+ report.severity = "error"
21
+ report.severity_reason = {
22
+ :type => Bugsnag::Report::UNHANDLED_EXCEPTION_MIDDLEWARE,
23
+ :attributes => FRAMEWORK_ATTRIBUTES
21
24
  }
22
- })
25
+ end
23
26
  raise
24
27
  ensure
25
- Bugsnag.set_request_data :bugsnag_running_task, old_task
28
+ Bugsnag.configuration.set_request_data :bugsnag_running_task, old_task
26
29
  end
27
30
 
28
31
  alias_method :execute_without_bugsnag, :execute
@@ -3,6 +3,11 @@ require "resque/failure/multiple"
3
3
 
4
4
  module Bugsnag
5
5
  class Resque < ::Resque::Failure::Base
6
+
7
+ FRAMEWORK_ATTRIBUTES = {
8
+ :framework => "Resque"
9
+ }
10
+
6
11
  def self.configure(&block)
7
12
  add_failure_backend
8
13
  Bugsnag.configure(&block)
@@ -26,16 +31,14 @@ module Bugsnag
26
31
  end
27
32
 
28
33
  def save
29
- Bugsnag.auto_notify(exception, {
30
- :context => "#{payload['class']}@#{queue}",
31
- :payload => payload,
32
- :severity_reason => {
33
- :type => Bugsnag::Notification::UNHANDLED_EXCEPTION_MIDDLEWARE,
34
- :attributes => {
35
- :framework => "Resque"
36
- }
34
+ Bugsnag.notify(exception, true) do |report|
35
+ report.severity = "error"
36
+ report.severity_reason = {
37
+ :type => Bugsnag::Report::UNHANDLED_EXCEPTION_MIDDLEWARE,
38
+ :attributes => FRAMEWORK_ATTRIBUTES
37
39
  }
38
- })
40
+ report.meta_data.merge!({:context => "#{payload['class']}@#{queue}", :payload => payload})
41
+ end
39
42
  end
40
43
  end
41
44
  end
@@ -0,0 +1,49 @@
1
+ require 'shoryuken'
2
+
3
+ module Bugsnag
4
+ class Shoryuken
5
+
6
+ FRAMEWORK_ATTRIBUTES = {
7
+ :framework => "Shoryuken"
8
+ }
9
+
10
+ def initialize
11
+ Bugsnag.configure do |config|
12
+ config.app_type ||= "shoryuken"
13
+ config.default_delivery_method = :synchronous
14
+ end
15
+ end
16
+
17
+ def call(_, queue, _, body)
18
+ begin
19
+ Bugsnag.before_notify_callbacks << lambda {|report|
20
+ report.add_tab(:shoryuken, {
21
+ queue: queue,
22
+ body: body
23
+ })
24
+ }
25
+
26
+ yield
27
+ rescue Exception => ex
28
+ unless [Interrupt, SystemExit, SignalException].include?(ex.class)
29
+ Bugsnag.auto_notify(ex, true) do |report|
30
+ report.severity = "error"
31
+ report.severity_reason = {
32
+ :type => Bugsnag::Report::UNHANDLED_EXCEPTION_MIDDLEWARE,
33
+ :attributes => Bugsnag::Shoryuken::FRAMEWORK_ATTRIBUTES
34
+ }
35
+ end
36
+ end
37
+ raise
38
+ ensure
39
+ Bugsnag.configuration.clear_request_data
40
+ end
41
+ end
42
+ end
43
+ end
44
+
45
+ ::Shoryuken.configure_server do |config|
46
+ config.server_middleware do |chain|
47
+ chain.add ::Bugsnag::Shoryuken
48
+ end
49
+ end
@@ -2,6 +2,11 @@ require 'sidekiq'
2
2
 
3
3
  module Bugsnag
4
4
  class Sidekiq
5
+
6
+ FRAMEWORK_ATTRIBUTES = {
7
+ :framework => "Sidekiq"
8
+ }
9
+
5
10
  def initialize
6
11
  Bugsnag.configuration.internal_middleware.use(Bugsnag::Middleware::Sidekiq)
7
12
  Bugsnag.configuration.app_type = "sidekiq"
@@ -11,22 +16,21 @@ module Bugsnag
11
16
  def call(worker, msg, queue)
12
17
  begin
13
18
  # store msg/queue in thread local state to be read by Bugsnag::Middleware::Sidekiq
14
- Bugsnag.set_request_data :sidekiq, { :msg => msg, :queue => queue }
19
+ Bugsnag.configuration.set_request_data :sidekiq, { :msg => msg, :queue => queue }
15
20
 
16
21
  yield
17
22
  rescue Exception => ex
18
23
  raise ex if [Interrupt, SystemExit, SignalException].include? ex.class
19
- Bugsnag.auto_notify(ex, {
20
- :severity_reason => {
21
- :type => Bugsnag::Notification::UNHANDLED_EXCEPTION_MIDDLEWARE,
22
- :attributes => {
23
- :framework => "Sidekiq"
24
- }
24
+ Bugsnag.notify(ex, true) do |report|
25
+ report.severity = "error"
26
+ report.severity_reason = {
27
+ :type => Bugsnag::Report::UNHANDLED_EXCEPTION_MIDDLEWARE,
28
+ :attributes => FRAMEWORK_ATTRIBUTES
25
29
  }
26
- })
30
+ end
27
31
  raise
28
32
  ensure
29
- Bugsnag.clear_request_data
33
+ Bugsnag.configuration.clear_request_data
30
34
  end
31
35
  end
32
36
  end
@@ -4,16 +4,12 @@ module Bugsnag::Middleware
4
4
  @bugsnag = bugsnag
5
5
  end
6
6
 
7
- def call(notification)
8
- if notification.request_data[:before_callbacks]
9
- notification.request_data[:before_callbacks].each {|c| c.call(*[notification][0...c.arity]) }
7
+ def call(report)
8
+ if report.request_data[:before_callbacks]
9
+ report.request_data[:before_callbacks].each {|c| c.call(*[report][0...c.arity]) }
10
10
  end
11
11
 
12
- @bugsnag.call(notification)
13
-
14
- if notification.request_data[:after_callbacks]
15
- notification.request_data[:after_callbacks].each {|c| c.call(*[notification][0...c.arity]) }
16
- end
12
+ @bugsnag.call(report)
17
13
  end
18
14
  end
19
15
  end
@@ -18,36 +18,30 @@ module Bugsnag::Middleware
18
18
  @bugsnag = bugsnag
19
19
  end
20
20
 
21
- def call(notification)
22
- notification.exceptions.each do |ex|
23
-
24
- outer_break = false
21
+ def call(report)
22
+ report.raw_exceptions.each do |ex|
25
23
 
26
24
  ancestor_chain = ex.class.ancestors.select {
27
- |ancestor| ancestor.is_a?(Class)
25
+ |ancestor| ancestor.is_a?(Class)
28
26
  }.map {
29
27
  |ancestor| ancestor.to_s
30
28
  }
31
29
 
32
30
  INFO_CLASSES.each do |info_class|
33
31
  if ancestor_chain.include?(info_class)
34
- notification.severity_reason = {
35
- :type => Bugsnag::Notification::ERROR_CLASS,
32
+ report.severity_reason = {
33
+ :type => Bugsnag::Report::ERROR_CLASS,
36
34
  :attributes => {
37
35
  :errorClass => info_class
38
36
  }
39
37
  }
40
- notification.severity = 'info'
41
- outer_break = true
38
+ report.severity = 'info'
42
39
  break
43
40
  end
44
41
  end
45
-
46
- break if outer_break
47
42
  end
48
43
 
49
- @bugsnag.call(notification)
44
+ @bugsnag.call(report)
50
45
  end
51
46
  end
52
47
  end
53
-
@@ -6,15 +6,15 @@ module Bugsnag::Middleware
6
6
  @bugsnag = bugsnag
7
7
  end
8
8
 
9
- def call(notification)
10
- if notification.request_data[:rack_env] &&
11
- notification.request_data[:rack_env][:clearance] &&
12
- notification.request_data[:rack_env][:clearance].signed_in? &&
13
- notification.request_data[:rack_env][:clearance].current_user
9
+ def call(report)
10
+ if report.request_data[:rack_env] &&
11
+ report.request_data[:rack_env]["clearance"] &&
12
+ report.request_data[:rack_env]["clearance"].signed_in? &&
13
+ report.request_data[:rack_env]["clearance"].current_user
14
14
 
15
15
  # Extract useful user information
16
16
  user = {}
17
- user_object = notification.request_data[:rack_env][:clearance].current_user
17
+ user_object = report.request_data[:rack_env]["clearance"].current_user
18
18
  if user_object
19
19
  # Build the bugsnag user info from the current user record
20
20
  COMMON_USER_FIELDS.each do |field|
@@ -22,10 +22,10 @@ module Bugsnag::Middleware
22
22
  end
23
23
  end
24
24
 
25
- notification.user = user unless user.empty?
25
+ report.user = user unless user.empty?
26
26
  end
27
27
 
28
- @bugsnag.call(notification)
28
+ @bugsnag.call(report)
29
29
  end
30
30
  end
31
31
  end
@@ -0,0 +1,34 @@
1
+ module Bugsnag::Middleware
2
+ class ExceptionMetaData
3
+ def initialize(bugsnag)
4
+ @bugsnag = bugsnag
5
+ end
6
+
7
+ def call(report)
8
+ # Apply the user's information attached to the exceptions
9
+ report.raw_exceptions.each do |exception|
10
+ if exception.class.include?(Bugsnag::MetaData)
11
+ if exception.bugsnag_user_id.is_a?(String)
12
+ report.user = {id: exception.bugsnag_user_id}
13
+ end
14
+
15
+ if exception.bugsnag_context.is_a?(String)
16
+ report.context = exception.bugsnag_context
17
+ end
18
+
19
+ if exception.bugsnag_grouping_hash.is_a?(String)
20
+ report.grouping_hash = exception.bugsnag_grouping_hash
21
+ end
22
+
23
+ if exception.respond_to?(:bugsnag_meta_data) && exception.bugsnag_meta_data
24
+ exception.bugsnag_meta_data.each do |key, value|
25
+ report.add_tab key, value
26
+ end
27
+ end
28
+ end
29
+ end
30
+
31
+ @bugsnag.call(report)
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,21 @@
1
+ module Bugsnag::Middleware
2
+ class IgnoreErrorClass
3
+ def initialize(bugsnag)
4
+ @bugsnag = bugsnag
5
+ end
6
+
7
+ def call(report)
8
+ ignore_error_class = report.raw_exceptions.any? do |ex|
9
+ ancestor_chain = ex.class.ancestors.select { |ancestor| ancestor.is_a?(Class) }.to_set
10
+
11
+ report.configuration.ignore_classes.any? do |to_ignore|
12
+ to_ignore.is_a?(Proc) ? to_ignore.call(ex) : ancestor_chain.include?(to_ignore)
13
+ end
14
+ end
15
+
16
+ report.ignore! if ignore_error_class
17
+
18
+ @bugsnag.call(report)
19
+ end
20
+ end
21
+ end
@@ -4,10 +4,10 @@ module Bugsnag::Middleware
4
4
  @bugsnag = bugsnag
5
5
  end
6
6
 
7
- def call(notification)
8
- mailman_msg = notification.request_data[:mailman_msg]
9
- notification.add_tab(:mailman, {"message" => mailman_msg}) if mailman_msg
10
- @bugsnag.call(notification)
7
+ def call(report)
8
+ mailman_msg = report.request_data[:mailman_msg]
9
+ report.add_tab(:mailman, {"message" => mailman_msg}) if mailman_msg
10
+ @bugsnag.call(report)
11
11
  end
12
12
  end
13
13
  end
@@ -6,9 +6,9 @@ module Bugsnag::Middleware
6
6
  @bugsnag = bugsnag
7
7
  end
8
8
 
9
- def call(notification)
10
- if notification.request_data[:rack_env]
11
- env = notification.request_data[:rack_env]
9
+ def call(report)
10
+ if report.request_data[:rack_env]
11
+ env = report.request_data[:rack_env]
12
12
 
13
13
  request = ::Rack::Request.new(env)
14
14
 
@@ -17,10 +17,10 @@ module Bugsnag::Middleware
17
17
  session = env["rack.session"]
18
18
 
19
19
  # Set the context
20
- notification.context = "#{request.request_method} #{request.path}"
20
+ report.context = "#{request.request_method} #{request.path}"
21
21
 
22
22
  # Set a sensible default for user_id
23
- notification.user_id = request.ip
23
+ report.user["id"] = request.ip
24
24
 
25
25
  # Build the clean url (hide the port if it is obvious)
26
26
  url = "#{request.scheme}://#{request.host}"
@@ -28,9 +28,9 @@ module Bugsnag::Middleware
28
28
 
29
29
  # If app is passed a bad URL, this code will crash attempting to clean it
30
30
  begin
31
- url << Bugsnag::Cleaner.new(notification.configuration.params_filters).clean_url(request.fullpath)
31
+ url << Bugsnag::Cleaner.new(report.configuration.meta_data_filters).clean_url(request.fullpath)
32
32
  rescue StandardError => stde
33
- Bugsnag.log "RackRequest - Rescued error while cleaning request.fullpath: #{stde}"
33
+ Bugsnag.configuration.warn "RackRequest - Rescued error while cleaning request.fullpath: #{stde}"
34
34
  end
35
35
 
36
36
  headers = {}
@@ -48,7 +48,7 @@ module Bugsnag::Middleware
48
48
  end
49
49
 
50
50
  # Add a request tab
51
- notification.add_tab(:request, {
51
+ report.add_tab(:request, {
52
52
  :url => url,
53
53
  :httpMethod => request.request_method,
54
54
  :params => params.to_hash,
@@ -58,23 +58,23 @@ module Bugsnag::Middleware
58
58
  })
59
59
 
60
60
  # Add an environment tab
61
- if notification.configuration.send_environment
62
- notification.add_tab(:environment, env)
61
+ if report.configuration.send_environment
62
+ report.add_tab(:environment, env)
63
63
  end
64
64
 
65
65
  # Add a session tab
66
66
  if session
67
67
  if session.is_a?(Hash)
68
68
  # Rails 3
69
- notification.add_tab(:session, session)
69
+ report.add_tab(:session, session)
70
70
  elsif session.respond_to?(:to_hash)
71
71
  # Rails 4
72
- notification.add_tab(:session, session.to_hash)
72
+ report.add_tab(:session, session.to_hash)
73
73
  end
74
74
  end
75
75
  end
76
76
 
77
- @bugsnag.call(notification)
77
+ @bugsnag.call(report)
78
78
  end
79
79
  end
80
80
  end
@@ -6,40 +6,40 @@ module Bugsnag::Middleware
6
6
  @bugsnag = bugsnag
7
7
  end
8
8
 
9
- def call(notification)
10
- if notification.request_data[:rack_env]
11
- env = notification.request_data[:rack_env]
9
+ def call(report)
10
+ if report.request_data[:rack_env]
11
+ env = report.request_data[:rack_env]
12
12
  params = env["action_dispatch.request.parameters"]
13
13
  client_ip = env["action_dispatch.remote_ip"].to_s rescue SPOOF
14
14
 
15
15
  if params
16
16
  # Set the context
17
- notification.context = "#{params[:controller]}##{params[:action]}"
17
+ report.context = "#{params[:controller]}##{params[:action]}"
18
18
 
19
19
  # Augment the request tab
20
- notification.add_tab(:request, {
20
+ report.add_tab(:request, {
21
21
  :railsAction => "#{params[:controller]}##{params[:action]}",
22
22
  :params => params
23
23
  })
24
24
  end
25
25
 
26
26
  # Use action_dispatch.remote_ip for IP address fields and send request id
27
- notification.add_tab(:request, {
27
+ report.add_tab(:request, {
28
28
  :clientIp => client_ip,
29
29
  :requestId => env["action_dispatch.request_id"]
30
30
  })
31
31
 
32
- notification.user_id = client_ip
32
+ report.user["id"] = client_ip
33
33
 
34
34
  # Add the rails version
35
- if notification.configuration.send_environment
36
- notification.add_tab(:environment, {
35
+ if report.configuration.send_environment
36
+ report.add_tab(:environment, {
37
37
  :railsVersion => Rails::VERSION::STRING
38
38
  })
39
39
  end
40
40
  end
41
41
 
42
- @bugsnag.call(notification)
42
+ @bugsnag.call(report)
43
43
  end
44
44
  end
45
45
  end