bugsnag 5.5.0 → 6.0.0

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