bugsnag 6.21.0 → 6.25.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (41) hide show
  1. checksums.yaml +4 -4
  2. data/.yardopts +1 -0
  3. data/CHANGELOG.md +137 -0
  4. data/VERSION +1 -1
  5. data/bugsnag.gemspec +18 -1
  6. data/lib/bugsnag/breadcrumb_type.rb +14 -0
  7. data/lib/bugsnag/breadcrumbs/breadcrumb.rb +34 -1
  8. data/lib/bugsnag/breadcrumbs/breadcrumbs.rb +1 -0
  9. data/lib/bugsnag/breadcrumbs/on_breadcrumb_callback_list.rb +50 -0
  10. data/lib/bugsnag/cleaner.rb +31 -18
  11. data/lib/bugsnag/configuration.rb +240 -22
  12. data/lib/bugsnag/delivery/synchronous.rb +2 -2
  13. data/lib/bugsnag/delivery/thread_queue.rb +2 -2
  14. data/lib/bugsnag/endpoint_configuration.rb +11 -0
  15. data/lib/bugsnag/endpoint_validator.rb +80 -0
  16. data/lib/bugsnag/error.rb +25 -0
  17. data/lib/bugsnag/event.rb +7 -0
  18. data/lib/bugsnag/feature_flag.rb +74 -0
  19. data/lib/bugsnag/integrations/rack.rb +3 -3
  20. data/lib/bugsnag/integrations/rails/active_job.rb +102 -0
  21. data/lib/bugsnag/integrations/railtie.rb +36 -3
  22. data/lib/bugsnag/integrations/resque.rb +17 -3
  23. data/lib/bugsnag/middleware/active_job.rb +18 -0
  24. data/lib/bugsnag/middleware/delayed_job.rb +21 -2
  25. data/lib/bugsnag/middleware/exception_meta_data.rb +2 -0
  26. data/lib/bugsnag/middleware/rack_request.rb +84 -19
  27. data/lib/bugsnag/middleware/rails3_request.rb +2 -2
  28. data/lib/bugsnag/middleware/rake.rb +1 -1
  29. data/lib/bugsnag/middleware/session_data.rb +3 -1
  30. data/lib/bugsnag/middleware/sidekiq.rb +1 -1
  31. data/lib/bugsnag/middleware/suggestion_data.rb +9 -7
  32. data/lib/bugsnag/report.rb +204 -8
  33. data/lib/bugsnag/session_tracker.rb +52 -12
  34. data/lib/bugsnag/stacktrace.rb +13 -2
  35. data/lib/bugsnag/tasks/bugsnag.rake +1 -1
  36. data/lib/bugsnag/utility/duplicator.rb +124 -0
  37. data/lib/bugsnag/utility/feature_data_store.rb +41 -0
  38. data/lib/bugsnag/utility/feature_flag_delegate.rb +89 -0
  39. data/lib/bugsnag/utility/metadata_delegate.rb +102 -0
  40. data/lib/bugsnag.rb +143 -5
  41. metadata +24 -7
@@ -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 URL error notifications will be delivered to
115
- # @return [String]
116
- attr_reader :notify_endpoint
117
- alias :endpoint :notify_endpoint
130
+ # The URLs to send events and sessions to
131
+ # @return [EndpointConfiguration]
132
+ attr_reader :endpoints
118
133
 
