bugsnag 6.19.0 → 6.26.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.
Files changed (46) hide show
  1. checksums.yaml +4 -4
  2. data/.yardopts +1 -0
  3. data/CHANGELOG.md +176 -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 +243 -25
  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/mongo.rb +5 -3
  20. data/lib/bugsnag/integrations/rack.rb +3 -3
  21. data/lib/bugsnag/integrations/rails/active_job.rb +102 -0
  22. data/lib/bugsnag/integrations/rails/rails_breadcrumbs.rb +2 -0
  23. data/lib/bugsnag/integrations/railtie.rb +70 -27
  24. data/lib/bugsnag/integrations/resque.rb +17 -3
  25. data/lib/bugsnag/integrations/sidekiq.rb +1 -0
  26. data/lib/bugsnag/middleware/active_job.rb +18 -0
  27. data/lib/bugsnag/middleware/classify_error.rb +1 -0
  28. data/lib/bugsnag/middleware/delayed_job.rb +21 -2
  29. data/lib/bugsnag/middleware/exception_meta_data.rb +2 -0
  30. data/lib/bugsnag/middleware/rack_request.rb +84 -19
  31. data/lib/bugsnag/middleware/rails3_request.rb +2 -2
  32. data/lib/bugsnag/middleware/rake.rb +1 -1
  33. data/lib/bugsnag/middleware/session_data.rb +3 -1
  34. data/lib/bugsnag/middleware/sidekiq.rb +1 -1
  35. data/lib/bugsnag/middleware/suggestion_data.rb +9 -7
  36. data/lib/bugsnag/middleware_stack.rb +6 -6
  37. data/lib/bugsnag/report.rb +204 -8
  38. data/lib/bugsnag/session_tracker.rb +52 -12
  39. data/lib/bugsnag/stacktrace.rb +13 -2
  40. data/lib/bugsnag/tasks/bugsnag.rake +1 -1
  41. data/lib/bugsnag/utility/duplicator.rb +124 -0
  42. data/lib/bugsnag/utility/feature_data_store.rb +41 -0
  43. data/lib/bugsnag/utility/feature_flag_delegate.rb +89 -0
  44. data/lib/bugsnag/utility/metadata_delegate.rb +102 -0
  45. data/lib/bugsnag.rb +156 -8
  46. 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
  ##
@@ -485,7 +578,7 @@ module Bugsnag
485
578
  # Returning false from an on_error callback will cause the error to be ignored
486
579
  # and will prevent any remaining callbacks from being called
487
580
  #
488
- # @param callback [Proc]
581
+ # @param callback [Proc, Method, #call]
489
582
  # @return [void]
490
583
  def add_on_error(callback)
491
584
  middleware.use(callback)
@@ -494,15 +587,140 @@ module Bugsnag
494
587
  ##
495
588
  # Remove the given callback from the list of on_error callbacks
496
589
  #
497
- # Note that this must be the same Proc instance that was passed to
590
+ # Note that this must be the same instance that was passed to
498
591
  # {#add_on_error}, otherwise it will not be removed
499
592
  #
500
- # @param callback [Proc]
593
+ # @param callback [Proc, Method, #call]
501
594
  # @return [void]
502
595
  def remove_on_error(callback)
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
@@ -127,6 +127,8 @@ module Bugsnag
127
127
  end
128
128
  end
129
129
 
130
- ##
131
- # Add the subscriber to the global Mongo monitoring object
132
- Mongo::Monitoring::Global.subscribe(Mongo::Monitoring::COMMAND, Bugsnag::MongoBreadcrumbSubscriber.new)
130
+ if defined?(Mongo::Monitoring)
131
+ ##
132
+ # Add the subscriber to the global Mongo monitoring object
133
+ Mongo::Monitoring::Global.subscribe(Mongo::Monitoring::COMMAND, Bugsnag::MongoBreadcrumbSubscriber.new)
134
+ 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