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
data/lib/bugsnag.rb
CHANGED
@@ -1,138 +1,586 @@
|
|
1
1
|
require "rubygems"
|
2
2
|
require "thread"
|
3
|
+
require "set"
|
4
|
+
require "json"
|
5
|
+
require "uri"
|
6
|
+
require "socket"
|
7
|
+
require "logger"
|
3
8
|
|
4
9
|
require "bugsnag/version"
|
10
|
+
require "bugsnag/utility/feature_data_store"
|
5
11
|
require "bugsnag/configuration"
|
6
12
|
require "bugsnag/meta_data"
|
7
|
-
require "bugsnag/
|
13
|
+
require "bugsnag/report"
|
14
|
+
require "bugsnag/event"
|
8
15
|
require "bugsnag/cleaner"
|
9
16
|
require "bugsnag/helpers"
|
10
|
-
require "bugsnag/
|
17
|
+
require "bugsnag/session_tracker"
|
11
18
|
|
12
19
|
require "bugsnag/delivery"
|
13
20
|
require "bugsnag/delivery/synchronous"
|
14
21
|
require "bugsnag/delivery/thread_queue"
|
15
22
|
|
16
|
-
require "bugsnag/
|
17
|
-
require "bugsnag/railtie" if defined?(Rails::Railtie)
|
23
|
+
require "bugsnag/feature_flag"
|
18
24
|
|
19
|
-
|
20
|
-
|
21
|
-
require "bugsnag/
|
22
|
-
require "bugsnag/middleware/rails3_request"
|
23
|
-
require "bugsnag/middleware/sidekiq"
|
24
|
-
require "bugsnag/middleware/mailman"
|
25
|
-
require "bugsnag/middleware/rake"
|
26
|
-
require "bugsnag/middleware/callbacks"
|
25
|
+
# Rack is not bundled with the other integrations
|
26
|
+
# as it doesn't auto-configure when loaded
|
27
|
+
require "bugsnag/integrations/rack"
|
27
28
|
|
29
|
+
require "bugsnag/breadcrumb_type"
|
30
|
+
require "bugsnag/breadcrumbs/validator"
|
31
|
+
require "bugsnag/breadcrumbs/breadcrumb"
|
32
|
+
|
33
|
+
require "bugsnag/utility/duplicator"
|
34
|
+
require "bugsnag/utility/metadata_delegate"
|
35
|
+
require "bugsnag/utility/feature_flag_delegate"
|
36
|
+
|
37
|
+
# rubocop:todo Metrics/ModuleLength
|
28
38
|
module Bugsnag
|
29
|
-
LOG_PREFIX = "** [Bugsnag] "
|
30
39
|
LOCK = Mutex.new
|
40
|
+
INTEGRATIONS = [:resque, :sidekiq, :mailman, :delayed_job, :shoryuken, :que, :mongo]
|
41
|
+
|
42
|
+
NIL_EXCEPTION_DESCRIPTION = "'nil' was notified as an exception"
|
31
43
|
|
32
44
|
class << self
|
45
|
+
include Utility::FeatureDataStore
|
46
|
+
|
47
|
+
##
|
33
48
|
# Configure the Bugsnag notifier application-wide settings.
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
49
|
+
#
|
50
|
+
# Yields a {Configuration} object to use to set application settings.
|
51
|
+
#
|
52
|
+
# @yieldparam configuration [Configuration]
|
53
|
+
# @return [void]
|
54
|
+
def configure(validate_api_key=true)
|
55
|
+
yield(configuration) if block_given?
|
56
|
+
|
57
|
+
# Create the session tracker if sessions are enabled to avoid the overhead
|
58
|
+
# of creating it on the first request. We skip this if we're not validating
|
59
|
+
# the API key as we use this internally before the user's configure block
|
60
|
+
# has run, so we don't know if sessions are enabled yet.
|
61
|
+
session_tracker if validate_api_key && configuration.auto_capture_sessions
|
62
|
+
|
63
|
+
check_key_valid if validate_api_key
|
64
|
+
check_endpoint_setup
|
65
|
+
|
66
|
+
register_at_exit
|
67
|
+
end
|
68
|
+
|
69
|
+
##
|
70
|
+
# Explicitly notify of an exception.
|
71
|
+
#
|
72
|
+
# Optionally accepts a block to append metadata to the yielded report.
|
73
|
+
def notify(exception, auto_notify=false, &block)
|
74
|
+
unless false.equal? auto_notify or true.equal? auto_notify
|
75
|
+
configuration.warn("Adding metadata/severity using a hash is no longer supported, please use block syntax instead")
|
76
|
+
auto_notify = false
|
39
77
|
end
|
40
78
|
|
41
|
-
|
79
|
+
return unless should_deliver_notification?(exception, auto_notify)
|
80
|
+
|
81
|
+
exception = NIL_EXCEPTION_DESCRIPTION if exception.nil?
|
82
|
+
|
83
|
+
report = Report.new(exception, configuration, auto_notify)
|
84
|
+
|
85
|
+
# If this is an auto_notify we yield the block before the any middleware is run
|
86
|
+
begin
|
87
|
+
yield(report) if block_given? && auto_notify
|
88
|
+
rescue StandardError => e
|
89
|
+
configuration.warn("Error in internal notify block: #{e}")
|
90
|
+
configuration.warn("Error in internal notify block stacktrace: #{e.backtrace.inspect}")
|
91
|
+
end
|
92
|
+
|
93
|
+
if report.ignore?
|
94
|
+
configuration.debug("Not notifying #{report.exceptions.last[:errorClass]} due to ignore being signified in auto_notify block")
|
95
|
+
return
|
96
|
+
end
|
97
|
+
|
98
|
+
# Run internal middleware
|
99
|
+
configuration.internal_middleware.run(report)
|
100
|
+
if report.ignore?
|
101
|
+
configuration.debug("Not notifying #{report.exceptions.last[:errorClass]} due to ignore being signified in internal middlewares")
|
102
|
+
return
|
103
|
+
end
|
104
|
+
|
105
|
+
# Store before_middleware severity reason for future reference
|
106
|
+
initial_severity = report.severity
|
107
|
+
initial_reason = report.severity_reason
|
108
|
+
|
109
|
+
# Run users middleware
|
110
|
+
configuration.middleware.run(report) do
|
111
|
+
if report.ignore?
|
112
|
+
configuration.debug("Not notifying #{report.exceptions.last[:errorClass]} due to ignore being signified in user provided middleware")
|
113
|
+
return
|
114
|
+
end
|
115
|
+
|
116
|
+
# If this is not an auto_notify then the block was provided by the user. This should be the last
|
117
|
+
# block that is run as it is the users "most specific" block.
|
118
|
+
begin
|
119
|
+
yield(report) if block_given? && !auto_notify
|
120
|
+
rescue StandardError => e
|
121
|
+
configuration.warn("Error in notify block: #{e}")
|
122
|
+
configuration.warn("Error in notify block stacktrace: #{e.backtrace.inspect}")
|
123
|
+
end
|
124
|
+
|
125
|
+
if report.ignore?
|
126
|
+
configuration.debug("Not notifying #{report.exceptions.last[:errorClass]} due to ignore being signified in user provided block")
|
127
|
+
return
|
128
|
+
end
|
42
129
|
|
43
|
-
|
44
|
-
|
130
|
+
# Test whether severity has been changed and ensure severity_reason is consistant in auto_notify case
|
131
|
+
if report.severity != initial_severity
|
132
|
+
report.severity_reason = {
|
133
|
+
:type => Report::USER_CALLBACK_SET_SEVERITY
|
134
|
+
}
|
135
|
+
else
|
136
|
+
report.severity_reason = initial_reason
|
137
|
+
end
|
45
138
|
|
46
|
-
|
47
|
-
|
139
|
+
if report.unhandled_overridden?
|
140
|
+
# let the dashboard know that the unhandled flag was overridden
|
141
|
+
report.severity_reason[:unhandledOverridden] = true
|
142
|
+
end
|
48
143
|
|
49
|
-
|
50
|
-
log "Bugsnag exception handler #{VERSION} ready"
|
51
|
-
@logged_ready = true
|
144
|
+
deliver_notification(report)
|
52
145
|
end
|
53
146
|
end
|
54
147
|
|
55
|
-
|
56
|
-
|
57
|
-
|
148
|
+
##
|
149
|
+
# Registers an at_exit function to automatically catch errors on exit.
|
150
|
+
#
|
151
|
+
# @return [void]
|
152
|
+
def register_at_exit
|
153
|
+
return if at_exit_handler_installed?
|
154
|
+
@exit_handler_added = true
|
155
|
+
at_exit do
|
156
|
+
if $!
|
157
|
+
exception = unwrap_bundler_exception($!)
|
158
|
+
|
159
|
+
Bugsnag.notify(exception, true) do |report|
|
160
|
+
report.severity = 'error'
|
161
|
+
report.severity_reason = {
|
162
|
+
:type => Bugsnag::Report::UNHANDLED_EXCEPTION
|
163
|
+
}
|
164
|
+
end
|
165
|
+
end
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
##
|
170
|
+
# Checks if an at_exit handler has been added.
|
171
|
+
#
|
172
|
+
# The {Bugsnag#configure} method will add this automatically, but it can be
|
173
|
+
# added manually using {Bugsnag#register_at_exit}.
|
174
|
+
#
|
175
|
+
# @return [Boolean]
|
176
|
+
def at_exit_handler_installed?
|
177
|
+
@exit_handler_added ||= false
|
178
|
+
end
|
179
|
+
|
180
|
+
##
|
181
|
+
# Returns the client's Configuration object, or creates one if not yet created.
|
182
|
+
#
|
183
|
+
# @return [Configuration]
|
184
|
+
def configuration
|
185
|
+
@configuration = nil unless defined?(@configuration)
|
186
|
+
@configuration || LOCK.synchronize { @configuration ||= Bugsnag::Configuration.new }
|
187
|
+
end
|
188
|
+
|
189
|
+
##
|
190
|
+
# Returns the client's SessionTracker object, or creates one if not yet created.
|
191
|
+
#
|
192
|
+
# @return [SessionTracker]
|
193
|
+
def session_tracker
|
194
|
+
@session_tracker = nil unless defined?(@session_tracker)
|
195
|
+
@session_tracker || LOCK.synchronize { @session_tracker ||= Bugsnag::SessionTracker.new}
|
196
|
+
end
|
58
197
|
|
59
|
-
|
198
|
+
##
|
199
|
+
# Starts a new session, which allows Bugsnag to track error rates across
|
200
|
+
# releases
|
201
|
+
#
|
202
|
+
# @return [void]
|
203
|
+
def start_session
|
204
|
+
session_tracker.start_session
|
205
|
+
end
|
60
206
|
|
61
|
-
|
62
|
-
|
207
|
+
##
|
208
|
+
# Stop any events being attributed to the current session until it is
|
209
|
+
# resumed or a new session is started
|
210
|
+
#
|
211
|
+
# @see resume_session
|
212
|
+
#
|
213
|
+
# @return [void]
|
214
|
+
def pause_session
|
215
|
+
session_tracker.pause_session
|
63
216
|
end
|
64
217
|
|
65
|
-
|
66
|
-
|
67
|
-
|
218
|
+
##
|
219
|
+
# Resume the current session if it was previously paused. If there is no
|
220
|
+
# current session, a new session will be started
|
221
|
+
#
|
222
|
+
# @see pause_session
|
223
|
+
#
|
224
|
+
# @return [Boolean] true if a paused session was resumed
|
225
|
+
def resume_session
|
226
|
+
session_tracker.resume_session
|
227
|
+
end
|
68
228
|
|
69
|
-
|
229
|
+
##
|
230
|
+
# Allow access to "before notify" callbacks as an array.
|
231
|
+
#
|
232
|
+
# These callbacks will be called whenever an error notification is being made.
|
233
|
+
#
|
234
|
+
# @deprecated Use {Bugsnag#add_on_error} instead
|
235
|
+
def before_notify_callbacks
|
236
|
+
Bugsnag.configuration.request_data[:before_callbacks] ||= []
|
237
|
+
end
|
238
|
+
|
239
|
+
##
|
240
|
+
# Attempts to load all integrations through auto-discovery.
|
241
|
+
#
|
242
|
+
# @return [void]
|
243
|
+
def load_integrations
|
244
|
+
require "bugsnag/integrations/railtie" if defined?(Rails::Railtie)
|
245
|
+
INTEGRATIONS.each do |integration|
|
246
|
+
begin
|
247
|
+
require "bugsnag/integrations/#{integration}"
|
248
|
+
rescue LoadError
|
249
|
+
end
|
250
|
+
end
|
251
|
+
end
|
70
252
|
|
71
|
-
|
72
|
-
|
73
|
-
|
253
|
+
##
|
254
|
+
# Load a specific integration.
|
255
|
+
#
|
256
|
+
# @param integration [Symbol] One of the integrations in {INTEGRATIONS}
|
257
|
+
# @return [void]
|
258
|
+
def load_integration(integration)
|
259
|
+
integration = :railtie if integration == :rails
|
260
|
+
if INTEGRATIONS.include?(integration) || integration == :railtie
|
261
|
+
require "bugsnag/integrations/#{integration}"
|
74
262
|
else
|
75
|
-
|
263
|
+
configuration.debug("Integration #{integration} is not currently supported")
|
76
264
|
end
|
77
265
|
end
|
78
266
|
|
79
|
-
|
80
|
-
#
|
81
|
-
#
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
267
|
+
##
|
268
|
+
# Leave a breadcrumb to be attached to subsequent reports
|
269
|
+
#
|
270
|
+
# @param name [String] the main breadcrumb name/message
|
271
|
+
# @param meta_data [Hash] String, Numeric, or Boolean meta data to attach
|
272
|
+
# @param type [String] the breadcrumb type, see {Bugsnag::BreadcrumbType}
|
273
|
+
# @param auto [Symbol] set to :auto if the breadcrumb is automatically created
|
274
|
+
# @return [void]
|
275
|
+
def leave_breadcrumb(name, meta_data={}, type=Bugsnag::Breadcrumbs::MANUAL_BREADCRUMB_TYPE, auto=:manual)
|
276
|
+
breadcrumb = Bugsnag::Breadcrumbs::Breadcrumb.new(name, type, meta_data, auto)
|
277
|
+
validator = Bugsnag::Breadcrumbs::Validator.new(configuration)
|
278
|
+
|
279
|
+
# Initial validation
|
280
|
+
validator.validate(breadcrumb)
|
281
|
+
|
282
|
+
# Skip if it's already invalid
|
283
|
+
return if breadcrumb.ignore?
|
284
|
+
|
285
|
+
# Run before_breadcrumb_callbacks
|
286
|
+
configuration.before_breadcrumb_callbacks.each do |c|
|
287
|
+
c.arity > 0 ? c.call(breadcrumb) : c.call
|
288
|
+
break if breadcrumb.ignore?
|
289
|
+
end
|
290
|
+
|
291
|
+
# Return early if ignored
|
292
|
+
return if breadcrumb.ignore?
|
293
|
+
|
294
|
+
# Run on_breadcrumb callbacks
|
295
|
+
configuration.on_breadcrumb_callbacks.call(breadcrumb)
|
296
|
+
return if breadcrumb.ignore?
|
297
|
+
|
298
|
+
# Validate again in case of callback alteration
|
299
|
+
validator.validate(breadcrumb)
|
300
|
+
|
301
|
+
# Add to breadcrumbs buffer if still valid
|
302
|
+
configuration.breadcrumbs << breadcrumb unless breadcrumb.ignore?
|
86
303
|
end
|
87
304
|
|
88
|
-
|
89
|
-
|
90
|
-
|
305
|
+
##
|
306
|
+
# Add the given block to the list of on_error callbacks
|
307
|
+
#
|
308
|
+
# The on_error callbacks will be called when an error is captured or reported
|
309
|
+
# and are passed a {Bugsnag::Report} object
|
310
|
+
#
|
311
|
+
# Returning false from an on_error callback will cause the error to be ignored
|
312
|
+
# and will prevent any remaining callbacks from being called
|
313
|
+
#
|
314
|
+
# @return [void]
|
315
|
+
def on_error(&block)
|
316
|
+
configuration.on_error(&block)
|
91
317
|
end
|
92
318
|
|
93
|
-
|
94
|
-
|
95
|
-
|
319
|
+
##
|
320
|
+
# Add the given callback to the list of on_error callbacks
|
321
|
+
#
|
322
|
+
# The on_error callbacks will be called when an error is captured or reported
|
323
|
+
# and are passed a {Bugsnag::Report} object
|
324
|
+
#
|
325
|
+
# Returning false from an on_error callback will cause the error to be ignored
|
326
|
+
# and will prevent any remaining callbacks from being called
|
327
|
+
#
|
328
|
+
# @param callback [Proc, Method, #call]
|
329
|
+
# @return [void]
|
330
|
+
def add_on_error(callback)
|
331
|
+
configuration.add_on_error(callback)
|
96
332
|
end
|
97
333
|
|
98
|
-
|
99
|
-
|
100
|
-
|
334
|
+
##
|
335
|
+
# Remove the given callback from the list of on_error callbacks
|
336
|
+
#
|
337
|
+
# Note that this must be the same Proc instance that was passed to
|
338
|
+
# {Bugsnag#add_on_error}, otherwise it will not be removed
|
339
|
+
#
|
340
|
+
# @param callback [Proc]
|
341
|
+
# @return [void]
|
342
|
+
def remove_on_error(callback)
|
343
|
+
configuration.remove_on_error(callback)
|
101
344
|
end
|
102
345
|
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
346
|
+
##
|
347
|
+
# Add the given callback to the list of on_breadcrumb callbacks
|
348
|
+
#
|
349
|
+
# The on_breadcrumb callbacks will be called when a breadcrumb is left and
|
350
|
+
# are passed the {Breadcrumbs::Breadcrumb Breadcrumb} object
|
351
|
+
#
|
352
|
+
# Returning false from an on_breadcrumb callback will cause the breadcrumb
|
353
|
+
# to be ignored and will prevent any remaining callbacks from being called
|
354
|
+
#
|
355
|
+
# @param callback [Proc, Method, #call]
|
356
|
+
# @return [void]
|
357
|
+
def add_on_breadcrumb(callback)
|
358
|
+
configuration.add_on_breadcrumb(callback)
|
107
359
|
end
|
108
360
|
|
109
|
-
|
110
|
-
|
111
|
-
|
361
|
+
##
|
362
|
+
# Remove the given callback from the list of on_breadcrumb callbacks
|
363
|
+
#
|
364
|
+
# Note that this must be the same instance that was passed to
|
365
|
+
# {add_on_breadcrumb}, otherwise it will not be removed
|
366
|
+
#
|
367
|
+
# @param callback [Proc, Method, #call]
|
368
|
+
# @return [void]
|
369
|
+
def remove_on_breadcrumb(callback)
|
370
|
+
configuration.remove_on_breadcrumb(callback)
|
112
371
|
end
|
113
372
|
|
114
|
-
|
115
|
-
#
|
116
|
-
#
|
117
|
-
|
118
|
-
|
373
|
+
##
|
374
|
+
# Returns the current list of breadcrumbs
|
375
|
+
#
|
376
|
+
# This is a per-thread circular buffer, containing at most 'max_breadcrumbs'
|
377
|
+
# breadcrumbs
|
378
|
+
#
|
379
|
+
# @return [Bugsnag::Utility::CircularBuffer]
|
380
|
+
def breadcrumbs
|
381
|
+
configuration.breadcrumbs
|
119
382
|
end
|
120
383
|
|
121
|
-
|
122
|
-
|
123
|
-
|
384
|
+
##
|
385
|
+
# Returns the client's Cleaner object, or creates one if not yet created.
|
386
|
+
#
|
387
|
+
# @api private
|
388
|
+
#
|
389
|
+
# @return [Cleaner]
|
390
|
+
def cleaner
|
391
|
+
@cleaner = nil unless defined?(@cleaner)
|
392
|
+
@cleaner || LOCK.synchronize do
|
393
|
+
@cleaner ||= Bugsnag::Cleaner.new(configuration)
|
394
|
+
end
|
124
395
|
end
|
125
396
|
|
126
|
-
|
127
|
-
|
128
|
-
|
397
|
+
##
|
398
|
+
# Global metadata added to every event
|
399
|
+
#
|
400
|
+
# @return [Hash]
|
401
|
+
def metadata
|
402
|
+
configuration.metadata
|
403
|
+
end
|
404
|
+
|
405
|
+
##
|
406
|
+
# Add values to metadata
|
407
|
+
#
|
408
|
+
# @overload add_metadata(section, data)
|
409
|
+
# Merges data into the given section of metadata
|
410
|
+
# @param section [String, Symbol]
|
411
|
+
# @param data [Hash]
|
412
|
+
#
|
413
|
+
# @overload add_metadata(section, key, value)
|
414
|
+
# Sets key to value in the given section of metadata. If the value is nil
|
415
|
+
# the key will be deleted
|
416
|
+
# @param section [String, Symbol]
|
417
|
+
# @param key [String, Symbol]
|
418
|
+
# @param value
|
419
|
+
#
|
420
|
+
# @return [void]
|
421
|
+
def add_metadata(section, key_or_data, *args)
|
422
|
+
configuration.add_metadata(section, key_or_data, *args)
|
129
423
|
end
|
130
|
-
end
|
131
|
-
end
|
132
424
|
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
425
|
+
##
|
426
|
+
# Clear values from metadata
|
427
|
+
#
|
428
|
+
# @overload clear_metadata(section)
|
429
|
+
# Clears the given section of metadata
|
430
|
+
# @param section [String, Symbol]
|
431
|
+
#
|
432
|
+
# @overload clear_metadata(section, key)
|
433
|
+
# Clears the key in the given section of metadata
|
434
|
+
# @param section [String, Symbol]
|
435
|
+
# @param key [String, Symbol]
|
436
|
+
#
|
437
|
+
# @return [void]
|
438
|
+
def clear_metadata(section, *args)
|
439
|
+
configuration.clear_metadata(section, *args)
|
440
|
+
end
|
441
|
+
|
442
|
+
# Expose the feature flag delegate internally for use when creating new Events
|
443
|
+
#
|
444
|
+
# The Bugsnag module's feature_flag_delegate is request-specific
|
445
|
+
#
|
446
|
+
# @return [Bugsnag::Utility::FeatureFlagDelegate]
|
447
|
+
# @api private
|
448
|
+
def feature_flag_delegate
|
449
|
+
configuration.request_data[:feature_flag_delegate] ||= Utility::FeatureFlagDelegate.new
|
450
|
+
end
|
451
|
+
|
452
|
+
private
|
453
|
+
|
454
|
+
def should_deliver_notification?(exception, auto_notify)
|
455
|
+
return false unless configuration.enable_events
|
456
|
+
|
457
|
+
reason = abort_reason(exception, auto_notify)
|
458
|
+
configuration.debug(reason) unless reason.nil?
|
459
|
+
reason.nil?
|
460
|
+
end
|
461
|
+
|
462
|
+
def abort_reason(exception, auto_notify)
|
463
|
+
if !configuration.auto_notify && auto_notify
|
464
|
+
"Not notifying because auto_notify is disabled"
|
465
|
+
elsif !configuration.valid_api_key?
|
466
|
+
"Not notifying due to an invalid api_key"
|
467
|
+
elsif !configuration.should_notify_release_stage?
|
468
|
+
"Not notifying due to notify_release_stages :#{configuration.notify_release_stages.inspect}"
|
469
|
+
elsif exception.respond_to?(:skip_bugsnag) && exception.skip_bugsnag
|
470
|
+
"Not notifying due to skip_bugsnag flag"
|
471
|
+
end
|
472
|
+
end
|
473
|
+
|
474
|
+
##
|
475
|
+
# Deliver the notification to Bugsnag
|
476
|
+
#
|
477
|
+
# @param report [Report]
|
478
|
+
# @return void
|
479
|
+
def deliver_notification(report)
|
480
|
+
configuration.info("Notifying #{configuration.notify_endpoint} of #{report.exceptions.last[:errorClass]}")
|
481
|
+
|
482
|
+
options = { headers: report.headers }
|
483
|
+
|
484
|
+
delivery_method = Bugsnag::Delivery[configuration.delivery_method]
|
485
|
+
|
486
|
+
if delivery_method.respond_to?(:serialize_and_deliver)
|
487
|
+
delivery_method.serialize_and_deliver(
|
488
|
+
configuration.notify_endpoint,
|
489
|
+
proc { report_to_json(report) },
|
490
|
+
configuration,
|
491
|
+
options
|
492
|
+
)
|
493
|
+
else
|
494
|
+
delivery_method.deliver(
|
495
|
+
configuration.notify_endpoint,
|
496
|
+
report_to_json(report),
|
497
|
+
configuration,
|
498
|
+
options
|
499
|
+
)
|
500
|
+
end
|
501
|
+
|
502
|
+
leave_breadcrumb(
|
503
|
+
report.summary[:error_class],
|
504
|
+
report.summary,
|
505
|
+
Bugsnag::Breadcrumbs::ERROR_BREADCRUMB_TYPE,
|
506
|
+
:auto
|
507
|
+
)
|
508
|
+
end
|
509
|
+
|
510
|
+
# Check if the API key is valid and warn (once) if it is not
|
511
|
+
def check_key_valid
|
512
|
+
@key_warning = false unless defined?(@key_warning)
|
513
|
+
if !configuration.valid_api_key? && !@key_warning
|
514
|
+
configuration.warn("No valid API key has been set, notifications will not be sent")
|
515
|
+
@key_warning = true
|
516
|
+
end
|
517
|
+
end
|
518
|
+
|
519
|
+
##
|
520
|
+
# Verifies the current endpoint setup
|
521
|
+
#
|
522
|
+
# If only a notify_endpoint has been set, session tracking will be disabled
|
523
|
+
# If only a session_endpoint has been set, and ArgumentError will be raised
|
524
|
+
def check_endpoint_setup
|
525
|
+
notify_set = configuration.notify_endpoint && configuration.notify_endpoint != Bugsnag::Configuration::DEFAULT_NOTIFY_ENDPOINT
|
526
|
+
session_set = configuration.session_endpoint && configuration.session_endpoint != Bugsnag::Configuration::DEFAULT_SESSION_ENDPOINT
|
527
|
+
if notify_set && !session_set
|
528
|
+
configuration.warn("The session endpoint has not been set, all further session capturing will be disabled")
|
529
|
+
configuration.disable_sessions
|
530
|
+
elsif !notify_set && session_set
|
531
|
+
raise ArgumentError, "The session endpoint cannot be modified without the notify endpoint"
|
532
|
+
end
|
533
|
+
end
|
534
|
+
|
535
|
+
##
|
536
|
+
# Convert the Report object to JSON
|
537
|
+
#
|
538
|
+
# We ensure the report is safe to send by removing recursion, fixing
|
539
|
+
# encoding errors and redacting metadata according to "meta_data_filters"
|
540
|
+
#
|
541
|
+
# @param report [Report]
|
542
|
+
# @return [String]
|
543
|
+
def report_to_json(report)
|
544
|
+
cleaned = cleaner.clean_object(report.as_json)
|
545
|
+
trimmed = Bugsnag::Helpers.trim_if_needed(cleaned)
|
546
|
+
|
547
|
+
::JSON.dump(trimmed)
|
548
|
+
end
|
549
|
+
|
550
|
+
##
|
551
|
+
# When running a script with 'bundle exec', uncaught exceptions will be
|
552
|
+
# converted to "friendly errors" which has the side effect of wrapping them
|
553
|
+
# in a SystemExit
|
554
|
+
#
|
555
|
+
# By default we ignore SystemExit, so need to unwrap the original exception
|
556
|
+
# in order to avoid ignoring real errors
|
557
|
+
#
|
558
|
+
# @param exception [Exception]
|
559
|
+
# @return [Exception]
|
560
|
+
def unwrap_bundler_exception(exception)
|
561
|
+
running_in_bundler = ENV.include?('BUNDLE_BIN_PATH')
|
562
|
+
|
563
|
+
# See if this exception came from Bundler's 'with_friendly_errors' method
|
564
|
+
return exception unless running_in_bundler
|
565
|
+
return exception unless exception.is_a?(SystemExit)
|
566
|
+
return exception unless exception.respond_to?(:cause)
|
567
|
+
return exception unless exception.backtrace.first.include?('/bundler/friendly_errors.rb')
|
568
|
+
return exception if exception.cause.nil?
|
569
|
+
|
570
|
+
unwrapped = exception.cause
|
571
|
+
|
572
|
+
# We may need to unwrap another level if the exception came from running
|
573
|
+
# an executable file directly (i.e. 'bundle exec <file>'). In this case
|
574
|
+
# there can be a SystemExit from 'with_friendly_errors' _and_ a SystemExit
|
575
|
+
# from 'kernel_load'
|
576
|
+
return unwrapped unless unwrapped.is_a?(SystemExit)
|
577
|
+
return unwrapped unless unwrapped.backtrace.first.include?('/bundler/cli/exec.rb')
|
578
|
+
return unwrapped if unwrapped.cause.nil?
|
579
|
+
|
580
|
+
unwrapped.cause
|
581
|
+
end
|
137
582
|
end
|
138
583
|
end
|
584
|
+
# rubocop:enable Metrics/ModuleLength
|
585
|
+
|
586
|
+
Bugsnag.load_integrations unless ENV["BUGSNAG_DISABLE_AUTOCONFIGURE"]
|