bugsnag 6.19.0 → 6.23.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +99 -0
- data/VERSION +1 -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/configuration.rb +118 -8
- data/lib/bugsnag/delivery/synchronous.rb +2 -2
- data/lib/bugsnag/delivery/thread_queue.rb +2 -2
- data/lib/bugsnag/error.rb +25 -0
- data/lib/bugsnag/event.rb +7 -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 +43 -25
- data/lib/bugsnag/integrations/resque.rb +13 -3
- 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 +2 -2
- data/lib/bugsnag/middleware/rails3_request.rb +2 -2
- data/lib/bugsnag/middleware/rake.rb +1 -1
- data/lib/bugsnag/middleware/sidekiq.rb +1 -1
- data/lib/bugsnag/middleware_stack.rb +6 -6
- data/lib/bugsnag/report.rb +129 -5
- data/lib/bugsnag/session_tracker.rb +3 -7
- data/lib/bugsnag/tasks/bugsnag.rake +1 -1
- data/lib/bugsnag/utility/metadata_delegate.rb +102 -0
- data/lib/bugsnag.rb +61 -5
- metadata +10 -3
@@ -9,11 +9,44 @@ 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
|
+
|
17
50
|
rake_tasks do
|
18
51
|
require "bugsnag/integrations/rake"
|
19
52
|
load "bugsnag/tasks/bugsnag.rake"
|
@@ -27,7 +60,7 @@ module Bugsnag
|
|
27
60
|
config.logger = ::Rails.logger
|
28
61
|
config.release_stage ||= ::Rails.env.to_s
|
29
62
|
config.project_root = ::Rails.root.to_s
|
30
|
-
config.
|
63
|
+
config.internal_middleware.use(Bugsnag::Middleware::Rails3Request)
|
31
64
|
config.runtime_versions["rails"] = ::Rails::VERSION::STRING
|
32
65
|
end
|
33
66
|
|
@@ -41,6 +74,14 @@ module Bugsnag
|
|
41
74
|
include Bugsnag::Rails::ActiveRecordRescue
|
42
75
|
end
|
43
76
|
|
77
|
+
ActiveSupport.on_load(:active_job) do
|
78
|
+
require "bugsnag/middleware/active_job"
|
79
|
+
Bugsnag.configuration.internal_middleware.use(Bugsnag::Middleware::ActiveJob)
|
80
|
+
|
81
|
+
require "bugsnag/integrations/rails/active_job"
|
82
|
+
include Bugsnag::Rails::ActiveJob
|
83
|
+
end
|
84
|
+
|
44
85
|
Bugsnag::Rails::DEFAULT_RAILS_BREADCRUMBS.each { |event| event_subscription(event) }
|
45
86
|
|
46
87
|
# Make sure we don't overwrite the value set by another integration because
|
@@ -80,28 +121,5 @@ module Bugsnag
|
|
80
121
|
Bugsnag.configuration.warn("Unable to add Bugsnag::Rack middleware as the middleware stack is frozen")
|
81
122
|
end
|
82
123
|
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
124
|
end
|
107
125
|
end
|
@@ -44,9 +44,19 @@ module Bugsnag
|
|
44
44
|
:attributes => FRAMEWORK_ATTRIBUTES
|
45
45
|
}
|
46
46
|
|
47
|
-
|
48
|
-
|
49
|
-
|
47
|
+
metadata = payload
|
48
|
+
class_name = payload['class']
|
49
|
+
|
50
|
+
# when using Active Job the payload "class" will always be the Resque
|
51
|
+
# "JobWrapper", not the actual job class so we need to fix this here
|
52
|
+
if metadata['args'] && metadata['args'][0] && metadata['args'][0]['job_class']
|
53
|
+
class_name = metadata['args'][0]['job_class']
|
54
|
+
metadata['wrapped'] ||= class_name
|
55
|
+
end
|
56
|
+
|
57
|
+
context = "#{class_name}@#{queue}"
|
58
|
+
report.meta_data.merge!({ context: context, payload: metadata })
|
59
|
+
report.automatic_context = context
|
50
60
|
end
|
51
61
|
end
|
52
62
|
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
|
@@ -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
|
|
@@ -18,8 +18,8 @@ module Bugsnag::Middleware
|
|
18
18
|
client_ip = request.ip.to_s rescue SPOOF
|
19
19
|
session = env["rack.session"]
|
20
20
|
|
21
|
-
# Set the context
|
22
|
-
report.
|
21
|
+
# Set the automatic context
|
22
|
+
report.automatic_context = "#{request.request_method} #{request.path}"
|
23
23
|
|
24
24
|
# Set a sensible default for user_id
|
25
25
|
report.user["id"] = request.ip
|
@@ -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, {
|
@@ -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
|
@@ -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
|
data/lib/bugsnag/report.rb
CHANGED
@@ -1,8 +1,10 @@
|
|
1
1
|
require "json"
|
2
2
|
require "pathname"
|
3
|
+
require "bugsnag/error"
|
3
4
|
require "bugsnag/stacktrace"
|
4
5
|
|
5
6
|
module Bugsnag
|
7
|
+
# rubocop:todo Metrics/ClassLength
|
6
8
|
class Report
|
7
9
|
NOTIFIER_NAME = "Ruby Bugsnag Notifier"
|
8
10
|
NOTIFIER_VERSION = Bugsnag::VERSION
|
@@ -45,16 +47,13 @@ module Bugsnag
|
|
45
47
|
# @return [Configuration]
|
46
48
|
attr_accessor :configuration
|
47
49
|
|
48
|
-
# Additional context for this report
|
49
|
-
# @return [String, nil]
|
50
|
-
attr_accessor :context
|
51
|
-
|
52
50
|
# The delivery method that will be used for this report
|
53
51
|
# @see Configuration#delivery_method
|
54
52
|
# @return [Symbol]
|
55
53
|
attr_accessor :delivery_method
|
56
54
|
|
57
55
|
# The list of exceptions in this report
|
56
|
+
# @deprecated Use {#errors} instead
|
58
57
|
# @return [Array<Hash>]
|
59
58
|
attr_accessor :exceptions
|
60
59
|
|
@@ -72,10 +71,12 @@ module Bugsnag
|
|
72
71
|
attr_accessor :grouping_hash
|
73
72
|
|
74
73
|
# Arbitrary metadata attached to this report
|
74
|
+
# @deprecated Use {#metadata} instead
|
75
75
|
# @return [Hash]
|
76
76
|
attr_accessor :meta_data
|
77
77
|
|
78
78
|
# The raw Exception instances for this report
|
79
|
+
# @deprecated Use {#original_error} instead
|
79
80
|
# @see #exceptions
|
80
81
|
# @return [Array<Exception>]
|
81
82
|
attr_accessor :raw_exceptions
|
@@ -102,21 +103,35 @@ module Bugsnag
|
|
102
103
|
# @return [Hash]
|
103
104
|
attr_accessor :user
|
104
105
|
|
106
|
+
# A list of errors in this report
|
107
|
+
# @return [Array<Error>]
|
108
|
+
attr_reader :errors
|
109
|
+
|
110
|
+
# The Exception instance this report was created for
|
111
|
+
# @return [Exception]
|
112
|
+
attr_reader :original_error
|
113
|
+
|
105
114
|
##
|
106
115
|
# Initializes a new report from an exception.
|
107
116
|
def initialize(exception, passed_configuration, auto_notify=false)
|
117
|
+
# store the creation time for use as device.time
|
118
|
+
@created_at = Time.now.utc.iso8601(3)
|
119
|
+
|
108
120
|
@should_ignore = false
|
109
121
|
@unhandled = auto_notify
|
110
122
|
|
111
123
|
self.configuration = passed_configuration
|
112
124
|
|
125
|
+
@original_error = exception
|
113
126
|
self.raw_exceptions = generate_raw_exceptions(exception)
|
114
127
|
self.exceptions = generate_exception_list
|
128
|
+
@errors = generate_error_list
|
115
129
|
|
116
130
|
self.api_key = configuration.api_key
|
117
131
|
self.app_type = configuration.app_type
|
118
132
|
self.app_version = configuration.app_version
|
119
133
|
self.breadcrumbs = []
|
134
|
+
self.context = configuration.context if configuration.context_set?
|
120
135
|
self.delivery_method = configuration.delivery_method
|
121
136
|
self.hostname = configuration.hostname
|
122
137
|
self.runtime_versions = configuration.runtime_versions.dup
|
@@ -125,8 +140,29 @@ module Bugsnag
|
|
125
140
|
self.severity = auto_notify ? "error" : "warning"
|
126
141
|
self.severity_reason = auto_notify ? {:type => UNHANDLED_EXCEPTION} : {:type => HANDLED_EXCEPTION}
|
127
142
|
self.user = {}
|
143
|
+
|
144
|
+
@metadata_delegate = Utility::MetadataDelegate.new
|
128
145
|
end
|
129
146
|
|
147
|
+
##
|
148
|
+
# Additional context for this report
|
149
|
+
# @!attribute context
|
150
|
+
# @return [String, nil]
|
151
|
+
def context
|
152
|
+
return @context if defined?(@context)
|
153
|
+
|
154
|
+
@automatic_context
|
155
|
+
end
|
156
|
+
|
157
|
+
attr_writer :context
|
158
|
+
|
159
|
+
##
|
160
|
+
# Context set automatically by Bugsnag uses this attribute, which prevents
|
161
|
+
# it from overwriting the user-supplied context
|
162
|
+
# @api private
|
163
|
+
# @return [String, nil]
|
164
|
+
attr_accessor :automatic_context
|
165
|
+
|
130
166
|
##
|
131
167
|
# Add a new metadata tab to this notification.
|
132
168
|
#
|
@@ -135,6 +171,8 @@ module Bugsnag
|
|
135
171
|
# exists, this will be merged with the existing values. If a Hash is not
|
136
172
|
# given, the value will be placed into the 'custom' tab
|
137
173
|
# @return [void]
|
174
|
+
#
|
175
|
+
# @deprecated Use {#add_metadata} instead
|
138
176
|
def add_tab(name, value)
|
139
177
|
return if name.nil?
|
140
178
|
|
@@ -153,6 +191,8 @@ module Bugsnag
|
|
153
191
|
#
|
154
192
|
# @param name [String]
|
155
193
|
# @return [void]
|
194
|
+
#
|
195
|
+
# @deprecated Use {#clear_metadata} instead
|
156
196
|
def remove_tab(name)
|
157
197
|
return if name.nil?
|
158
198
|
|
@@ -175,7 +215,8 @@ module Bugsnag
|
|
175
215
|
context: context,
|
176
216
|
device: {
|
177
217
|
hostname: hostname,
|
178
|
-
runtimeVersions: runtime_versions
|
218
|
+
runtimeVersions: runtime_versions,
|
219
|
+
time: @created_at
|
179
220
|
},
|
180
221
|
exceptions: exceptions,
|
181
222
|
groupingHash: grouping_hash,
|
@@ -257,6 +298,82 @@ module Bugsnag
|
|
257
298
|
end
|
258
299
|
end
|
259
300
|
|
301
|
+
# A Hash containing arbitrary metadata
|
302
|
+
# @!attribute metadata
|
303
|
+
# @return [Hash]
|
304
|
+
def metadata
|
305
|
+
@meta_data
|
306
|
+
end
|
307
|
+
|
308
|
+
# @param metadata [Hash]
|
309
|
+
# @return [void]
|
310
|
+
def metadata=(metadata)
|
311
|
+
@meta_data = metadata
|
312
|
+
end
|
313
|
+
|
314
|
+
##
|
315
|
+
# Data from the current HTTP request. May be nil if no data has been recorded
|
316
|
+
#
|
317
|
+
# @return [Hash, nil]
|
318
|
+
def request
|
319
|
+
@meta_data[:request]
|
320
|
+
end
|
321
|
+
|
322
|
+
##
|
323
|
+
# Add values to metadata
|
324
|
+
#
|
325
|
+
# @overload add_metadata(section, data)
|
326
|
+
# Merges data into the given section of metadata
|
327
|
+
# @param section [String, Symbol]
|
328
|
+
# @param data [Hash]
|
329
|
+
#
|
330
|
+
# @overload add_metadata(section, key, value)
|
331
|
+
# Sets key to value in the given section of metadata. If the value is nil
|
332
|
+
# the key will be deleted
|
333
|
+
# @param section [String, Symbol]
|
334
|
+
# @param key [String, Symbol]
|
335
|
+
# @param value
|
336
|
+
#
|
337
|
+
# @return [void]
|
338
|
+
def add_metadata(section, key_or_data, *args)
|
339
|
+
@metadata_delegate.add_metadata(@meta_data, section, key_or_data, *args)
|
340
|
+
end
|
341
|
+
|
342
|
+
##
|
343
|
+
# Clear values from metadata
|
344
|
+
#
|
345
|
+
# @overload clear_metadata(section)
|
346
|
+
# Clears the given section of metadata
|
347
|
+
# @param section [String, Symbol]
|
348
|
+
#
|
349
|
+
# @overload clear_metadata(section, key)
|
350
|
+
# Clears the key in the given section of metadata
|
351
|
+
# @param section [String, Symbol]
|
352
|
+
# @param key [String, Symbol]
|
353
|
+
#
|
354
|
+
# @return [void]
|
355
|
+
def clear_metadata(section, *args)
|
356
|
+
@metadata_delegate.clear_metadata(@meta_data, section, *args)
|
357
|
+
end
|
358
|
+
|
359
|
+
##
|
360
|
+
# Set information about the current user
|
361
|
+
#
|
362
|
+
# Additional user fields can be added as metadata in a "user" section
|
363
|
+
#
|
364
|
+
# Setting a field to 'nil' will remove it from the user data
|
365
|
+
#
|
366
|
+
# @param id [String, nil]
|
367
|
+
# @param email [String, nil]
|
368
|
+
# @param name [String, nil]
|
369
|
+
# @return [void]
|
370
|
+
def set_user(id = nil, email = nil, name = nil)
|
371
|
+
new_user = { id: id, email: email, name: name }
|
372
|
+
new_user.reject! { |key, value| value.nil? }
|
373
|
+
|
374
|
+
@user = new_user
|
375
|
+
end
|
376
|
+
|
260
377
|
private
|
261
378
|
|
262
379
|
def generate_exception_list
|
@@ -269,6 +386,12 @@ module Bugsnag
|
|
269
386
|
end
|
270
387
|
end
|
271
388
|
|
389
|
+
def generate_error_list
|
390
|
+
exceptions.map do |exception|
|
391
|
+
Error.new(exception[:errorClass], exception[:message], exception[:stacktrace])
|
392
|
+
end
|
393
|
+
end
|
394
|
+
|
272
395
|
def error_class(exception)
|
273
396
|
# The "Class" check is for some strange exceptions like Timeout::Error
|
274
397
|
# which throw the error class instead of an instance
|
@@ -311,4 +434,5 @@ module Bugsnag
|
|
311
434
|
exceptions
|
312
435
|
end
|
313
436
|
end
|
437
|
+
# rubocop:enable Metrics/ClassLength
|
314
438
|
end
|
@@ -35,7 +35,8 @@ module Bugsnag
|
|
35
35
|
#
|
36
36
|
# This allows Bugsnag to track error rates for a release.
|
37
37
|
def start_session
|
38
|
-
return unless Bugsnag.configuration.enable_sessions
|
38
|
+
return unless Bugsnag.configuration.enable_sessions && Bugsnag.configuration.should_notify_release_stage?
|
39
|
+
|
39
40
|
start_delivery_thread
|
40
41
|
start_time = Time.now().utc().strftime('%Y-%m-%dT%H:%M:00')
|
41
42
|
new_session = {
|
@@ -83,7 +84,7 @@ module Bugsnag
|
|
83
84
|
end
|
84
85
|
end
|
85
86
|
end
|
86
|
-
@delivery_thread = Concurrent::TimerTask.execute(execution_interval:
|
87
|
+
@delivery_thread = Concurrent::TimerTask.execute(execution_interval: 10) do
|
87
88
|
if @session_counts.size > 0
|
88
89
|
send_sessions
|
89
90
|
end
|
@@ -106,11 +107,6 @@ module Bugsnag
|
|
106
107
|
return
|
107
108
|
end
|
108
109
|
|
109
|
-
if !Bugsnag.configuration.should_notify_release_stage?
|
110
|
-
Bugsnag.configuration.debug("Not delivering sessions due to notify_release_stages :#{Bugsnag.configuration.notify_release_stages.inspect}")
|
111
|
-
return
|
112
|
-
end
|
113
|
-
|
114
110
|
body = {
|
115
111
|
:notifier => {
|
116
112
|
:name => Bugsnag::Report::NOTIFIER_NAME,
|
@@ -0,0 +1,102 @@
|
|
1
|
+
module Bugsnag::Utility
|
2
|
+
# @api private
|
3
|
+
class MetadataDelegate
|
4
|
+
# nil is a valid metadata value, so we need a sentinel object so we can tell
|
5
|
+
# if the value parameter has been provided
|
6
|
+
NOT_PROVIDED = Object.new
|
7
|
+
|
8
|
+
##
|
9
|
+
# Add values to metadata
|
10
|
+
#
|
11
|
+
# @overload add_metadata(metadata, section, data)
|
12
|
+
# Merges data into the given section of metadata
|
13
|
+
# @param metadata [Hash] The metadata hash to operate on
|
14
|
+
# @param section [String, Symbol]
|
15
|
+
# @param data [Hash]
|
16
|
+
#
|
17
|
+
# @overload add_metadata(metadata, section, key, value)
|
18
|
+
# Sets key to value in the given section of metadata. If the value is nil
|
19
|
+
# the key will be deleted
|
20
|
+
# @param metadata [Hash] The metadata hash to operate on
|
21
|
+
# @param section [String, Symbol]
|
22
|
+
# @param key [String, Symbol]
|
23
|
+
# @param value
|
24
|
+
#
|
25
|
+
# @return [void]
|
26
|
+
def add_metadata(metadata, section, key_or_data, value = NOT_PROVIDED)
|
27
|
+
case value
|
28
|
+
when NOT_PROVIDED
|
29
|
+
merge_metadata(metadata, section, key_or_data)
|
30
|
+
when nil
|
31
|
+
clear_metadata(metadata, section, key_or_data)
|
32
|
+
else
|
33
|
+
overwrite_metadata(metadata, section, key_or_data, value)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
##
|
38
|
+
# Clear values from metadata
|
39
|
+
#
|
40
|
+
# @overload clear_metadata(metadata, section)
|
41
|
+
# Clears the given section of metadata
|
42
|
+
# @param metadata [Hash] The metadata hash to operate on
|
43
|
+
# @param section [String, Symbol]
|
44
|
+
#
|
45
|
+
# @overload clear_metadata(metadata, section, key)
|
46
|
+
# Clears the key in the given section of metadata
|
47
|
+
# @param metadata [Hash] The metadata hash to operate on
|
48
|
+
# @param section [String, Symbol]
|
49
|
+
# @param key [String, Symbol]
|
50
|
+
#
|
51
|
+
# @return [void]
|
52
|
+
def clear_metadata(metadata, section, key = nil)
|
53
|
+
if key.nil?
|
54
|
+
metadata.delete(section)
|
55
|
+
elsif metadata[section]
|
56
|
+
metadata[section].delete(key)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
private
|
61
|
+
|
62
|
+
##
|
63
|
+
# Merge new metadata into the existing metadata
|
64
|
+
#
|
65
|
+
# Any keys with a 'nil' value in the new metadata will be deleted from the
|
66
|
+
# existing metadata
|
67
|
+
#
|
68
|
+
# @param existing_metadata [Hash]
|
69
|
+
# @param section [String, Symbol]
|
70
|
+
# @param new_metadata [Hash]
|
71
|
+
# @return [void]
|
72
|
+
def merge_metadata(existing_metadata, section, new_metadata)
|
73
|
+
return unless new_metadata.is_a?(Hash)
|
74
|
+
|
75
|
+
existing_metadata[section] ||= {}
|
76
|
+
data = existing_metadata[section]
|
77
|
+
|
78
|
+
new_metadata.each do |key, value|
|
79
|
+
if value.nil?
|
80
|
+
data.delete(key)
|
81
|
+
else
|
82
|
+
data[key] = value
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
##
|
88
|
+
# Overwrite the value in metadata's section & key
|
89
|
+
#
|
90
|
+
# @param metadata [Hash]
|
91
|
+
# @param section [String, Symbol]
|
92
|
+
# @param key [String, Symbol]
|
93
|
+
# @param value
|
94
|
+
# @return [void]
|
95
|
+
def overwrite_metadata(metadata, section, key, value)
|
96
|
+
return unless key.is_a?(String) || key.is_a?(Symbol)
|
97
|
+
|
98
|
+
metadata[section] ||= {}
|
99
|
+
metadata[section][key] = value
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|