bugsnag 4.2.1 → 6.27.1

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 (106) hide show
  1. checksums.yaml +5 -5
  2. data/.yardopts +12 -0
  3. data/CHANGELOG.md +814 -0
  4. data/README.md +21 -25
  5. data/VERSION +1 -1
  6. data/bugsnag.gemspec +19 -8
  7. data/lib/bugsnag/breadcrumb_type.rb +14 -0
  8. data/lib/bugsnag/breadcrumbs/breadcrumb.rb +109 -0
  9. data/lib/bugsnag/breadcrumbs/breadcrumbs.rb +13 -0
  10. data/lib/bugsnag/breadcrumbs/on_breadcrumb_callback_list.rb +48 -0
  11. data/lib/bugsnag/breadcrumbs/validator.rb +29 -0
  12. data/lib/bugsnag/cleaner.rb +170 -59
  13. data/lib/bugsnag/code_extractor.rb +137 -0
  14. data/lib/bugsnag/configuration.rb +670 -45
  15. data/lib/bugsnag/delivery/synchronous.rb +31 -14
  16. data/lib/bugsnag/delivery/thread_queue.rb +23 -6
  17. data/lib/bugsnag/delivery.rb +13 -0
  18. data/lib/bugsnag/endpoint_configuration.rb +11 -0
  19. data/lib/bugsnag/endpoint_validator.rb +80 -0
  20. data/lib/bugsnag/error.rb +25 -0
  21. data/lib/bugsnag/event.rb +5 -0
  22. data/lib/bugsnag/feature_flag.rb +74 -0
  23. data/lib/bugsnag/helpers.rb +121 -25
  24. data/lib/bugsnag/integrations/delayed_job.rb +51 -0
  25. data/lib/bugsnag/integrations/mailman.rb +43 -0
  26. data/lib/bugsnag/integrations/mongo.rb +133 -0
  27. data/lib/bugsnag/integrations/que.rb +53 -0
  28. data/lib/bugsnag/integrations/rack.rb +83 -0
  29. data/lib/bugsnag/integrations/rails/active_job.rb +100 -0
  30. data/lib/bugsnag/{rails → integrations/rails}/active_record_rescue.rb +10 -1
  31. data/lib/bugsnag/{rails → integrations/rails}/controller_methods.rb +1 -9
  32. data/lib/bugsnag/integrations/rails/rails_breadcrumbs.rb +115 -0
  33. data/lib/bugsnag/integrations/railtie.rb +153 -0
  34. data/lib/bugsnag/integrations/rake.rb +74 -0
  35. data/lib/bugsnag/integrations/resque.rb +94 -0
  36. data/lib/bugsnag/integrations/shoryuken.rb +50 -0
  37. data/lib/bugsnag/integrations/sidekiq.rb +68 -0
  38. data/lib/bugsnag/meta_data.rb +1 -0
  39. data/lib/bugsnag/middleware/active_job.rb +18 -0
  40. data/lib/bugsnag/middleware/breadcrumbs.rb +21 -0
  41. data/lib/bugsnag/middleware/callbacks.rb +6 -8
  42. data/lib/bugsnag/middleware/classify_error.rb +50 -0
  43. data/lib/bugsnag/middleware/clearance_user.rb +33 -0
  44. data/lib/bugsnag/middleware/delayed_job.rb +93 -0
  45. data/lib/bugsnag/middleware/discard_error_class.rb +30 -0
  46. data/lib/bugsnag/middleware/exception_meta_data.rb +42 -0
  47. data/lib/bugsnag/middleware/ignore_error_class.rb +26 -0
  48. data/lib/bugsnag/middleware/mailman.rb +6 -4
  49. data/lib/bugsnag/middleware/rack_request.rb +126 -30
  50. data/lib/bugsnag/middleware/rails3_request.rb +15 -17
  51. data/lib/bugsnag/middleware/rake.rb +7 -5
  52. data/lib/bugsnag/middleware/session_data.rb +25 -0
  53. data/lib/bugsnag/middleware/sidekiq.rb +9 -4
  54. data/lib/bugsnag/middleware/suggestion_data.rb +34 -0
  55. data/lib/bugsnag/middleware/warden_user.rb +11 -6
  56. data/lib/bugsnag/middleware_stack.rb +62 -9
  57. data/lib/bugsnag/on_error_callbacks.rb +33 -0
  58. data/lib/bugsnag/report.rb +516 -0
  59. data/lib/bugsnag/session_tracker.rb +182 -0
  60. data/lib/bugsnag/stacktrace.rb +82 -0
  61. data/lib/bugsnag/tasks/bugsnag.rake +2 -70
  62. data/lib/bugsnag/utility/circular_buffer.rb +62 -0
  63. data/lib/bugsnag/utility/duplicator.rb +124 -0
  64. data/lib/bugsnag/utility/feature_data_store.rb +41 -0
  65. data/lib/bugsnag/utility/feature_flag_delegate.rb +89 -0
  66. data/lib/bugsnag/utility/metadata_delegate.rb +102 -0
  67. data/lib/bugsnag.rb +528 -80
  68. metadata +61 -123
  69. data/.document +0 -5
  70. data/.gitignore +0 -52
  71. data/.rspec +0 -3
  72. data/.travis.yml +0 -14
  73. data/CONTRIBUTING.md +0 -47
  74. data/Gemfile +0 -2
  75. data/Rakefile +0 -29
  76. data/lib/bugsnag/capistrano.rb +0 -7
  77. data/lib/bugsnag/capistrano2.rb +0 -32
  78. data/lib/bugsnag/delay/resque.rb +0 -21
  79. data/lib/bugsnag/delayed_job.rb +0 -57
  80. data/lib/bugsnag/deploy.rb +0 -34
  81. data/lib/bugsnag/mailman.rb +0 -28
  82. data/lib/bugsnag/middleware/rails2_request.rb +0 -52
  83. data/lib/bugsnag/notification.rb +0 -459
  84. data/lib/bugsnag/rack.rb +0 -53
  85. data/lib/bugsnag/rails/action_controller_rescue.rb +0 -62
  86. data/lib/bugsnag/rails.rb +0 -66
  87. data/lib/bugsnag/railtie.rb +0 -80
  88. data/lib/bugsnag/rake.rb +0 -25
  89. data/lib/bugsnag/resque.rb +0 -40
  90. data/lib/bugsnag/sidekiq.rb +0 -42
  91. data/lib/bugsnag/tasks/bugsnag.cap +0 -48
  92. data/rails/init.rb +0 -7
  93. data/spec/cleaner_spec.rb +0 -138
  94. data/spec/code_spec.rb +0 -86
  95. data/spec/fixtures/crashes/end_of_file.rb +0 -9
  96. data/spec/fixtures/crashes/short_file.rb +0 -1
  97. data/spec/fixtures/crashes/start_of_file.rb +0 -9
  98. data/spec/fixtures/middleware/internal_info_setter.rb +0 -11
  99. data/spec/fixtures/middleware/public_info_setter.rb +0 -11
  100. data/spec/fixtures/tasks/Rakefile +0 -15
  101. data/spec/helper_spec.rb +0 -163
  102. data/spec/integration_spec.rb +0 -132
  103. data/spec/middleware_spec.rb +0 -181
  104. data/spec/notification_spec.rb +0 -877
  105. data/spec/rack_spec.rb +0 -56
  106. data/spec/spec_helper.rb +0 -53