119
- # The URL session notifications will be delivered to
120
- # @return [String]
121
- attr_reader :session_endpoint
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
- # @see Bugsnag::Breadcrumbs::VALID_BREADCRUMB_TYPES
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
- # These are set exclusively using the "set_endpoints" method
202
- @notify_endpoint = DEFAULT_NOTIFY_ENDPOINT
203
- @session_endpoint = DEFAULT_SESSION_ENDPOINT
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 (see info)
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 (see info)
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 breadcrumb circular buffer
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] a thread based circular buffer containing breadcrumbs
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 {#set_endpoints} instead
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. The 'set_endpoints' method should be used instead")
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 {#set_endpoints} instead
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. The 'set_endpoints' method should be used instead")
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
- @notify_endpoint = new_notify_endpoint
467
- @session_endpoint = new_session_endpoint
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.warn("Notification to #{url} failed, #{e.inspect}")
22
- configuration.warn(e.backtrace)
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.warn("Notification to #{url} failed, #{e.inspect}")
35
- configuration.warn(e.backtrace)
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,11 @@
1
+ module Bugsnag
2
+ class EndpointConfiguration
3
+ attr_reader :notify
4
+ attr_reader :sessions
5
+
6
+ def initialize(notify, sessions)
7
+ @notify = notify
8
+ @sessions = sessions
9
+ end
10
+ end
11
+ end
@@ -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
@@ -0,0 +1,7 @@
1
+ require "bugsnag/report"
2
+
3
+ module Bugsnag
4
+ # For now Event is just an alias of Report. This points to the same object so
5
+ # any changes to Report will also affect Event
6
+ Event = Report
7
+ end
@@ -0,0 +1,74 @@
1
+ module Bugsnag
2
+ class FeatureFlag
3
+ # Get the name of this feature flag
4
+ #
5
+ # @return [String]
6
+ attr_reader :name
7
+
8
+ # Get the variant of this feature flag
9
+ #
10
+ # @return [String, nil]
11
+ attr_reader :variant
12
+
13
+ # @param name [String] The name of this feature flags
14
+ # @param variant [String, nil] An optional variant for this flag
15
+ def initialize(name, variant = nil)
16
+ @name = name
17
+ @variant = coerce_variant(variant)
18
+ end
19
+
20
+ def ==(other)
21
+ self.class == other.class && @name == other.name && @variant == other.variant
22
+ end
23
+
24
+ def hash
25
+ [@name, @variant].hash
26
+ end
27
+
28
+ # Convert this flag to a hash
29
+ #
30
+ # @example With no variant
31
+ # { "featureFlag" => "name" }
32
+ #
33
+ # @example With a variant
34
+ # { "featureFlag" => "name", "variant" => "variant" }
35
+ #
36
+ # @return [Hash{String => String}]
37
+ def to_h
38
+ if @variant.nil?
39
+ { "featureFlag" => @name }
40
+ else
41
+ { "featureFlag" => @name, "variant" => @variant }
42
+ end
43
+ end
44
+
45
+ # Check if this flag is valid, i.e. has a name that's a String and a variant
46
+ # that's either nil or a String
47
+ #
48
+ # @return [Boolean]
49
+ def valid?
50
+ @name.is_a?(String) &&
51
+ !@name.empty? &&
52
+ (@variant.nil? || @variant.is_a?(String))
53
+ end
54
+
55
+ private
56
+
57
+ # Coerce this variant into a valid value (String or nil)
58
+ #
59
+ # If the variant is not already a string or nil, we use #to_s to coerce it.
60
+ # If #to_s raises, the variant will be set to nil
61
+ #
62
+ # @param variant [Object]
63
+ # @return [String, nil]
64
+ def coerce_variant(variant)
65
+ if variant.nil? || variant.is_a?(String)
66
+ variant
67
+ else
68
+ variant.to_s
69
+ end
70
+ rescue StandardError
71
+ nil
72
+ end
73
+ end
74
+ end
@@ -25,9 +25,9 @@ module Bugsnag
25
25
  end
26
26
 
27
27
  # Hook up rack-based notification middlewares
28
- config.middleware.insert_before([Bugsnag::Middleware::Rails3Request,Bugsnag::Middleware::Callbacks], Bugsnag::Middleware::RackRequest) if defined?(::Rack)
29
- config.middleware.insert_before(Bugsnag::Middleware::Callbacks, Bugsnag::Middleware::WardenUser) if defined?(Warden)
30
- config.middleware.insert_before(Bugsnag::Middleware::Callbacks, Bugsnag::Middleware::ClearanceUser) if defined?(Clearance)
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