bugsnag 6.21.0 → 6.24.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +102 -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/cleaner.rb +31 -18
- data/lib/bugsnag/configuration.rb +240 -22
- 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/integrations/rack.rb +3 -3
- data/lib/bugsnag/integrations/rails/active_job.rb +102 -0
- data/lib/bugsnag/integrations/railtie.rb +9 -1
- data/lib/bugsnag/integrations/resque.rb +17 -3
- data/lib/bugsnag/middleware/active_job.rb +18 -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/report.rb +166 -6
- data/lib/bugsnag/session_tracker.rb +52 -12
- data/lib/bugsnag/stacktrace.rb +10 -1
- data/lib/bugsnag/tasks/bugsnag.rake +1 -1
- data/lib/bugsnag/utility/duplicator.rb +124 -0
- data/lib/bugsnag/utility/metadata_delegate.rb +102 -0
- data/lib/bugsnag.rb +126 -4
- metadata +16 -6
@@ -12,6 +12,9 @@ require "bugsnag/middleware/session_data"
|
|
12
12
|
require "bugsnag/middleware/breadcrumbs"
|
13
13
|
require "bugsnag/utility/circular_buffer"
|
14
14
|
require "bugsnag/breadcrumbs/breadcrumbs"
|
15
|
+
require "bugsnag/breadcrumbs/on_breadcrumb_callback_list"
|
16
|
+
require "bugsnag/endpoint_configuration"
|
17
|
+
require "bugsnag/endpoint_validator"
|
15
18
|
|
16
19
|
module Bugsnag
|
17
20
|
class Configuration
|
@@ -24,6 +27,7 @@ module Bugsnag
|
|
24
27
|
attr_accessor :release_stage
|
25
28
|
|
26
29
|
# A list of which release stages should cause notifications to be sent
|
30
|
+
# @deprecated Use {#enabled_release_stages} instead
|
27
31
|
# @return [Array<String>, nil]
|
28
32
|
attr_accessor :notify_release_stages
|
29
33
|
|
@@ -52,9 +56,20 @@ module Bugsnag
|
|
52
56
|
|
53
57
|
# A list of keys that should be filtered out from the report and breadcrumb
|
54
58
|
# metadata before sending them to Bugsnag
|
59
|
+
# @deprecated Use {#redacted_keys} instead
|
55
60
|
# @return [Set<String, Regexp>]
|
56
61
|
attr_accessor :meta_data_filters
|
57
62
|
|
63
|
+
# A set of keys that should be redacted from the report and breadcrumb
|
64
|
+
# metadata before sending them to Bugsnag
|
65
|
+
#
|
66
|
+
# When adding strings, keys that are equal to the string (ignoring case)
|
67
|
+
# will be redacted. When adding regular expressions, any keys which match
|
68
|
+
# the regular expression will be redacted
|
69
|
+
#
|
70
|
+
# @return [Set<String, Regexp>]
|
71
|
+
attr_accessor :redacted_keys
|
72
|
+
|
58
73
|
# The logger to use for Bugsnag log messages
|
59
74
|
# @return [Logger]
|
60
75
|
attr_accessor :logger
|
@@ -104,6 +119,7 @@ module Bugsnag
|
|
104
119
|
attr_accessor :discard_classes
|
105
120
|
|
106
121
|
# Whether Bugsnag should automatically record sessions
|
122
|
+
# @deprecated Use {#auto_track_sessions} instead
|
107
123
|
# @return [Boolean]
|
108
124
|
attr_accessor :auto_capture_sessions
|
109
125
|
|
@@ -111,21 +127,23 @@ module Bugsnag
|
|
111
127
|
# @return [Set<Class, Proc>]
|
112
128
|
attr_accessor :ignore_classes
|
113
129
|
|
114
|
-
# The
|
115
|
-
# @return [
|
116
|
-
attr_reader :
|
117
|
-
alias :endpoint :notify_endpoint
|
130
|
+
# The URLs to send events and sessions to
|
131
|
+
# @return [EndpointConfiguration]
|
132
|
+
attr_reader :endpoints
|
118
133
|
|
119
|
-
#
|
120
|
-
# @
|
121
|
-
|
134
|
+
# Whether events will be delivered
|
135
|
+
# @api private
|
136
|
+
# @return [Boolean]
|
137
|
+
attr_reader :enable_events
|
122
138
|
|
123
139
|
# Whether sessions will be delivered
|
140
|
+
# @api private
|
124
141
|
# @return [Boolean]
|
125
142
|
attr_reader :enable_sessions
|
126
143
|
|
127
144
|
# A list of strings indicating allowable automatic breadcrumb types
|
128
|
-
# @
|
145
|
+
# @deprecated Use {#enabled_breadcrumb_types} instead
|
146
|
+
# @see Bugsnag::BreadcrumbType
|
129
147
|
# @return [Array<String>]
|
130
148
|
attr_accessor :enabled_automatic_breadcrumb_types
|
131
149
|
|
@@ -137,14 +155,37 @@ module Bugsnag
|
|
137
155
|
# @return [Integer]
|
138
156
|
attr_reader :max_breadcrumbs
|
139
157
|
|
140
|
-
#
|
158
|
+
# @deprecated Use {vendor_paths} instead
|
141
159
|
# @return [Regexp]
|
142
160
|
attr_accessor :vendor_path
|
143
161
|
|
162
|
+
# An array of paths within the {project_root} that should not be considered
|
163
|
+
# as "in project"
|
164
|
+
#
|
165
|
+
# These paths should be relative to the {project_root} and will only match
|
166
|
+
# whole directory names
|
167
|
+
#
|
168
|
+
# @return [Array<String>]
|
169
|
+
attr_accessor :vendor_paths
|
170
|
+
|
171
|
+
# The default context for all future events
|
172
|
+
# Setting this will disable automatic context setting
|
173
|
+
# @return [String, nil]
|
174
|
+
attr_accessor :context
|
175
|
+
|
176
|
+
# Global metadata added to every event
|
177
|
+
# @return [Hash]
|
178
|
+
attr_reader :metadata
|
179
|
+
|
144
180
|
# @api private
|
145
181
|
# @return [Array<String>]
|
146
182
|
attr_reader :scopes_to_filter
|
147
183
|
|
184
|
+
# Expose on_breadcrumb_callbacks internally for Bugsnag.leave_breadcrumb
|
185
|
+
# @api private
|
186
|
+
# @return [Breadcrumbs::OnBreadcrumbCallbackList]
|
187
|
+
attr_reader :on_breadcrumb_callbacks
|
188
|
+
|
148
189
|
API_KEY_REGEX = /[0-9a-f]{32}/i
|
149
190
|
THREAD_LOCAL_NAME = "bugsnag_req_data"
|
150
191
|
|
@@ -180,6 +221,7 @@ module Bugsnag
|
|
180
221
|
self.send_environment = false
|
181
222
|
self.send_code = true
|
182
223
|
self.meta_data_filters = Set.new(DEFAULT_META_DATA_FILTERS)
|
224
|
+
@redacted_keys = Set.new
|
183
225
|
self.scopes_to_filter = DEFAULT_SCOPES_TO_FILTER
|
184
226
|
self.hostname = default_hostname
|
185
227
|
self.runtime_versions = {}
|
@@ -193,16 +235,20 @@ module Bugsnag
|
|
193
235
|
# All valid breadcrumb types should be allowable initially
|
194
236
|
self.enabled_automatic_breadcrumb_types = Bugsnag::Breadcrumbs::VALID_BREADCRUMB_TYPES.dup
|
195
237
|
self.before_breadcrumb_callbacks = []
|
238
|
+
@on_breadcrumb_callbacks = Breadcrumbs::OnBreadcrumbCallbackList.new(self)
|
196
239
|
|
197
240
|
# Store max_breadcrumbs here instead of outputting breadcrumbs.max_items
|
198
241
|
# to avoid infinite recursion when creating breadcrumb buffer
|
199
242
|
@max_breadcrumbs = DEFAULT_MAX_BREADCRUMBS
|
200
243
|
|
201
|
-
|
202
|
-
|
203
|
-
@
|
244
|
+
@endpoints = EndpointConfiguration.new(DEFAULT_NOTIFY_ENDPOINT, DEFAULT_SESSION_ENDPOINT)
|
245
|
+
|
246
|
+
@enable_events = true
|
204
247
|
@enable_sessions = true
|
205
248
|
|
249
|
+
@metadata = {}
|
250
|
+
@metadata_delegate = Utility::MetadataDelegate.new
|
251
|
+
|
206
252
|
# SystemExit and SignalException are common Exception types seen with
|
207
253
|
# successful exits and are not automatically reported to Bugsnag
|
208
254
|
# TODO move these defaults into `discard_classes` when `ignore_classes`
|
@@ -222,6 +268,7 @@ module Bugsnag
|
|
222
268
|
# Stacktrace lines that matches regex will be marked as "out of project"
|
223
269
|
# will only appear in the full trace.
|
224
270
|
self.vendor_path = DEFAULT_VENDOR_PATH
|
271
|
+
@vendor_paths = []
|
225
272
|
|
226
273
|
# Set up logging
|
227
274
|
self.logger = Logger.new(STDOUT)
|
@@ -389,15 +436,23 @@ module Bugsnag
|
|
389
436
|
##
|
390
437
|
# Logs a warning level message
|
391
438
|
#
|
392
|
-
# @param
|
439
|
+
# @param message [String, #to_s] The message to log
|
393
440
|
def warn(message)
|
394
441
|
logger.warn(PROG_NAME) { message }
|
395
442
|
end
|
396
443
|
|
444
|
+
##
|
445
|
+
# Logs an error level message
|
446
|
+
#
|
447
|
+
# @param message [String, #to_s] The message to log
|
448
|
+
def error(message)
|
449
|
+
logger.error(PROG_NAME) { message }
|
450
|
+
end
|
451
|
+
|
397
452
|
##
|
398
453
|
# Logs a debug level message
|
399
454
|
#
|
400
|
-
# @param
|
455
|
+
# @param message [String, #to_s] The message to log
|
401
456
|
def debug(message)
|
402
457
|
logger.debug(PROG_NAME) { message }
|
403
458
|
end
|
@@ -426,33 +481,54 @@ module Bugsnag
|
|
426
481
|
end
|
427
482
|
|
428
483
|
##
|
429
|
-
# Returns the
|
484
|
+
# Returns the current list of breadcrumbs
|
485
|
+
#
|
486
|
+
# This is a per-thread circular buffer, containing at most 'max_breadcrumbs'
|
487
|
+
# breadcrumbs
|
430
488
|
#
|
431
|
-
# @return [Bugsnag::Utility::CircularBuffer]
|
489
|
+
# @return [Bugsnag::Utility::CircularBuffer]
|
432
490
|
def breadcrumbs
|
433
491
|
request_data[:breadcrumbs] ||= Bugsnag::Utility::CircularBuffer.new(@max_breadcrumbs)
|
434
492
|
end
|
435
493
|
|
494
|
+
# The URL error notifications will be delivered to
|
495
|
+
# @!attribute notify_endpoint
|
496
|
+
# @return [String]
|
497
|
+
# @deprecated Use {#endpoints} instead
|
498
|
+
def notify_endpoint
|
499
|
+
@endpoints.notify
|
500
|
+
end
|
501
|
+
|
502
|
+
alias :endpoint :notify_endpoint
|
503
|
+
|
436
504
|
# Sets the notification endpoint
|
437
505
|
#
|
438
|
-
# @deprecated Use {#
|
506
|
+
# @deprecated Use {#endpoints} instead
|
439
507
|
#
|
440
508
|
# @param new_notify_endpoint [String] The URL to deliver error notifications to
|
441
509
|
# @return [void]
|
442
510
|
def endpoint=(new_notify_endpoint)
|
443
|
-
warn("The 'endpoint' configuration option is deprecated.
|
511
|
+
warn("The 'endpoint' configuration option is deprecated. Set both endpoints with the 'endpoints=' method instead")
|
444
512
|
set_endpoints(new_notify_endpoint, session_endpoint) # Pass the existing session_endpoint through so it doesn't get overwritten
|
445
513
|
end
|
446
514
|
|
515
|
+
# The URL session notifications will be delivered to
|
516
|
+
# @!attribute session_endpoint
|
517
|
+
# @return [String]
|
518
|
+
# @deprecated Use {#endpoints} instead
|
519
|
+
def session_endpoint
|
520
|
+
@endpoints.sessions
|
521
|
+
end
|
522
|
+
|
447
523
|
##
|
448
524
|
# Sets the sessions endpoint
|
449
525
|
#
|
450
|
-
# @deprecated Use {#
|
526
|
+
# @deprecated Use {#endpoints} instead
|
451
527
|
#
|
452
528
|
# @param new_session_endpoint [String] The URL to deliver session notifications to
|
453
529
|
# @return [void]
|
454
530
|
def session_endpoint=(new_session_endpoint)
|
455
|
-
warn("The 'session_endpoint' configuration option is deprecated.
|
531
|
+
warn("The 'session_endpoint' configuration option is deprecated. Set both endpoints with the 'endpoints=' method instead")
|
456
532
|
set_endpoints(notify_endpoint, new_session_endpoint) # Pass the existing notify_endpoint through so it doesn't get overwritten
|
457
533
|
end
|
458
534
|
|
@@ -462,9 +538,26 @@ module Bugsnag
|
|
462
538
|
# @param new_notify_endpoint [String] The URL to deliver error notifications to
|
463
539
|
# @param new_session_endpoint [String] The URL to deliver session notifications to
|
464
540
|
# @return [void]
|
541
|
+
# @deprecated Use {#endpoints} instead
|
465
542
|
def set_endpoints(new_notify_endpoint, new_session_endpoint)
|
466
|
-
|
467
|
-
|
543
|
+
self.endpoints = EndpointConfiguration.new(new_notify_endpoint, new_session_endpoint)
|
544
|
+
end
|
545
|
+
|
546
|
+
def endpoints=(endpoint_configuration)
|
547
|
+
result = EndpointValidator.validate(endpoint_configuration)
|
548
|
+
|
549
|
+
if result.valid?
|
550
|
+
@enable_events = true
|
551
|
+
@enable_sessions = true
|
552
|
+
else
|
553
|
+
warn(result.reason)
|
554
|
+
|
555
|
+
@enable_events = result.keep_events_enabled_for_backwards_compatibility?
|
556
|
+
@enable_sessions = false
|
557
|
+
end
|
558
|
+
|
559
|
+
# use the given endpoints even if they are invalid
|
560
|
+
@endpoints = endpoint_configuration
|
468
561
|
end
|
469
562
|
|
470
563
|
##
|
@@ -503,6 +596,131 @@ module Bugsnag
|
|
503
596
|
middleware.remove(callback)
|
504
597
|
end
|
505
598
|
|
599
|
+
##
|
600
|
+
# Add the given callback to the list of on_breadcrumb callbacks
|
601
|
+
#
|
602
|
+
# The on_breadcrumb callbacks will be called when a breadcrumb is left and
|
603
|
+
# are passed the {Breadcrumbs::Breadcrumb Breadcrumb} object
|
604
|
+
#
|
605
|
+
# Returning false from an on_breadcrumb callback will cause the breadcrumb
|
606
|
+
# to be ignored and will prevent any remaining callbacks from being called
|
607
|
+
#
|
608
|
+
# @param callback [Proc, Method, #call]
|
609
|
+
# @return [void]
|
610
|
+
def add_on_breadcrumb(callback)
|
611
|
+
@on_breadcrumb_callbacks.add(callback)
|
612
|
+
end
|
613
|
+
|
614
|
+
##
|
615
|
+
# Remove the given callback from the list of on_breadcrumb callbacks
|
616
|
+
#
|
617
|
+
# Note that this must be the same instance that was passed to
|
618
|
+
# {add_on_breadcrumb}, otherwise it will not be removed
|
619
|
+
#
|
620
|
+
# @param callback [Proc, Method, #call]
|
621
|
+
# @return [void]
|
622
|
+
def remove_on_breadcrumb(callback)
|
623
|
+
@on_breadcrumb_callbacks.remove(callback)
|
624
|
+
end
|
625
|
+
|
626
|
+
##
|
627
|
+
# Add values to metadata
|
628
|
+
#
|
629
|
+
# @overload add_metadata(section, data)
|
630
|
+
# Merges data into the given section of metadata
|
631
|
+
# @param section [String, Symbol]
|
632
|
+
# @param data [Hash]
|
633
|
+
#
|
634
|
+
# @overload add_metadata(section, key, value)
|
635
|
+
# Sets key to value in the given section of metadata. If the value is nil
|
636
|
+
# the key will be deleted
|
637
|
+
# @param section [String, Symbol]
|
638
|
+
# @param key [String, Symbol]
|
639
|
+
# @param value
|
640
|
+
#
|
641
|
+
# @return [void]
|
642
|
+
def add_metadata(section, key_or_data, *args)
|
643
|
+
@mutex.synchronize do
|
644
|
+
@metadata_delegate.add_metadata(@metadata, section, key_or_data, *args)
|
645
|
+
end
|
646
|
+
end
|
647
|
+
|
648
|
+
##
|
649
|
+
# Clear values from metadata
|
650
|
+
#
|
651
|
+
# @overload clear_metadata(section)
|
652
|
+
# Clears the given section of metadata
|
653
|
+
# @param section [String, Symbol]
|
654
|
+
#
|
655
|
+
# @overload clear_metadata(section, key)
|
656
|
+
# Clears the key in the given section of metadata
|
657
|
+
# @param section [String, Symbol]
|
658
|
+
# @param key [String, Symbol]
|
659
|
+
#
|
660
|
+
# @return [void]
|
661
|
+
def clear_metadata(section, *args)
|
662
|
+
@mutex.synchronize do
|
663
|
+
@metadata_delegate.clear_metadata(@metadata, section, *args)
|
664
|
+
end
|
665
|
+
end
|
666
|
+
|
667
|
+
##
|
668
|
+
# Has the context been explicitly set?
|
669
|
+
#
|
670
|
+
# This is necessary to differentiate between the context not being set and
|
671
|
+
# the context being set to 'nil' explicitly
|
672
|
+
#
|
673
|
+
# @api private
|
674
|
+
# @return [Boolean]
|
675
|
+
def context_set?
|
676
|
+
defined?(@context) != nil
|
677
|
+
end
|
678
|
+
|
679
|
+
# TODO: These methods can be a simple attr_accessor when they replace the
|
680
|
+
# methods they are aliasing
|
681
|
+
# NOTE: they are not aliases as YARD doesn't allow documenting the non-alias
|
682
|
+
# as deprecated without also marking the alias as deprecated
|
683
|
+
|
684
|
+
# A list of which release stages should cause notifications to be sent
|
685
|
+
# @!attribute enabled_release_stages
|
686
|
+
# @return [Array<String>, nil]
|
687
|
+
def enabled_release_stages
|
688
|
+
@notify_release_stages
|
689
|
+
end
|
690
|
+
|
691
|
+
# @param release_stages [Array<String>, nil]
|
692
|
+
# @return [void]
|
693
|
+
def enabled_release_stages=(release_stages)
|
694
|
+
@notify_release_stages = release_stages
|
695
|
+
end
|
696
|
+
|
697
|
+
# A list of breadcrumb types that Bugsnag will collect automatically
|
698
|
+
# @!attribute enabled_breadcrumb_types
|
699
|
+
# @see Bugsnag::BreadcrumbType
|
700
|
+
# @return [Array<String>]
|
701
|
+
def enabled_breadcrumb_types
|
702
|
+
@enabled_automatic_breadcrumb_types
|
703
|
+
end
|
704
|
+
|
705
|
+
# @param breadcrumb_types [Array<String>]
|
706
|
+
# @return [void]
|
707
|
+
def enabled_breadcrumb_types=(breadcrumb_types)
|
708
|
+
@enabled_automatic_breadcrumb_types = breadcrumb_types
|
709
|
+
end
|
710
|
+
|
711
|
+
# Whether sessions should be tracked automatically
|
712
|
+
# @!attribute auto_track_sessions
|
713
|
+
# @return [Boolean]
|
714
|
+
def auto_track_sessions
|
715
|
+
@auto_capture_sessions
|
716
|
+
end
|
717
|
+
|
718
|
+
# @param track_sessions [Boolean]
|
719
|
+
# @return [void]
|
720
|
+
def auto_track_sessions=(track_sessions)
|
721
|
+
@auto_capture_sessions = track_sessions
|
722
|
+
end
|
723
|
+
|
506
724
|
private
|
507
725
|
|
508
726
|
attr_writer :scopes_to_filter
|
@@ -18,8 +18,8 @@ module Bugsnag
|
|
18
18
|
# KLUDGE: Since we don't re-raise http exceptions, this breaks rspec
|
19
19
|
raise if e.class.to_s == "RSpec::Expectations::ExpectationNotMetError"
|
20
20
|
|
21
|
-
configuration.
|
22
|
-
configuration.
|
21
|
+
configuration.error("Unable to send information to Bugsnag (#{url}), #{e.inspect}")
|
22
|
+
configuration.error(e.backtrace)
|
23
23
|
end
|
24
24
|
end
|
25
25
|
|
@@ -31,8 +31,8 @@ module Bugsnag
|
|
31
31
|
begin
|
32
32
|
payload = get_payload.call
|
33
33
|
rescue StandardError => e
|
34
|
-
configuration.
|
35
|
-
configuration.
|
34
|
+
configuration.error("Unable to send information to Bugsnag (#{url}), #{e.inspect}")
|
35
|
+
configuration.error(e.backtrace)
|
36
36
|
end
|
37
37
|
|
38
38
|
Synchronous.deliver(url, payload, configuration, options) unless payload.nil?
|
@@ -0,0 +1,80 @@
|
|
1
|
+
module Bugsnag
|
2
|
+
# @api private
|
3
|
+
class EndpointValidator
|
4
|
+
def self.validate(endpoints)
|
5
|
+
# ensure we have an EndpointConfiguration object
|
6
|
+
return Result.missing_urls unless endpoints.is_a?(EndpointConfiguration)
|
7
|
+
|
8
|
+
# check for missing URLs
|
9
|
+
return Result.missing_urls if endpoints.notify.nil? && endpoints.sessions.nil?
|
10
|
+
return Result.missing_notify if endpoints.notify.nil?
|
11
|
+
return Result.missing_session if endpoints.sessions.nil?
|
12
|
+
|
13
|
+
# check for empty URLs
|
14
|
+
return Result.invalid_urls if endpoints.notify.empty? && endpoints.sessions.empty?
|
15
|
+
return Result.invalid_notify if endpoints.notify.empty?
|
16
|
+
return Result.invalid_session if endpoints.sessions.empty?
|
17
|
+
|
18
|
+
Result.valid
|
19
|
+
end
|
20
|
+
|
21
|
+
# @api private
|
22
|
+
class Result
|
23
|
+
# rubocop:disable Layout/LineLength
|
24
|
+
MISSING_URLS = "Invalid configuration. endpoints must be set with both a notify and session URL. Bugsnag will not send any requests.".freeze
|
25
|
+
MISSING_NOTIFY_URL = "Invalid configuration. endpoints.sessions cannot be set without also setting endpoints.notify. Bugsnag will not send any requests.".freeze
|
26
|
+
MISSING_SESSION_URL = "Invalid configuration. endpoints.notify cannot be set without also setting endpoints.sessions. Bugsnag will not send any sessions.".freeze
|
27
|
+
|
28
|
+
INVALID_URLS = "Invalid configuration. endpoints should be valid URLs, got empty strings. Bugsnag will not send any requests.".freeze
|
29
|
+
INVALID_NOTIFY_URL = "Invalid configuration. endpoints.notify should be a valid URL, got empty string. Bugsnag will not send any requests.".freeze
|
30
|
+
INVALID_SESSION_URL = "Invalid configuration. endpoints.sessions should be a valid URL, got empty string. Bugsnag will not send any sessions.".freeze
|
31
|
+
# rubocop:enable Layout/LineLength
|
32
|
+
|
33
|
+
attr_reader :reason
|
34
|
+
|
35
|
+
def initialize(valid, keep_events_enabled_for_backwards_compatibility = true, reason = nil)
|
36
|
+
@valid = valid
|
37
|
+
@keep_events_enabled_for_backwards_compatibility = keep_events_enabled_for_backwards_compatibility
|
38
|
+
@reason = reason
|
39
|
+
end
|
40
|
+
|
41
|
+
def valid?
|
42
|
+
@valid
|
43
|
+
end
|
44
|
+
|
45
|
+
def keep_events_enabled_for_backwards_compatibility?
|
46
|
+
@keep_events_enabled_for_backwards_compatibility
|
47
|
+
end
|
48
|
+
|
49
|
+
# factory functions
|
50
|
+
|
51
|
+
def self.valid
|
52
|
+
new(true)
|
53
|
+
end
|
54
|
+
|
55
|
+
def self.missing_urls
|
56
|
+
new(false, false, MISSING_URLS)
|
57
|
+
end
|
58
|
+
|
59
|
+
def self.missing_notify
|
60
|
+
new(false, false, MISSING_NOTIFY_URL)
|
61
|
+
end
|
62
|
+
|
63
|
+
def self.missing_session
|
64
|
+
new(false, true, MISSING_SESSION_URL)
|
65
|
+
end
|
66
|
+
|
67
|
+
def self.invalid_urls
|
68
|
+
new(false, false, INVALID_URLS)
|
69
|
+
end
|
70
|
+
|
71
|
+
def self.invalid_notify
|
72
|
+
new(false, false, INVALID_NOTIFY_URL)
|
73
|
+
end
|
74
|
+
|
75
|
+
def self.invalid_session
|
76
|
+
new(false, true, INVALID_SESSION_URL)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Bugsnag
|
2
|
+
class Error
|
3
|
+
# @return [String] the error's class name
|
4
|
+
attr_accessor :error_class
|
5
|
+
|
6
|
+
# @return [String] the error's message
|
7
|
+
attr_accessor :error_message
|
8
|
+
|
9
|
+
# @return [Hash] the error's processed stacktrace
|
10
|
+
attr_reader :stacktrace
|
11
|
+
|
12
|
+
# @return [String] the type of error (always "ruby")
|
13
|
+
attr_accessor :type
|
14
|
+
|
15
|
+
# @api private
|
16
|
+
TYPE = "ruby".freeze
|
17
|
+
|
18
|
+
def initialize(error_class, error_message, stacktrace)
|
19
|
+
@error_class = error_class
|
20
|
+
@error_message = error_message
|
21
|
+
@stacktrace = stacktrace
|
22
|
+
@type = TYPE
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -25,9 +25,9 @@ module Bugsnag
|
|
25
25
|
end
|
26
26
|
|
27
27
|
# Hook up rack-based notification middlewares
|
28
|
-
config.
|
29
|
-
config.
|
30
|
-
config.
|
28
|
+
config.internal_middleware.insert_before(Bugsnag::Middleware::Rails3Request, Bugsnag::Middleware::RackRequest) if defined?(::Rack)
|
29
|
+
config.internal_middleware.use(Bugsnag::Middleware::WardenUser) if defined?(Warden)
|
30
|
+
config.internal_middleware.use(Bugsnag::Middleware::ClearanceUser) if defined?(Clearance)
|
31
31
|
|
32
32
|
# Set environment data for payload
|
33
33
|
# Note we only set the detected app_type if it's not already set. This
|
@@ -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
|
@@ -60,7 +60,7 @@ module Bugsnag
|
|
60
60
|
config.logger = ::Rails.logger
|
61
61
|
config.release_stage ||= ::Rails.env.to_s
|
62
62
|
config.project_root = ::Rails.root.to_s
|
63
|
-
config.
|
63
|
+
config.internal_middleware.use(Bugsnag::Middleware::Rails3Request)
|
64
64
|
config.runtime_versions["rails"] = ::Rails::VERSION::STRING
|
65
65
|
end
|
66
66
|
|
@@ -74,6 +74,14 @@ module Bugsnag
|
|
74
74
|
include Bugsnag::Rails::ActiveRecordRescue
|
75
75
|
end
|
76
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
|
+
|
77
85
|
Bugsnag::Rails::DEFAULT_RAILS_BREADCRUMBS.each { |event| event_subscription(event) }
|
78
86
|
|
79
87
|
# Make sure we don't overwrite the value set by another integration because
|