bugsnag 6.19.0 → 6.26.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.yardopts +1 -0
- data/CHANGELOG.md +176 -0
- data/VERSION +1 -1
- data/bugsnag.gemspec +18 -1
- data/lib/bugsnag/breadcrumb_type.rb +14 -0
- data/lib/bugsnag/breadcrumbs/breadcrumb.rb +34 -1
- data/lib/bugsnag/breadcrumbs/breadcrumbs.rb +1 -0
- data/lib/bugsnag/breadcrumbs/on_breadcrumb_callback_list.rb +50 -0
- data/lib/bugsnag/cleaner.rb +31 -18
- data/lib/bugsnag/configuration.rb +243 -25
- data/lib/bugsnag/delivery/synchronous.rb +2 -2
- data/lib/bugsnag/delivery/thread_queue.rb +2 -2
- 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 +7 -0
- data/lib/bugsnag/feature_flag.rb +74 -0
- data/lib/bugsnag/integrations/mongo.rb +5 -3
- data/lib/bugsnag/integrations/rack.rb +3 -3
- data/lib/bugsnag/integrations/rails/active_job.rb +102 -0
- data/lib/bugsnag/integrations/rails/rails_breadcrumbs.rb +2 -0
- data/lib/bugsnag/integrations/railtie.rb +70 -27
- data/lib/bugsnag/integrations/resque.rb +17 -3
- data/lib/bugsnag/integrations/sidekiq.rb +1 -0
- data/lib/bugsnag/middleware/active_job.rb +18 -0
- data/lib/bugsnag/middleware/classify_error.rb +1 -0
- data/lib/bugsnag/middleware/delayed_job.rb +21 -2
- data/lib/bugsnag/middleware/exception_meta_data.rb +2 -0
- data/lib/bugsnag/middleware/rack_request.rb +84 -19
- data/lib/bugsnag/middleware/rails3_request.rb +2 -2
- data/lib/bugsnag/middleware/rake.rb +1 -1
- data/lib/bugsnag/middleware/session_data.rb +3 -1
- data/lib/bugsnag/middleware/sidekiq.rb +1 -1
- data/lib/bugsnag/middleware/suggestion_data.rb +9 -7
- data/lib/bugsnag/middleware_stack.rb +6 -6
- data/lib/bugsnag/report.rb +204 -8
- data/lib/bugsnag/session_tracker.rb +52 -12
- data/lib/bugsnag/stacktrace.rb +13 -2
- data/lib/bugsnag/tasks/bugsnag.rake +1 -1
- 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 +156 -8
- metadata +24 -7
@@ -0,0 +1,102 @@
|
|
1
|
+
require 'set'
|
2
|
+
|
3
|
+
module Bugsnag::Rails
|
4
|
+
module ActiveJob
|
5
|
+
SEVERITY = 'error'
|
6
|
+
SEVERITY_REASON = {
|
7
|
+
type: Bugsnag::Report::UNHANDLED_EXCEPTION_MIDDLEWARE,
|
8
|
+
attributes: { framework: 'Active Job' }
|
9
|
+
}
|
10
|
+
|
11
|
+
EXISTING_INTEGRATIONS = Set[
|
12
|
+
'ActiveJob::QueueAdapters::DelayedJobAdapter',
|
13
|
+
'ActiveJob::QueueAdapters::QueAdapter',
|
14
|
+
'ActiveJob::QueueAdapters::ResqueAdapter',
|
15
|
+
'ActiveJob::QueueAdapters::ShoryukenAdapter',
|
16
|
+
'ActiveJob::QueueAdapters::SidekiqAdapter'
|
17
|
+
]
|
18
|
+
|
19
|
+
INLINE_ADAPTER = 'ActiveJob::QueueAdapters::InlineAdapter'
|
20
|
+
|
21
|
+
# these methods were added after the first Active Job release so
|
22
|
+
# may not be present, depending on the Rails version
|
23
|
+
MAYBE_MISSING_METHODS = [
|
24
|
+
:provider_job_id,
|
25
|
+
:priority,
|
26
|
+
:executions,
|
27
|
+
:enqueued_at,
|
28
|
+
:timezone
|
29
|
+
]
|
30
|
+
|
31
|
+
def self.included(base)
|
32
|
+
base.class_eval do
|
33
|
+
around_perform do |job, block|
|
34
|
+
adapter = _bugsnag_get_adapter_name(job)
|
35
|
+
|
36
|
+
# if we have an integration for this queue adapter already then we should
|
37
|
+
# leave this job alone or we'll end up with duplicate metadata
|
38
|
+
next block.call if EXISTING_INTEGRATIONS.include?(adapter)
|
39
|
+
|
40
|
+
Bugsnag.configuration.detected_app_type = 'active job'
|
41
|
+
|
42
|
+
begin
|
43
|
+
Bugsnag.configuration.set_request_data(:active_job, _bugsnag_extract_metadata(job))
|
44
|
+
|
45
|
+
block.call
|
46
|
+
rescue Exception => e
|
47
|
+
Bugsnag.notify(e, true) do |report|
|
48
|
+
report.severity = SEVERITY
|
49
|
+
report.severity_reason = SEVERITY_REASON
|
50
|
+
end
|
51
|
+
|
52
|
+
# when using the "inline" adapter the job is run immediately, which
|
53
|
+
# will result in our Rack integration catching the re-raised error
|
54
|
+
# and reporting it a second time if it's run in a web request
|
55
|
+
if adapter == INLINE_ADAPTER
|
56
|
+
e.instance_eval do
|
57
|
+
def skip_bugsnag
|
58
|
+
true
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
raise
|
64
|
+
ensure
|
65
|
+
Bugsnag.configuration.clear_request_data
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
private
|
72
|
+
|
73
|
+
def _bugsnag_get_adapter_name(job)
|
74
|
+
adapter = job.class.queue_adapter
|
75
|
+
|
76
|
+
# in Rails 4 queue adapters were references to a class. In Rails 5+
|
77
|
+
# they are an instance of that class instead
|
78
|
+
return adapter.name if adapter.is_a?(Class)
|
79
|
+
|
80
|
+
adapter.class.name
|
81
|
+
end
|
82
|
+
|
83
|
+
def _bugsnag_extract_metadata(job)
|
84
|
+
metadata = {
|
85
|
+
job_id: job.job_id,
|
86
|
+
job_name: job.class.name,
|
87
|
+
queue: job.queue_name,
|
88
|
+
arguments: job.arguments,
|
89
|
+
locale: job.locale
|
90
|
+
}
|
91
|
+
|
92
|
+
MAYBE_MISSING_METHODS.each do |method_name|
|
93
|
+
next unless job.respond_to?(method_name)
|
94
|
+
|
95
|
+
metadata[method_name] = job.send(method_name)
|
96
|
+
end
|
97
|
+
|
98
|
+
metadata.compact!
|
99
|
+
metadata
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
@@ -38,6 +38,8 @@ module Bugsnag::Rails
|
|
38
38
|
:type => Bugsnag::Breadcrumbs::PROCESS_BREADCRUMB_TYPE,
|
39
39
|
:allowed_data => [
|
40
40
|
:name,
|
41
|
+
# :connection_id is no longer provided in Rails 6.1+ but we can get it
|
42
|
+
# from the :connection key of the event instead
|
41
43
|
:connection_id,
|
42
44
|
:cached
|
43
45
|
]
|
@@ -9,11 +9,67 @@ require "bugsnag/integrations/rails/rails_breadcrumbs"
|
|
9
9
|
|
10
10
|
module Bugsnag
|
11
11
|
class Railtie < ::Rails::Railtie
|
12
|
-
|
13
12
|
FRAMEWORK_ATTRIBUTES = {
|
14
13
|
:framework => "Rails"
|
15
14
|
}
|
16
15
|
|
16
|
+
##
|
17
|
+
# Subscribes to an ActiveSupport event, leaving a breadcrumb when it triggers
|
18
|
+
#
|
19
|
+
# @api private
|
20
|
+
# @param event [Hash] details of the event to subscribe to
|
21
|
+
def event_subscription(event)
|
22
|
+
ActiveSupport::Notifications.subscribe(event[:id]) do |*, event_id, data|
|
23
|
+
filtered_data = data.slice(*event[:allowed_data])
|
24
|
+
filtered_data[:event_name] = event[:id]
|
25
|
+
filtered_data[:event_id] = event_id
|
26
|
+
|
27
|
+
if event[:id] == "sql.active_record"
|
28
|
+
if data.key?(:binds)
|
29
|
+
binds = data[:binds].each_with_object({}) { |bind, output| output[bind.name] = '?' if defined?(bind.name) }
|
30
|
+
filtered_data[:binds] = JSON.dump(binds) unless binds.empty?
|
31
|
+
end
|
32
|
+
|
33
|
+
# Rails < 6.1 included connection_id in the event data, but now
|
34
|
+
# includes the connection object instead
|
35
|
+
if data.key?(:connection) && !data.key?(:connection_id)
|
36
|
+
# the connection ID is the object_id of the connection object
|
37
|
+
filtered_data[:connection_id] = data[:connection].object_id
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
Bugsnag.leave_breadcrumb(
|
42
|
+
event[:message],
|
43
|
+
filtered_data,
|
44
|
+
event[:type],
|
45
|
+
:auto
|
46
|
+
)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
##
|
51
|
+
# Do we need to rescue (& notify) in Active Record callbacks?
|
52
|
+
#
|
53
|
+
# On Rails versions < 4.2, Rails did not raise errors in AR callbacks
|
54
|
+
# On Rails version 4.2, a config option was added to control this
|
55
|
+
# On Rails version 5.0, the config option was removed and errors in callbacks
|
56
|
+
# always bubble up
|
57
|
+
#
|
58
|
+
# @api private
|
59
|
+
def self.rescue_in_active_record_callbacks?
|
60
|
+
# Rails 5+ will re-raise errors in callbacks, so we don't need to rescue them
|
61
|
+
return false if ::Rails::VERSION::MAJOR > 4
|
62
|
+
|
63
|
+
# before 4.2, errors were always swallowed, so we need to rescue them
|
64
|
+
return true if ::Rails::VERSION::MAJOR < 4
|
65
|
+
|
66
|
+
# a config option was added in 4.2 to control this, but won't exist in 4.0 & 4.1
|
67
|
+
return true unless ActiveRecord::Base.respond_to?(:raise_in_transactional_callbacks)
|
68
|
+
|
69
|
+
# if the config option is false, we need to rescue and notify
|
70
|
+
ActiveRecord::Base.raise_in_transactional_callbacks == false
|
71
|
+
end
|
72
|
+
|
17
73
|
rake_tasks do
|
18
74
|
require "bugsnag/integrations/rake"
|
19
75
|
load "bugsnag/tasks/bugsnag.rake"
|
@@ -27,7 +83,7 @@ module Bugsnag
|
|
27
83
|
config.logger = ::Rails.logger
|
28
84
|
config.release_stage ||= ::Rails.env.to_s
|
29
85
|
config.project_root = ::Rails.root.to_s
|
30
|
-
config.
|
86
|
+
config.internal_middleware.use(Bugsnag::Middleware::Rails3Request)
|
31
87
|
config.runtime_versions["rails"] = ::Rails::VERSION::STRING
|
32
88
|
end
|
33
89
|
|
@@ -37,8 +93,18 @@ module Bugsnag
|
|
37
93
|
end
|
38
94
|
|
39
95
|
ActiveSupport.on_load(:active_record) do
|
40
|
-
|
41
|
-
|
96
|
+
if Bugsnag::Railtie.rescue_in_active_record_callbacks?
|
97
|
+
require "bugsnag/integrations/rails/active_record_rescue"
|
98
|
+
include Bugsnag::Rails::ActiveRecordRescue
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
ActiveSupport.on_load(:active_job) do
|
103
|
+
require "bugsnag/middleware/active_job"
|
104
|
+
Bugsnag.configuration.internal_middleware.use(Bugsnag::Middleware::ActiveJob)
|
105
|
+
|
106
|
+
require "bugsnag/integrations/rails/active_job"
|
107
|
+
include Bugsnag::Rails::ActiveJob
|
42
108
|
end
|
43
109
|
|
44
110
|
Bugsnag::Rails::DEFAULT_RAILS_BREADCRUMBS.each { |event| event_subscription(event) }
|
@@ -80,28 +146,5 @@ module Bugsnag
|
|
80
146
|
Bugsnag.configuration.warn("Unable to add Bugsnag::Rack middleware as the middleware stack is frozen")
|
81
147
|
end
|
82
148
|
end
|
83
|
-
|
84
|
-
##
|
85
|
-
# Subscribes to an ActiveSupport event, leaving a breadcrumb when it triggers
|
86
|
-
#
|
87
|
-
# @api private
|
88
|
-
# @param event [Hash] details of the event to subscribe to
|
89
|
-
def event_subscription(event)
|
90
|
-
ActiveSupport::Notifications.subscribe(event[:id]) do |*, event_id, data|
|
91
|
-
filtered_data = data.slice(*event[:allowed_data])
|
92
|
-
filtered_data[:event_name] = event[:id]
|
93
|
-
filtered_data[:event_id] = event_id
|
94
|
-
if event[:id] == "sql.active_record" && data.key?(:binds)
|
95
|
-
binds = data[:binds].each_with_object({}) { |bind, output| output[bind.name] = '?' if defined?(bind.name) }
|
96
|
-
filtered_data[:binds] = JSON.dump(binds) unless binds.empty?
|
97
|
-
end
|
98
|
-
Bugsnag.leave_breadcrumb(
|
99
|
-
event[:message],
|
100
|
-
filtered_data,
|
101
|
-
event[:type],
|
102
|
-
:auto
|
103
|
-
)
|
104
|
-
end
|
105
|
-
end
|
106
149
|
end
|
107
150
|
end
|
@@ -44,9 +44,23 @@ module Bugsnag
|
|
44
44
|
:attributes => FRAMEWORK_ATTRIBUTES
|
45
45
|
}
|
46
46
|
|
47
|
-
|
48
|
-
|
49
|
-
|
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
|
50
64
|
end
|
51
65
|
end
|
52
66
|
end
|
@@ -4,6 +4,7 @@ module Bugsnag
|
|
4
4
|
##
|
5
5
|
# Extracts and attaches Sidekiq job and queue information to an error report
|
6
6
|
class Sidekiq
|
7
|
+
include ::Sidekiq::ServerMiddleware if defined?(::Sidekiq::ServerMiddleware)
|
7
8
|
|
8
9
|
unless const_defined?(:FRAMEWORK_ATTRIBUTES)
|
9
10
|
FRAMEWORK_ATTRIBUTES = {
|
@@ -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
|
@@ -9,6 +9,7 @@ module Bugsnag::Middleware
|
|
9
9
|
"ActionController::UnknownAction",
|
10
10
|
"ActionController::UnknownFormat",
|
11
11
|
"ActionController::UnknownHttpMethod",
|
12
|
+
"ActionDispatch::Http::MimeNegotiation::InvalidType",
|
12
13
|
"ActiveRecord::RecordNotFound",
|
13
14
|
"CGI::Session::CookieStore::TamperedWithCookie",
|
14
15
|
"Mongoid::Errors::DocumentNotFound",
|
@@ -2,6 +2,11 @@ module Bugsnag::Middleware
|
|
2
2
|
##
|
3
3
|
# Attaches delayed_job information to an error report
|
4
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
|
+
|
5
10
|
def initialize(bugsnag)
|
6
11
|
@bugsnag = bugsnag
|
7
12
|
end
|
@@ -23,8 +28,10 @@ module Bugsnag::Middleware
|
|
23
28
|
if job.respond_to?(:payload_object)
|
24
29
|
job_data[:active_job] = job.payload_object.job_data if job.payload_object.respond_to?(:job_data)
|
25
30
|
payload_data = construct_job_payload(job.payload_object)
|
26
|
-
|
27
|
-
|
31
|
+
|
32
|
+
context = get_context(payload_data, job_data[:active_job])
|
33
|
+
report.automatic_context = context unless context.nil?
|
34
|
+
|
28
35
|
job_data[:payload] = payload_data
|
29
36
|
end
|
30
37
|
|
@@ -70,5 +77,17 @@ module Bugsnag::Middleware
|
|
70
77
|
end
|
71
78
|
data
|
72
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
|
73
92
|
end
|
74
93
|
end
|
@@ -16,6 +16,8 @@ module Bugsnag::Middleware
|
|
16
16
|
|
17
17
|
if exception.respond_to?(:bugsnag_context)
|
18
18
|
context = exception.bugsnag_context
|
19
|
+
# note: this should set 'context' not 'automatic_context' as it's a
|
20
|
+
# user-supplied value
|
19
21
|
report.context = context if context.is_a?(String)
|
20
22
|
end
|
21
23
|
|
@@ -1,8 +1,11 @@
|
|
1
|
+
require "json"
|
2
|
+
|
1
3
|
module Bugsnag::Middleware
|
2
4
|
##
|
3
5
|
# Extracts and attaches rack data to an error report
|
4
6
|
class RackRequest
|
5
7
|
SPOOF = "[SPOOF]".freeze
|
8
|
+
COOKIE_HEADER = "Cookie".freeze
|
6
9
|
|
7
10
|
def initialize(bugsnag)
|
8
11
|
@bugsnag = bugsnag
|
@@ -18,8 +21,8 @@ module Bugsnag::Middleware
|
|
18
21
|
client_ip = request.ip.to_s rescue SPOOF
|
19
22
|
session = env["rack.session"]
|
20
23
|
|
21
|
-
# Set the context
|
22
|
-
report.
|
24
|
+
# Set the automatic context
|
25
|
+
report.automatic_context = "#{request.request_method} #{request.path}"
|
23
26
|
|
24
27
|
# Set a sensible default for user_id
|
25
28
|
report.user["id"] = request.ip
|
@@ -42,22 +45,6 @@ module Bugsnag::Middleware
|
|
42
45
|
Bugsnag.configuration.warn "RackRequest - Rescued error while cleaning request.referer: #{stde}"
|
43
46
|
end
|
44
47
|
|
45
|
-
headers = {}
|
46
|
-
|
47
|
-
env.each_pair do |key, value|
|
48
|
-
if key.to_s.start_with?("HTTP_")
|
49
|
-
header_key = key[5..-1]
|
50
|
-
elsif ["CONTENT_TYPE", "CONTENT_LENGTH"].include?(key)
|
51
|
-
header_key = key
|
52
|
-
else
|
53
|
-
next
|
54
|
-
end
|
55
|
-
|
56
|
-
headers[header_key.split("_").map {|s| s.capitalize}.join("-")] = value
|
57
|
-
end
|
58
|
-
|
59
|
-
headers["Referer"] = referer if headers["Referer"]
|
60
|
-
|
61
48
|
# Add a request tab
|
62
49
|
report.add_tab(:request, {
|
63
50
|
:url => url,
|
@@ -65,9 +52,17 @@ module Bugsnag::Middleware
|
|
65
52
|
:params => params.to_hash,
|
66
53
|
:referer => referer,
|
67
54
|
:clientIp => client_ip,
|
68
|
-
:headers =>
|
55
|
+
:headers => format_headers(env, referer)
|
69
56
|
})
|
70
57
|
|
58
|
+
# add the HTTP version if present
|
59
|
+
if env["SERVER_PROTOCOL"]
|
60
|
+
report.add_metadata(:request, :httpVersion, env["SERVER_PROTOCOL"])
|
61
|
+
end
|
62
|
+
|
63
|
+
add_request_body(report, request, env)
|
64
|
+
add_cookies(report, request)
|
65
|
+
|
71
66
|
# Add an environment tab
|
72
67
|
if report.configuration.send_environment
|
73
68
|
report.add_tab(:environment, env)
|
@@ -87,5 +82,75 @@ module Bugsnag::Middleware
|
|
87
82
|
|
88
83
|
@bugsnag.call(report)
|
89
84
|
end
|
85
|
+
|
86
|
+
private
|
87
|
+
|
88
|
+
def format_headers(env, referer)
|
89
|
+
headers = {}
|
90
|
+
|
91
|
+
env.each_pair do |key, value|
|
92
|
+
if key.to_s.start_with?("HTTP_")
|
93
|
+
header_key = key[5..-1]
|
94
|
+
elsif ["CONTENT_TYPE", "CONTENT_LENGTH"].include?(key)
|
95
|
+
header_key = key
|
96
|
+
else
|
97
|
+
next
|
98
|
+
end
|
99
|
+
|
100
|
+
headers[header_key.split("_").map {|s| s.capitalize}.join("-")] = value
|
101
|
+
end
|
102
|
+
|
103
|
+
headers["Referer"] = referer if headers["Referer"]
|
104
|
+
|
105
|
+
headers
|
106
|
+
end
|
107
|
+
|
108
|
+
def add_request_body(report, request, env)
|
109
|
+
body = parsed_request_body(request, env)
|
110
|
+
|
111
|
+
# this request may not have a body
|
112
|
+
return unless body.is_a?(Hash) && !body.empty?
|
113
|
+
|
114
|
+
report.add_metadata(:request, :body, body)
|
115
|
+
end
|
116
|
+
|
117
|
+
def parsed_request_body(request, env)
|
118
|
+
return request.POST rescue nil if request.form_data?
|
119
|
+
|
120
|
+
content_type = env["CONTENT_TYPE"]
|
121
|
+
|
122
|
+
return nil if content_type.nil?
|
123
|
+
|
124
|
+
if content_type.include?('/json') || content_type.include?('+json')
|
125
|
+
begin
|
126
|
+
body = request.body
|
127
|
+
|
128
|
+
return JSON.parse(body.read)
|
129
|
+
rescue StandardError
|
130
|
+
return nil
|
131
|
+
ensure
|
132
|
+
# the body must be rewound so other things can read it after we do
|
133
|
+
body.rewind
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
nil
|
138
|
+
end
|
139
|
+
|
140
|
+
def add_cookies(report, request)
|
141
|
+
return unless record_cookies?
|
142
|
+
|
143
|
+
cookies = request.cookies rescue nil
|
144
|
+
|
145
|
+
return unless cookies.is_a?(Hash) && !cookies.empty?
|
146
|
+
|
147
|
+
report.add_metadata(:request, :cookies, cookies)
|
148
|
+
end
|
149
|
+
|
150
|
+
def record_cookies?
|
151
|
+
# only record cookies in the request if none of the filters match "Cookie"
|
152
|
+
# the "Cookie" header will be filtered as normal
|
153
|
+
!Bugsnag.cleaner.filters_match?(COOKIE_HEADER)
|
154
|
+
end
|
90
155
|
end
|
91
156
|
end
|
@@ -15,8 +15,8 @@ module Bugsnag::Middleware
|
|
15
15
|
client_ip = env["action_dispatch.remote_ip"].to_s rescue SPOOF
|
16
16
|
|
17
17
|
if params
|
18
|
-
# Set the context
|
19
|
-
report.
|
18
|
+
# Set the automatic context
|
19
|
+
report.automatic_context = "#{params[:controller]}##{params[:action]}"
|
20
20
|
|
21
21
|
# Augment the request tab
|
22
22
|
report.add_tab(:request, {
|
@@ -8,12 +8,14 @@ module Bugsnag::Middleware
|
|
8
8
|
|
9
9
|
def call(report)
|
10
10
|
session = Bugsnag::SessionTracker.get_current_session
|
11
|
-
|
11
|
+
|
12
|
+
if session && !session[:paused?]
|
12
13
|
if report.unhandled
|
13
14
|
session[:events][:unhandled] += 1
|
14
15
|
else
|
15
16
|
session[:events][:handled] += 1
|
16
17
|
end
|
18
|
+
|
17
19
|
report.session = session
|
18
20
|
end
|
19
21
|
|
@@ -10,7 +10,7 @@ module Bugsnag::Middleware
|
|
10
10
|
sidekiq = report.request_data[:sidekiq]
|
11
11
|
if sidekiq
|
12
12
|
report.add_tab(:sidekiq, sidekiq)
|
13
|
-
report.
|
13
|
+
report.automatic_context ||= "#{sidekiq[:msg]['wrapped'] || sidekiq[:msg]['class']}@#{sidekiq[:msg]['queue']}"
|
14
14
|
end
|
15
15
|
@bugsnag.call(report)
|
16
16
|
end
|
@@ -10,23 +10,25 @@ module Bugsnag::Middleware
|
|
10
10
|
@bugsnag = bugsnag
|
11
11
|
end
|
12
12
|
|
13
|
-
def call(
|
13
|
+
def call(event)
|
14
14
|
matches = []
|
15
|
-
|
16
|
-
|
15
|
+
|
16
|
+
event.errors.each do |error|
|
17
|
+
match = CAPTURE_REGEX.match(error.error_message)
|
18
|
+
|
17
19
|
next unless match
|
18
20
|
|
19
21
|
suggestions = match.captures[0].split(DELIMITER)
|
20
|
-
matches.concat
|
22
|
+
matches.concat(suggestions.map(&:strip))
|
21
23
|
end
|
22
24
|
|
23
25
|
if matches.size == 1
|
24
|
-
|
26
|
+
event.add_metadata(:error, { suggestion: matches.first })
|
25
27
|
elsif matches.size > 1
|
26
|
-
|
28
|
+
event.add_metadata(:error, { suggestions: matches })
|
27
29
|
end
|
28
30
|
|
29
|
-
@bugsnag.call(
|
31
|
+
@bugsnag.call(event)
|
30
32
|
end
|
31
33
|
end
|
32
34
|
end
|
@@ -131,8 +131,8 @@ module Bugsnag
|
|
131
131
|
#
|
132
132
|
# @return [Array<Proc>]
|
133
133
|
def middleware_procs
|
134
|
-
# Split the middleware into separate lists of
|
135
|
-
|
134
|
+
# Split the middleware into separate lists of callables (e.g. Proc, Lambda, Method) and Classes
|
135
|
+
callables, classes = @middlewares.partition {|middleware| middleware.respond_to?(:call) }
|
136
136
|
|
137
137
|
# Wrap the classes in a proc that, when called, news up the middleware and
|
138
138
|
# passes the next middleware in the queue
|
@@ -140,12 +140,12 @@ module Bugsnag
|
|
140
140
|
proc {|next_middleware| middleware.new(next_middleware) }
|
141
141
|
end
|
142
142
|
|
143
|
-
# Wrap the list of
|
143
|
+
# Wrap the list of callables in a proc that, when called, wraps them in an
|
144
144
|
# 'OnErrorCallbacks' instance that also has a reference to the next middleware
|
145
|
-
|
145
|
+
wrapped_callables = proc {|next_middleware| OnErrorCallbacks.new(next_middleware, callables) }
|
146
146
|
|
147
|
-
# Return the combined middleware and wrapped
|
148
|
-
middleware_instances.push(
|
147
|
+
# Return the combined middleware and wrapped callables
|
148
|
+
middleware_instances.push(wrapped_callables)
|
149
149
|
end
|
150
150
|
end
|
151
151
|
end
|