@@ -0,0 +1,74 @@
1
+ # this file can either be required manually by a user, in which case 'bugsnag'
2
+ # needs to be required, or it can be required automatically in the railtie,
3
+ # in which case 'bugsnag' has already been required
4
+ require 'bugsnag' unless defined?(Bugsnag)
5
+
6
+ Rake::TaskManager.record_task_metadata = true
7
+
8
+ if Gem::Version.new(RUBY_VERSION.dup) >= Gem::Version.new('2.0')
9
+ module Bugsnag
10
+ module RakeTask
11
+ FRAMEWORK_ATTRIBUTES = {
12
+ framework: 'Rake'
13
+ }
14
+
15
+ # Executes the rake task with Bugsnag setup with contextual data.
16
+ def execute(args = nil)
17
+ Bugsnag.configuration.detected_app_type = "rake"
18
+
19
+ old_task = Bugsnag.configuration.request_data[:bugsnag_running_task]
20
+ Bugsnag.configuration.set_request_data :bugsnag_running_task, self
21
+ Bugsnag.configuration.runtime_versions["rake"] = ::Rake::VERSION
22
+
23
+ super
24
+ rescue Exception => ex
25
+ Bugsnag.notify(ex, true) do |report|
26
+ report.severity = "error"
27
+ report.severity_reason = {
28
+ type: Bugsnag::Report::UNHANDLED_EXCEPTION_MIDDLEWARE,
29
+ attributes: FRAMEWORK_ATTRIBUTES
30
+ }
31
+ end
32
+ raise
33
+ ensure
34
+ Bugsnag.configuration.set_request_data :bugsnag_running_task, old_task
35
+ end
36
+ end
37
+ end
38
+
39
+ Rake::Task.send(:prepend, Bugsnag::RakeTask)
40
+ else
41
+ class Rake::Task
42
+ FRAMEWORK_ATTRIBUTES = {
43
+ framework: 'Rake'
44
+ }
45
+
46
+ ##
47
+ # Executes the rake task with Bugsnag setup with contextual data.
48
+ def execute_with_bugsnag(args=nil)
49
+ Bugsnag.configuration.detected_app_type = "rake"
50
+
51
+ old_task = Bugsnag.configuration.request_data[:bugsnag_running_task]
52
+ Bugsnag.configuration.set_request_data :bugsnag_running_task, self
53
+ Bugsnag.configuration.runtime_versions["rake"] = ::Rake::VERSION
54
+
55
+ execute_without_bugsnag(args)
56
+ rescue Exception => ex
57
+ Bugsnag.notify(ex, true) do |report|
58
+ report.severity = "error"
59
+ report.severity_reason = {
60
+ type: Bugsnag::Report::UNHANDLED_EXCEPTION_MIDDLEWARE,
61
+ attributes: FRAMEWORK_ATTRIBUTES
62
+ }
63
+ end
64
+ raise
65
+ ensure
66
+ Bugsnag.configuration.set_request_data :bugsnag_running_task, old_task
67
+ end
68
+
69
+ alias_method :execute_without_bugsnag, :execute
70
+ alias_method :execute, :execute_with_bugsnag
71
+ end
72
+ end
73
+
74
+ Bugsnag.configuration.internal_middleware.use(Bugsnag::Middleware::Rake)
@@ -0,0 +1,94 @@
1
+ require "resque"
2
+ require "resque/failure/multiple"
3
+
4
+ module Bugsnag
5
+ class Resque < ::Resque::Failure::Base
6
+
7
+ FRAMEWORK_ATTRIBUTES = {
8
+ :framework => "Resque"
9
+ }
10
+
11
+ ##
12
+ # Callthrough to Bugsnag configuration.
13
+ def self.configure(&block)
14
+ add_failure_backend
15
+ Bugsnag.configure(&block)
16
+ end
17
+
18
+ ##
19
+ # Sets up the Resque failure backend.
20
+ def self.add_failure_backend
21
+ return if ::Resque::Failure.backend == self
22
+
23
+ # Ensure resque is using a "Multiple" failure backend
24
+ unless ::Resque::Failure.backend <= ::Resque::Failure::Multiple
25
+ original_backend = ::Resque::Failure.backend
26
+ ::Resque::Failure.backend = ::Resque::Failure::Multiple
27
+ ::Resque::Failure.backend.classes ||= []
28
+ ::Resque::Failure.backend.classes << original_backend
29
+ end
30
+
31
+ # Add Bugsnag failure backend
32
+ unless ::Resque::Failure.backend.classes.include?(self)
33
+ ::Resque::Failure.backend.classes << self
34
+ end
35
+ end
36
+
37
+ ##
38
+ # Notifies Bugsnag of a raised exception.
39
+ def save
40
+ Bugsnag.notify(exception, true) do |report|
41
+ report.severity = "error"
42
+ report.severity_reason = {
43
+ :type => Bugsnag::Report::UNHANDLED_EXCEPTION_MIDDLEWARE,
44
+ :attributes => FRAMEWORK_ATTRIBUTES
45
+ }
46
+
47
+ metadata = payload
48
+ class_name = metadata['class']
49
+
50
+ # when using Active Job the payload "class" will always be the Resque
51
+ # "JobWrapper", so we need to unwrap the actual class name
52
+ if class_name == "ActiveJob::QueueAdapters::ResqueAdapter::JobWrapper"
53
+ unwrapped_class_name = metadata['args'][0]['job_class'] rescue nil
54
+
55
+ if unwrapped_class_name
56
+ class_name = unwrapped_class_name
57
+ metadata['wrapped'] ||= unwrapped_class_name
58
+ end
59
+ end
60
+
61
+ context = "#{class_name}@#{queue}"
62
+ report.meta_data.merge!({ context: context, payload: metadata })
63
+ report.automatic_context = context
64
+ end
65
+ end
66
+ end
67
+ end
68
+
69
+ # For backwards compatibility
70
+ Resque::Failure::Bugsnag = Bugsnag::Resque
71
+
72
+ # Auto-load the failure backend
73
+ Bugsnag::Resque.add_failure_backend
74
+
75
+ worker = Resque::Worker.new(:bugsnag_fork_check)
76
+
77
+ # If at_exit hooks are not enabled then we can't use the thread queue delivery
78
+ # method because it relies on an at_exit hook to ensure the queue is flushed
79
+ can_use_thread_queue = worker.respond_to?(:run_at_exit_hooks) && worker.run_at_exit_hooks
80
+ default_delivery_method = can_use_thread_queue ? :thread_queue : :synchronous
81
+
82
+ if worker.fork_per_job?
83
+ Resque.after_fork do
84
+ Bugsnag.configuration.detected_app_type = "resque"
85
+ Bugsnag.configuration.default_delivery_method = default_delivery_method
86
+ Bugsnag.configuration.runtime_versions["resque"] = ::Resque::VERSION
87
+ end
88
+ else
89
+ Resque.before_first_fork do
90
+ Bugsnag.configuration.detected_app_type = "resque"
91
+ Bugsnag.configuration.default_delivery_method = default_delivery_method
92
+ Bugsnag.configuration.runtime_versions["resque"] = ::Resque::VERSION
93
+ end
94
+ end
@@ -0,0 +1,50 @@
1
+ require 'shoryuken'
2
+
3
+ module Bugsnag
4
+ ##
5
+ # Extracts and attaches Shoryuken queue information to an error report
6
+ class Shoryuken
7
+
8
+ FRAMEWORK_ATTRIBUTES = {
9
+ :framework => "Shoryuken"
10
+ }
11
+
12
+ def initialize
13
+ Bugsnag.configure do |config|
14
+ config.detected_app_type = "shoryuken"
15
+ config.default_delivery_method = :synchronous
16
+ config.runtime_versions["shoryuken"] = ::Shoryuken::VERSION
17
+ end
18
+ end
19
+
20
+ def call(_, queue, _, body)
21
+ begin
22
+ Bugsnag.before_notify_callbacks << lambda {|report|
23
+ report.add_tab(:shoryuken, {
24
+ queue: queue,
25
+ body: body
26
+ })
27
+ }
28
+
29
+ yield
30
+ rescue Exception => ex
31
+ Bugsnag.notify(ex, true) do |report|
32
+ report.severity = "error"
33
+ report.severity_reason = {
34
+ :type => Bugsnag::Report::UNHANDLED_EXCEPTION_MIDDLEWARE,
35
+ :attributes => Bugsnag::Shoryuken::FRAMEWORK_ATTRIBUTES
36
+ }
37
+ end
38
+ raise
39
+ ensure
40
+ Bugsnag.configuration.clear_request_data
41
+ end
42
+ end
43
+ end
44
+ end
45
+
46
+ ::Shoryuken.configure_server do |config|
47
+ config.server_middleware do |chain|
48
+ chain.add ::Bugsnag::Shoryuken
49
+ end
50
+ end
@@ -0,0 +1,68 @@
1
+ require 'sidekiq'
2
+
3
+ module Bugsnag
4
+ ##
5
+ # Extracts and attaches Sidekiq job and queue information to an error report
6
+ class Sidekiq
7
+ include ::Sidekiq::ServerMiddleware if defined?(::Sidekiq::ServerMiddleware)
8
+
9
+ unless const_defined?(:FRAMEWORK_ATTRIBUTES)
10
+ FRAMEWORK_ATTRIBUTES = {
11
+ :framework => "Sidekiq"
12
+ }
13
+ end
14
+
15
+ def initialize
16
+ Bugsnag.configuration.internal_middleware.use(Bugsnag::Middleware::Sidekiq)
17
+ Bugsnag.configuration.detected_app_type = "sidekiq"
18
+ Bugsnag.configuration.runtime_versions["sidekiq"] = ::Sidekiq::VERSION
19
+ end
20
+
21
+ def call(worker, msg, queue)
22
+ # store msg/queue in thread local state to be read by Bugsnag::Middleware::Sidekiq
23
+ Bugsnag.configuration.set_request_data(:sidekiq, { :msg => msg, :queue => queue })
24
+ error_raised = false
25
+ yield
26
+ rescue Exception => ex
27
+ error_raised = true
28
+ self.class.notify(ex) unless self.class.sidekiq_supports_error_handlers
29
+ raise
30
+ ensure
31
+ # if an error was raised and error handlers are installed, the data will be cleared after
32
+ # the notification is sent. Otherwise, the data must be cleared.
33
+ keep_data = error_raised && self.class.sidekiq_supports_error_handlers
34
+ Bugsnag.configuration.clear_request_data unless keep_data
35
+ end
36
+
37
+ def self.notify(exception)
38
+ Bugsnag.notify(exception, true) do |report|
39
+ report.severity = "error"
40
+ report.severity_reason = {
41
+ :type => Bugsnag::Report::UNHANDLED_EXCEPTION_MIDDLEWARE,
42
+ :attributes => FRAMEWORK_ATTRIBUTES
43
+ }
44
+ end
45
+ end
46
+
47
+ def self.sidekiq_supports_error_handlers
48
+ Gem::Version.new(::Sidekiq::VERSION) >= Gem::Version.new('3.0.0')
49
+ end
50
+
51
+ def self.configure_server(server)
52
+ if Bugsnag::Sidekiq.sidekiq_supports_error_handlers
53
+ server.error_handlers << proc do |ex, _context, _config = nil|
54
+ Bugsnag::Sidekiq.notify(ex)
55
+ Bugsnag.configuration.clear_request_data
56
+ end
57
+ end
58
+
59
+ server.server_middleware do |chain|
60
+ chain.add ::Bugsnag::Sidekiq
61
+ end
62
+ end
63
+ end
64
+ end
65
+
66
+ ::Sidekiq.configure_server do |config|
67
+ Bugsnag::Sidekiq.configure_server(config)
68
+ end
@@ -3,5 +3,6 @@ module Bugsnag
3
3
  attr_accessor :bugsnag_meta_data
