bugsnag 6.19.0 → 6.23.0
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 +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
|