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.
- checksums.yaml +5 -5
- data/.yardopts +12 -0
- data/CHANGELOG.md +814 -0
- data/README.md +21 -25
- data/VERSION +1 -1
- data/bugsnag.gemspec +19 -8
- data/lib/bugsnag/breadcrumb_type.rb +14 -0
- data/lib/bugsnag/breadcrumbs/breadcrumb.rb +109 -0
- data/lib/bugsnag/breadcrumbs/breadcrumbs.rb +13 -0
- data/lib/bugsnag/breadcrumbs/on_breadcrumb_callback_list.rb +48 -0
- data/lib/bugsnag/breadcrumbs/validator.rb +29 -0
- data/lib/bugsnag/cleaner.rb +170 -59
- data/lib/bugsnag/code_extractor.rb +137 -0
- data/lib/bugsnag/configuration.rb +670 -45
- data/lib/bugsnag/delivery/synchronous.rb +31 -14
- data/lib/bugsnag/delivery/thread_queue.rb +23 -6
- data/lib/bugsnag/delivery.rb +13 -0
- data/lib/bugsnag/endpoint_configuration.rb +11 -0
- data/lib/bugsnag/endpoint_validator.rb +80 -0
- data/lib/bugsnag/error.rb +25 -0
- data/lib/bugsnag/event.rb +5 -0
- data/lib/bugsnag/feature_flag.rb +74 -0
- data/lib/bugsnag/helpers.rb +121 -25
- data/lib/bugsnag/integrations/delayed_job.rb +51 -0
- data/lib/bugsnag/integrations/mailman.rb +43 -0
- data/lib/bugsnag/integrations/mongo.rb +133 -0
- data/lib/bugsnag/integrations/que.rb +53 -0
- data/lib/bugsnag/integrations/rack.rb +83 -0
- data/lib/bugsnag/integrations/rails/active_job.rb +100 -0
- data/lib/bugsnag/{rails → integrations/rails}/active_record_rescue.rb +10 -1
- data/lib/bugsnag/{rails → integrations/rails}/controller_methods.rb +1 -9
- data/lib/bugsnag/integrations/rails/rails_breadcrumbs.rb +115 -0
- data/lib/bugsnag/integrations/railtie.rb +153 -0
- data/lib/bugsnag/integrations/rake.rb +74 -0
- data/lib/bugsnag/integrations/resque.rb +94 -0
- data/lib/bugsnag/integrations/shoryuken.rb +50 -0
- data/lib/bugsnag/integrations/sidekiq.rb +68 -0
- data/lib/bugsnag/meta_data.rb +1 -0
- data/lib/bugsnag/middleware/active_job.rb +18 -0
- data/lib/bugsnag/middleware/breadcrumbs.rb +21 -0
- data/lib/bugsnag/middleware/callbacks.rb +6 -8
- data/lib/bugsnag/middleware/classify_error.rb +50 -0
- data/lib/bugsnag/middleware/clearance_user.rb +33 -0
- data/lib/bugsnag/middleware/delayed_job.rb +93 -0
- data/lib/bugsnag/middleware/discard_error_class.rb +30 -0
- data/lib/bugsnag/middleware/exception_meta_data.rb +42 -0
- data/lib/bugsnag/middleware/ignore_error_class.rb +26 -0
- data/lib/bugsnag/middleware/mailman.rb +6 -4
- data/lib/bugsnag/middleware/rack_request.rb +126 -30
- data/lib/bugsnag/middleware/rails3_request.rb +15 -17
- data/lib/bugsnag/middleware/rake.rb +7 -5
- data/lib/bugsnag/middleware/session_data.rb +25 -0
- data/lib/bugsnag/middleware/sidekiq.rb +9 -4
- data/lib/bugsnag/middleware/suggestion_data.rb +34 -0
- data/lib/bugsnag/middleware/warden_user.rb +11 -6
- data/lib/bugsnag/middleware_stack.rb +62 -9
- data/lib/bugsnag/on_error_callbacks.rb +33 -0
- data/lib/bugsnag/report.rb +516 -0
- data/lib/bugsnag/session_tracker.rb +182 -0
- data/lib/bugsnag/stacktrace.rb +82 -0
- data/lib/bugsnag/tasks/bugsnag.rake +2 -70
- data/lib/bugsnag/utility/circular_buffer.rb +62 -0
- data/lib/bugsnag/utility/duplicator.rb +124 -0
- data/lib/bugsnag/utility/feature_data_store.rb +41 -0
- data/lib/bugsnag/utility/feature_flag_delegate.rb +89 -0
- data/lib/bugsnag/utility/metadata_delegate.rb +102 -0
- data/lib/bugsnag.rb +528 -80
- metadata +61 -123
- data/.document +0 -5
- data/.gitignore +0 -52
- data/.rspec +0 -3
- data/.travis.yml +0 -14
- data/CONTRIBUTING.md +0 -47
- data/Gemfile +0 -2
- data/Rakefile +0 -29
- data/lib/bugsnag/capistrano.rb +0 -7
- data/lib/bugsnag/capistrano2.rb +0 -32
- data/lib/bugsnag/delay/resque.rb +0 -21
- data/lib/bugsnag/delayed_job.rb +0 -57
- data/lib/bugsnag/deploy.rb +0 -34
- data/lib/bugsnag/mailman.rb +0 -28
- data/lib/bugsnag/middleware/rails2_request.rb +0 -52
- data/lib/bugsnag/notification.rb +0 -459
- data/lib/bugsnag/rack.rb +0 -53
- data/lib/bugsnag/rails/action_controller_rescue.rb +0 -62
- data/lib/bugsnag/rails.rb +0 -66
- data/lib/bugsnag/railtie.rb +0 -80
- data/lib/bugsnag/rake.rb +0 -25
- data/lib/bugsnag/resque.rb +0 -40
- data/lib/bugsnag/sidekiq.rb +0 -42
- data/lib/bugsnag/tasks/bugsnag.cap +0 -48
- data/rails/init.rb +0 -7
- data/spec/cleaner_spec.rb +0 -138
- data/spec/code_spec.rb +0 -86
- data/spec/fixtures/crashes/end_of_file.rb +0 -9
- data/spec/fixtures/crashes/short_file.rb +0 -1
- data/spec/fixtures/crashes/start_of_file.rb +0 -9
- data/spec/fixtures/middleware/internal_info_setter.rb +0 -11
- data/spec/fixtures/middleware/public_info_setter.rb +0 -11
- data/spec/fixtures/tasks/Rakefile +0 -15
- data/spec/helper_spec.rb +0 -163
- data/spec/integration_spec.rb +0 -132
- data/spec/middleware_spec.rb +0 -181
- data/spec/notification_spec.rb +0 -877
- data/spec/rack_spec.rb +0 -56
- 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
|
data/lib/bugsnag/meta_data.rb
CHANGED
@@ -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(
|
8
|
-
if
|
9
|
-
|
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(
|
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(
|
8
|
-
mailman_msg =
|
9
|
-
|
10
|
-
@bugsnag.call(
|
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
|