4
4
  attr_accessor :bugsnag_user_id
5
5
  attr_accessor :bugsnag_context
6
+ attr_accessor :bugsnag_grouping_hash
6
7
  end
7
8
  end
@@ -0,0 +1,18 @@
1
+ module Bugsnag::Middleware
2
+ class ActiveJob
3
+ def initialize(bugsnag)
4
+ @bugsnag = bugsnag
5
+ end
6
+
7
+ def call(report)
8
+ data = report.request_data[:active_job]
9
+
10
+ if data
11
+ report.add_tab(:active_job, data)
12
+ report.automatic_context = "#{data[:job_name]}@#{data[:queue]}"
13
+ end
14
+
15
+ @bugsnag.call(report)
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,21 @@
1
+ module Bugsnag::Middleware
2
+ ##
3
+ # Adds breadcrumbs to the report
4
+ class Breadcrumbs
5
+ ##
6
+ # @param next_callable [#call] the next callable middleware
7
+ def initialize(next_callable)
8
+ @next = next_callable
9
+ end
10
+
11
+ ##
12
+ # Execute this middleware
13
+ #
14
+ # @param report [Bugsnag::Report] the report being iterated over
15
+ def call(report)
16
+ breadcrumbs = report.configuration.breadcrumbs.to_a
17
+ report.breadcrumbs = breadcrumbs unless breadcrumbs.empty?
18
+ @next.call(report)
19
+ end
20
+ end
21
+ end
@@ -1,19 +1,17 @@
1
1
  module Bugsnag::Middleware
2
+ ##
3
+ # Calls all configured callbacks passing an error report
2
4
  class Callbacks
3
5
  def initialize(bugsnag)
4
6
  @bugsnag = bugsnag
5
7
  end
6
8
 
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]) }
9
+ def call(report)
10
+ if report.request_data[:before_callbacks]
11
+ report.request_data[:before_callbacks].each {|c| c.call(*[report][0...c.arity]) }
10
12
  end
11
13
 
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
14
+ @bugsnag.call(report)
17
15
  end
18
16
  end
19
17
  end
@@ -0,0 +1,50 @@
1
+ module Bugsnag::Middleware
2
+ ##
3
+ # Sets the severity to info for low-importance errors
4
+ class ClassifyError
5
+ INFO_CLASSES = [
6
+ "AbstractController::ActionNotFound",
7
+ "ActionController::InvalidAuthenticityToken",
8
+ "ActionController::ParameterMissing",
9
+ "ActionController::UnknownAction",
10
+ "ActionController::UnknownFormat",
11
+ "ActionController::UnknownHttpMethod",
12
+ "ActionDispatch::Http::MimeNegotiation::InvalidType",
13
+ "ActiveRecord::RecordNotFound",
14
+ "CGI::Session::CookieStore::TamperedWithCookie",
15
+ "Mongoid::Errors::DocumentNotFound",
16
+ "SignalException",
17
+ "SystemExit"
18
+ ]
19
+
20
+ def initialize(bugsnag)
21
+ @bugsnag = bugsnag
22
+ end
23
+
24
+ def call(report)
25
+ report.raw_exceptions.each do |ex|
26
+
27
+ ancestor_chain = ex.class.ancestors.select {
28
+ |ancestor| ancestor.is_a?(Class)
29
+ }.map {
30
+ |ancestor| ancestor.to_s
31
+ }
32
+
33
+ INFO_CLASSES.each do |info_class|
34
+ if ancestor_chain.include?(info_class)
35
+ report.severity_reason = {
36
+ :type => Bugsnag::Report::ERROR_CLASS,
37
+ :attributes => {
38
+ :errorClass => info_class
39
+ }
40
+ }
41
+ report.severity = 'info'
42
+ break
43
+ end
44
+ end
45
+ end
46
+
47
+ @bugsnag.call(report)
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,33 @@
1
+ module Bugsnag::Middleware
2
+ ##
3
+ # Extracts and appends clearance user information
4
+ class ClearanceUser
5
+ COMMON_USER_FIELDS = [:email, :name, :first_name, :last_name, :created_at, :id]
6
+
7
+ def initialize(bugsnag)
8
+ @bugsnag = bugsnag
9
+ end
10
+
11
+ def call(report)
12
+ if report.request_data[:rack_env] &&
13
+ report.request_data[:rack_env][:clearance] &&
14
+ report.request_data[:rack_env][:clearance].signed_in? &&
15
+ report.request_data[:rack_env][:clearance].current_user
16
+
17
+ # Extract useful user information
18
+ user = {}
19
+ user_object = report.request_data[:rack_env][:clearance].current_user
20
+ if user_object
21
+ # Build the bugsnag user info from the current user record
22
+ COMMON_USER_FIELDS.each do |field|
23
+ user[field] = user_object.send(field) if user_object.respond_to?(field)
24
+ end
25
+ end
26
+
27
+ report.user = user unless user.empty?
28
+ end
29
+
30
+ @bugsnag.call(report)
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,93 @@
1
+ module Bugsnag::Middleware
2
+ ##
3
+ # Attaches delayed_job information to an error report
4
+ class DelayedJob
5
+ # Active Job's queue adapter sets the "display_name" to this format. This
6
+ # breaks the event context as the ID and arguments are included, which will
7
+ # differ between executions of the same job
8
+ ACTIVE_JOB_DISPLAY_NAME = /^.* \[[0-9a-f-]+\] from DelayedJob\(.*\) with arguments: \[.*\]$/
9
+
10
+ def initialize(bugsnag)
11
+ @bugsnag = bugsnag
12
+ end
13
+
14
+ def call(report)
15
+ job = report.request_data[:delayed_job]
16
+ if job
17
+ job_data = {
18
+ :class => job.class.name,
19
+ :id => job.id
20
+ }
21
+ job_data[:priority] = job.priority if job.respond_to?(:priority)
22
+ job_data[:run_at] = job.run_at if job.respond_to?(:run_at)
23
+ job_data[:locked_at] = job.locked_at if job.respond_to?(:locked_at)
24
+ job_data[:locked_by] = job.locked_by if job.respond_to?(:locked_by)
25
+ job_data[:created_at] = job.created_at if job.respond_to?(:created_at)
26
+ job_data[:queue] = job.queue if job.respond_to?(:queue)
27
+
28
+ if job.respond_to?(:payload_object)
29
+ job_data[:active_job] = job.payload_object.job_data if job.payload_object.respond_to?(:job_data)
30
+ payload_data = construct_job_payload(job.payload_object)
31
+
32
+ context = get_context(payload_data, job_data[:active_job])
33
+ report.automatic_context = context unless context.nil?
34
+
35
+ job_data[:payload] = payload_data
36
+ end
37
+
38
+ if job.respond_to?(:attempts)
39
+ # +1 as "attempts" is zero-based and does not include the current failed attempt
40
+ job_data[:attempt] = job.attempts + 1
41
+ job_data[:max_attempts] = (job.respond_to?(:max_attempts) && job.max_attempts) || Delayed::Worker.max_attempts
42
+ end
43
+
44
+ report.add_tab(:job, job_data)
45
+ end
46
+ @bugsnag.call(report)
47
+ end
48
+
49
+ def construct_job_payload(payload)
50
+ data = {
51
+ :class => payload.class.name
52
+ }
53
+ data[:id] = payload.id if payload.respond_to?(:id)
54
+ data[:display_name] = payload.display_name if payload.respond_to?(:display_name)
55
+ data[:method_name] = payload.method_name if payload.respond_to?(:method_name)
56
+
57
+ if payload.respond_to?(:args)
58
+ data[:args] = payload.args
59
+ elsif payload.respond_to?(:to_h)
60
+ data[:args] = payload.to_h
61
+ elsif payload.respond_to?(:instance_values)
62
+ data[:args] = payload.instance_values
63
+ end
64
+
65
+ if payload.is_a?(::Delayed::PerformableMethod) && (object = payload.object)
66
+ data[:object] = {
67
+ :class => object.class.name
68
+ }
69
+ data[:object][:id] = object.id if object.respond_to?(:id)
70
+ end
71
+ if payload.respond_to?(:job_data) && payload.job_data.respond_to?(:[])
72
+ [:job_class, :arguments, :queue_name, :job_id].each do |key|
73
+ if (value = payload.job_data[key.to_s])
74
+ data[key] = value
75
+ end
76
+ end
77
+ end
78
+ data
79
+ end
80
+
81
+ private
82
+
83
+ def get_context(payload_data, active_job_data)
84
+ if payload_data.include?(:display_name) && !ACTIVE_JOB_DISPLAY_NAME.match?(payload_data[:display_name])
85
+ payload_data[:display_name]
86
+ elsif active_job_data && active_job_data['job_class'] && active_job_data['queue_name']
87
+ "#{active_job_data['job_class']}@#{active_job_data['queue_name']}"
88
+ elsif payload_data.include?(:class)
89
+ payload_data[:class]
90
+ end
91
+ end
92
+ end
93
+ end
@@ -0,0 +1,30 @@
1
+ module Bugsnag::Middleware
2
+ ##
3
+ # Determines if the exception should be ignored based on the configured
4
+ # `discard_classes`
5
+ class DiscardErrorClass
6
+ ##
7
+ # @param middleware [#call] The next middleware to call
8
+ def initialize(middleware)
9
+ @middleware = middleware
10
+ end
11
+
12
+ ##
13
+ # @param report [Report]
14
+ def call(report)
15
+ should_discard = report.raw_exceptions.any? do |ex|
16
+ report.configuration.discard_classes.any? do |to_ignore|
17
+ case to_ignore
18
+ when String then to_ignore == ex.class.name
19
+ when Regexp then to_ignore =~ ex.class.name
20
+ else false
21
+ end
22
+ end
23
+ end
24
+
25
+ report.ignore! if should_discard
26
+
27
+ @middleware.call(report)
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,42 @@
1
+ module Bugsnag::Middleware
2
+ ##
3
+ # Extracts data from the exception.
4
+ class ExceptionMetaData
5
+ def initialize(bugsnag)
6
+ @bugsnag = bugsnag
7
+ end
8
+
9
+ def call(report)
10
+ # Apply the user's information attached to the exceptions
11
+ report.raw_exceptions.each do |exception|
12
+ if exception.respond_to?(:bugsnag_user_id)
13
+ user_id = exception.bugsnag_user_id
14
+ report.user = {id: user_id} if user_id.is_a?(String)
15
+ end
16
+
17
+ if exception.respond_to?(:bugsnag_context)
18
+ context = exception.bugsnag_context
19
+ # note: this should set 'context' not 'automatic_context' as it's a
20
+ # user-supplied value
21
+ report.context = context if context.is_a?(String)
22
+ end
23
+
24
+ if exception.respond_to?(:bugsnag_grouping_hash)
25
+ group_hash = exception.bugsnag_grouping_hash
26
+ report.grouping_hash = group_hash if group_hash.is_a?(String)
27
+ end
28
+
29
+ if exception.respond_to?(:bugsnag_meta_data)
30
+ meta_data = exception.bugsnag_meta_data
31
+ if meta_data.is_a?(Hash)
32
+ meta_data.each do |key, value|
33
+ report.add_tab key, value
34
+ end
35
+ end
36
+ end
37
+ end
38
+
39
+ @bugsnag.call(report)
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,26 @@
1
+ module Bugsnag::Middleware
2
+ ##
3
+ # Determines if the exception should be ignored based on the configured
4
+ # `ignore_classes`
5
+ #
6
+ # @deprecated Use {DiscardErrorClass} instead
7
+ class IgnoreErrorClass
8
+ def initialize(bugsnag)
9
+ @bugsnag = bugsnag
10
+ end
11
+
12
+ def call(report)
13
+ ignore_error_class = report.raw_exceptions.any? do |ex|
14
+ ancestor_chain = ex.class.ancestors.select { |ancestor| ancestor.is_a?(Class) }.to_set
15
+
16
+ report.configuration.ignore_classes.any? do |to_ignore|
17
+ to_ignore.is_a?(Proc) ? to_ignore.call(ex) : ancestor_chain.include?(to_ignore)
18
+ end
19
+ end
20
+
21
+ report.ignore! if ignore_error_class
22
+
23
+ @bugsnag.call(report)
24
+ end
25
+ end
26
+ end
@@ -1,13 +1,15 @@
1
1
  module Bugsnag::Middleware
2
+ ##
3
+ # Extracts and attaches mailman data to an error report
2
4
  class Mailman
3
5
  def initialize(bugsnag)
4
6
  @bugsnag = bugsnag
5
7
  end
6
8
 
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)
9
+ def call(report)
10
+ mailman_msg = report.request_data[:mailman_msg]
11
+ report.add_tab(:mailman, {"message" => mailman_msg}) if mailman_msg
12
+ @bugsnag.call(report)
11
13
  end
12
14
  end
13
15
  end