bugsnag 6.22.1 → 6.23.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5a06ee3b5502be46d703f258c7bda2a5fca647fbe210095e989dd069af901a78
4
- data.tar.gz: 622e923de05d81c2d15fe65d738e7191e75b84230c0bbdcb8fed7c9f6831057c
3
+ metadata.gz: 763fc185ada81aaefdfb19edc7ca88788921c4577ec3e5a76addadcd8e13227c
4
+ data.tar.gz: 47661582b92e719b81f9023678dac8fcd558dd641ae124e0305b3f32157ccdd7
5
5
  SHA512:
6
- metadata.gz: 7ea25266b534f698108e3bc1cbbfeb81d6b528b77653ffe8edce45b7d6d0309faba4a43f0aeb946ace35b84b738cdd275e12cdc998abc7c2b20c02fa2ea07ed4
7
- data.tar.gz: 96a1fe53d1f2167cc3458e53ec178e6c8c25fbde85d1dca17a329da85f9ebde4e72416b5930f9ed595955c48e043e22d60915483e163d0f274a4292a06fa70d8
6
+ metadata.gz: 5de707cdf88e2c4d46cbfcd6fde2dad857c584c366788ea31dffa0b62c80d9a8dea9e8cf030306f70e793bff3999b92ae5c165309eb48d4fab2d77453160cd33
7
+ data.tar.gz: f23a0d06302491091fc15d209bd09e45f1a47017467f163e1ea36fd4702dbadf1c395e36fb71283261d78c505dc6f2459ad7a7da9052989f81200f6df3c0b7ba
data/CHANGELOG.md CHANGED
@@ -1,6 +1,57 @@
1
1
  Changelog
2
2
  =========
3
3
 
4
+ ## v6.23.0 (21 September 2021)
5
+
6
+ ### Enhancements
7
+
8
+ * Sessions will now be delivered every 10 seconds, instead of every 30 seconds
9
+ | [#680](https://github.com/bugsnag/bugsnag-ruby/pull/680)
10
+ * Log errors that prevent delivery at `ERROR` level
11
+ | [#681](https://github.com/bugsnag/bugsnag-ruby/pull/681)
12
+ * Add `on_breadcrumb` callbacks to replace `before_breadcrumb_callbacks`
13
+ | [#686](https://github.com/bugsnag/bugsnag-ruby/pull/686)
14
+ * Add `context` attribute to configuration, which will be used as the default context for events. Using this option will disable automatic context setting
15
+ | [#687](https://github.com/bugsnag/bugsnag-ruby/pull/687)
16
+ | [#688](https://github.com/bugsnag/bugsnag-ruby/pull/688)
17
+ * Add `Bugsnag#breadcrumbs` getter to fetch the current list of breadcrumbs
18
+ | [#689](https://github.com/bugsnag/bugsnag-ruby/pull/689)
19
+ * Add `time` (an ISO8601 string in UTC) to `device` metadata
20
+ | [#690](https://github.com/bugsnag/bugsnag-ruby/pull/690)
21
+ * Add `errors` to `Report`/`Event` containing an array of `Error` objects. The `Error` object contains `error_class`, `error_message`, `stacktrace` and `type` (always "ruby")
22
+ | [#691](https://github.com/bugsnag/bugsnag-ruby/pull/691)
23
+ * Add `original_error` to `Report`/`Event` containing the original Exception instance
24
+ | [#692](https://github.com/bugsnag/bugsnag-ruby/pull/692)
25
+ * Add `request` to `Report`/`Event` containing HTTP request metadata
26
+ | [#693](https://github.com/bugsnag/bugsnag-ruby/pull/693)
27
+ * Add `add_metadata` and `clear_metadata` to `Report`/`Event`
28
+ | [#694](https://github.com/bugsnag/bugsnag-ruby/pull/694)
29
+ * Add `set_user` to `Report`/`Event`
30
+ | [#695](https://github.com/bugsnag/bugsnag-ruby/pull/695)
31
+
32
+ ### Fixes
33
+
34
+ * Avoid starting session delivery thread when the current release stage is not enabled
35
+ | [#677](https://github.com/bugsnag/bugsnag-ruby/pull/677)
36
+
37
+ ### Deprecated
38
+
39
+ * `before_breadcrumb_callbacks` have been deprecated in favour of `on_breadcrumb` callbacks and will be removed in the next major release
40
+ * For consistency with Bugsnag notifiers for other languages, a number of methods have been deprecated in this release. The old options will be removed in the next major version | [#676](https://github.com/bugsnag/bugsnag-ruby/pull/676)
41
+ * The `notify_release_stages` configuration option has been deprecated in favour of `enabled_release_stages`
42
+ * The `auto_capture_sessions` and `track_sessions` configuration options have been deprecated in favour of `auto_track_sessions`
43
+ * The `enabled_automatic_breadcrumb_types` configuration option has been deprecated in favour of `enabled_breadcrumb_types`
44
+ * The `Report` class has been deprecated in favour of the `Event` class
45
+ * The `Report#meta_data` attribute has been deprecated in favour of `Event#metadata`
46
+ * The `Breadcrumb#meta_data` attribute has been deprecated in favour of `Breadcrumb#metadata`
47
+ * The `Breadcrumb#name` attribute has been deprecated in favour of `Breadcrumb#message`
48
+ * The breadcrumb type constants in the `Bugsnag::Breadcrumbs` module has been deprecated in favour of the constants available in the `Bugsnag::BreadcrumbType` module
49
+ For example, `Bugsnag::Breadcrumbs::ERROR_BREADCRUMB_TYPE` is now available as `Bugsnag::BreadcrumbType::ERROR`
50
+ * `Report#exceptions` has been deprecated in favour of the new `errors` property
51
+ * `Report#raw_exceptions` has been deprecated in favour of the new `original_error` property
52
+ * Accessing request data via `Report#metadata` has been deprecated in favour of using the new `request` property. Request data will be moved out of metadata in the next major version
53
+ * The `Report#add_tab` and `Report#remove_tab` methods have been deprecated in favour of the new `add_metadata` and `clear_metadata` methods
54
+
4
55
  ## v6.22.1 (11 August 2021)
5
56
 
6
57
  ### Fixes
data/VERSION CHANGED
@@ -1 +1 @@
1
- 6.22.1
1
+ 6.23.0
@@ -0,0 +1,14 @@
1
+ require "bugsnag/breadcrumbs/breadcrumbs"
2
+
3
+ module Bugsnag
4
+ module BreadcrumbType
5
+ ERROR = Bugsnag::Breadcrumbs::ERROR_BREADCRUMB_TYPE
6
+ LOG = Bugsnag::Breadcrumbs::LOG_BREADCRUMB_TYPE
7
+ MANUAL = Bugsnag::Breadcrumbs::MANUAL_BREADCRUMB_TYPE
8
+ NAVIGATION = Bugsnag::Breadcrumbs::NAVIGATION_BREADCRUMB_TYPE
9
+ PROCESS = Bugsnag::Breadcrumbs::PROCESS_BREADCRUMB_TYPE
10
+ REQUEST = Bugsnag::Breadcrumbs::REQUEST_BREADCRUMB_TYPE
11
+ STATE = Bugsnag::Breadcrumbs::STATE_BREADCRUMB_TYPE
12
+ USER = Bugsnag::Breadcrumbs::USER_BREADCRUMB_TYPE
13
+ end
14
+ end
@@ -1,11 +1,13 @@
1
1
  module Bugsnag::Breadcrumbs
2
2
  class Breadcrumb
3
+ # @deprecated Use {#message} instead
3
4
  # @return [String] the breadcrumb name
4
5
  attr_accessor :name
5
6
 
6
7
  # @return [String] the breadcrumb type
7
8
  attr_accessor :type
8
9
 
10
+ # @deprecated Use {#metadata} instead
9
11
  # @return [Hash, nil] metadata hash containing strings, numbers, or booleans, or nil
10
12
  attr_accessor :meta_data
11
13
 
@@ -23,7 +25,7 @@ module Bugsnag::Breadcrumbs
23
25
  # @api private
24
26
  #
25
27
  # @param name [String] the breadcrumb name
26
- # @param type [String] the breadcrumb type from Bugsnag::Breadcrumbs::VALID_BREADCRUMB_TYPES
28
+ # @param type [String] the breadcrumb type from Bugsnag::BreadcrumbType
27
29
  # @param meta_data [Hash, nil] a hash containing strings, numbers, or booleans, or nil
28
30
  # @param auto [Symbol] set to `:auto` if the breadcrumb is automatically generated
29
31
  def initialize(name, type, meta_data, auto)
@@ -72,5 +74,36 @@ module Bugsnag::Breadcrumbs
72
74
  :timestamp => @timestamp.iso8601(3)
73
75
  }
74
76
  end
77
+
78
+ # TODO: "message" and "metadata" can be simple attr_accessors when they
79
+ # replace "name" and "meta_data"
80
+ # NOTE: these are not aliases as YARD doesn't allow documenting the non-alias
81
+ # as deprecated without also marking the alias as deprecated
82
+
83
+ # The breadcrumb message
84
+ # @!attribute message
85
+ # @return [String]
86
+ def message
87
+ @name
88
+ end
89
+
90
+ # @param message [String]
91
+ # @return [void]
92
+ def message=(message)
93
+ @name = message
94
+ end
95
+
96
+ # A Hash containing arbitrary metadata associated with this breadcrumb
97
+ # @!attribute metadata
98
+ # @return [Hash, nil]
99
+ def metadata
100
+ @meta_data
101
+ end
102
+
103
+ # @param metadata [Hash, nil]
104
+ # @return [void]
105
+ def metadata=(metadata)
106
+ @meta_data = metadata
107
+ end
75
108
  end
76
109
  end
@@ -1,4 +1,5 @@
1
1
  module Bugsnag::Breadcrumbs
2
+ # @deprecated Use {Bugsnag::BreadcrumbType} instead
2
3
  VALID_BREADCRUMB_TYPES = [
3
4
  ERROR_BREADCRUMB_TYPE = "error",
4
5
  MANUAL_BREADCRUMB_TYPE = "manual",
@@ -0,0 +1,50 @@
1
+ require "set"
2
+
3
+ module Bugsnag::Breadcrumbs
4
+ class OnBreadcrumbCallbackList
5
+ def initialize(configuration)
6
+ @callbacks = Set.new
7
+ @mutex = Mutex.new
8
+ @configuration = configuration
9
+ end
10
+
11
+ ##
12
+ # @param callback [Proc, Method, #call]
13
+ # @return [void]
14
+ def add(callback)
15
+ @mutex.synchronize do
16
+ @callbacks.add(callback)
17
+ end
18
+ end
19
+
20
+ ##
21
+ # @param callback [Proc, Method, #call]
22
+ # @return [void]
23
+ def remove(callback)
24
+ @mutex.synchronize do
25
+ @callbacks.delete(callback)
26
+ end
27
+ end
28
+
29
+ ##
30
+ # @param breadcrumb [Breadcrumb]
31
+ # @return [void]
32
+ def call(breadcrumb)
33
+ @callbacks.each do |callback|
34
+ begin
35
+ should_continue = callback.call(breadcrumb)
36
+ rescue StandardError => e
37
+ @configuration.warn("Error occurred in on_breadcrumb callback: '#{e}'")
38
+ @configuration.warn("on_breadcrumb callback stacktrace: #{e.backtrace.inspect}")
39
+ end
40
+
41
+ # only stop if should_continue is explicity 'false' to allow callbacks
42
+ # to return 'nil'
43
+ if should_continue == false
44
+ breadcrumb.ignore!
45
+ break
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
@@ -12,6 +12,7 @@ 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"
15
16
 
16
17
  module Bugsnag
17
18
  class Configuration
@@ -24,6 +25,7 @@ module Bugsnag
24
25
  attr_accessor :release_stage
25
26
 
26
27
  # A list of which release stages should cause notifications to be sent
28
+ # @deprecated Use {#enabled_release_stages} instead
27
29
  # @return [Array<String>, nil]
28
30
  attr_accessor :notify_release_stages
29
31
 
@@ -104,6 +106,7 @@ module Bugsnag
104
106
  attr_accessor :discard_classes
105
107
 
106
108
  # Whether Bugsnag should automatically record sessions
109
+ # @deprecated Use {#auto_track_sessions} instead
107
110
  # @return [Boolean]
108
111
  attr_accessor :auto_capture_sessions
109
112
 
@@ -125,7 +128,8 @@ module Bugsnag
125
128
  attr_reader :enable_sessions
126
129
 
127
130
  # A list of strings indicating allowable automatic breadcrumb types
128
- # @see Bugsnag::Breadcrumbs::VALID_BREADCRUMB_TYPES
131
+ # @deprecated Use {#enabled_breadcrumb_types} instead
132
+ # @see Bugsnag::BreadcrumbType
129
133
  # @return [Array<String>]
130
134
  attr_accessor :enabled_automatic_breadcrumb_types
131
135
 
@@ -141,10 +145,20 @@ module Bugsnag
141
145
  # @return [Regexp]
142
146
  attr_accessor :vendor_path
143
147
 
148
+ # The default context for all future events
149
+ # Setting this will disable automatic context setting
150
+ # @return [String, nil]
151
+ attr_accessor :context
152
+
144
153
  # @api private
145
154
  # @return [Array<String>]
146
155
  attr_reader :scopes_to_filter
147
156
 
157
+ # Expose on_breadcrumb_callbacks internally for Bugsnag.leave_breadcrumb
158
+ # @api private
159
+ # @return [Breadcrumbs::OnBreadcrumbCallbackList]
160
+ attr_reader :on_breadcrumb_callbacks
161
+
148
162
  API_KEY_REGEX = /[0-9a-f]{32}/i
149
163
  THREAD_LOCAL_NAME = "bugsnag_req_data"
150
164
 
@@ -193,6 +207,7 @@ module Bugsnag
193
207
  # All valid breadcrumb types should be allowable initially
194
208
  self.enabled_automatic_breadcrumb_types = Bugsnag::Breadcrumbs::VALID_BREADCRUMB_TYPES.dup
195
209
  self.before_breadcrumb_callbacks = []
210
+ @on_breadcrumb_callbacks = Breadcrumbs::OnBreadcrumbCallbackList.new(self)
196
211
 
197
212
  # Store max_breadcrumbs here instead of outputting breadcrumbs.max_items
198
213
  # to avoid infinite recursion when creating breadcrumb buffer
@@ -389,15 +404,23 @@ module Bugsnag
389
404
  ##
390
405
  # Logs a warning level message
391
406
  #
392
- # @param (see info)
407
+ # @param message [String, #to_s] The message to log
393
408
  def warn(message)
394
409
  logger.warn(PROG_NAME) { message }
395
410
  end
396
411
 
412
+ ##
413
+ # Logs an error level message
414
+ #
415
+ # @param message [String, #to_s] The message to log
416
+ def error(message)
417
+ logger.error(PROG_NAME) { message }
418
+ end
419
+
397
420
  ##
398
421
  # Logs a debug level message
399
422
  #
400
- # @param (see info)
423
+ # @param message [String, #to_s] The message to log
401
424
  def debug(message)
402
425
  logger.debug(PROG_NAME) { message }
403
426
  end
@@ -426,9 +449,12 @@ module Bugsnag
426
449
  end
427
450
 
428
451
  ##
429
- # Returns the breadcrumb circular buffer
452
+ # Returns the current list of breadcrumbs
430
453
  #
431
- # @return [Bugsnag::Utility::CircularBuffer] a thread based circular buffer containing breadcrumbs
454
+ # This is a per-thread circular buffer, containing at most 'max_breadcrumbs'
455
+ # breadcrumbs
456
+ #
457
+ # @return [Bugsnag::Utility::CircularBuffer]
432
458
  def breadcrumbs
433
459
  request_data[:breadcrumbs] ||= Bugsnag::Utility::CircularBuffer.new(@max_breadcrumbs)
434
460
  end
@@ -503,6 +529,90 @@ module Bugsnag
503
529
  middleware.remove(callback)
504
530
  end
505
531
 
532
+ ##
533
+ # Add the given callback to the list of on_breadcrumb callbacks
534
+ #
535
+ # The on_breadcrumb callbacks will be called when a breadcrumb is left and
536
+ # are passed the {Breadcrumbs::Breadcrumb Breadcrumb} object
537
+ #
538
+ # Returning false from an on_breadcrumb callback will cause the breadcrumb
539
+ # to be ignored and will prevent any remaining callbacks from being called
540
+ #
541
+ # @param callback [Proc, Method, #call]
542
+ # @return [void]
543
+ def add_on_breadcrumb(callback)
544
+ @on_breadcrumb_callbacks.add(callback)
545
+ end
546
+
547
+ ##
548
+ # Remove the given callback from the list of on_breadcrumb callbacks
549
+ #
550
+ # Note that this must be the same instance that was passed to
551
+ # {add_on_breadcrumb}, otherwise it will not be removed
552
+ #
553
+ # @param callback [Proc, Method, #call]
554
+ # @return [void]
555
+ def remove_on_breadcrumb(callback)
556
+ @on_breadcrumb_callbacks.remove(callback)
557
+ end
558
+
559
+ ##
560
+ # Has the context been explicitly set?
561
+ #
562
+ # This is necessary to differentiate between the context not being set and
563
+ # the context being set to 'nil' explicitly
564
+ #
565
+ # @api private
566
+ # @return [Boolean]
567
+ def context_set?
568
+ defined?(@context) != nil
569
+ end
570
+
571
+ # TODO: These methods can be a simple attr_accessor when they replace the
572
+ # methods they are aliasing
573
+ # NOTE: they are not aliases as YARD doesn't allow documenting the non-alias
574
+ # as deprecated without also marking the alias as deprecated
575
+
576
+ # A list of which release stages should cause notifications to be sent
577
+ # @!attribute enabled_release_stages
578
+ # @return [Array<String>, nil]
579
+ def enabled_release_stages
580
+ @notify_release_stages
581
+ end
582
+
583
+ # @param release_stages [Array<String>, nil]
584
+ # @return [void]
585
+ def enabled_release_stages=(release_stages)
586
+ @notify_release_stages = release_stages
587
+ end
588
+
589
+ # A list of breadcrumb types that Bugsnag will collect automatically
590
+ # @!attribute enabled_breadcrumb_types
591
+ # @see Bugsnag::BreadcrumbType
592
+ # @return [Array<String>]
593
+ def enabled_breadcrumb_types
594
+ @enabled_automatic_breadcrumb_types
595
+ end
596
+
597
+ # @param breadcrumb_types [Array<String>]
598
+ # @return [void]
599
+ def enabled_breadcrumb_types=(breadcrumb_types)
600
+ @enabled_automatic_breadcrumb_types = breadcrumb_types
601
+ end
602
+
603
+ # Whether sessions should be tracked automatically
604
+ # @!attribute auto_track_sessions
605
+ # @return [Boolean]
606
+ def auto_track_sessions
607
+ @auto_capture_sessions
608
+ end
609
+
610
+ # @param track_sessions [Boolean]
611
+ # @return [void]
612
+ def auto_track_sessions=(track_sessions)
613
+ @auto_capture_sessions = track_sessions
614
+ end
615
+
506
616
  private
507
617
 
508
618
  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,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
@@ -56,7 +56,7 @@ module Bugsnag
56
56
 
57
57
  context = "#{class_name}@#{queue}"
58
58
  report.meta_data.merge!({ context: context, payload: metadata })
59
- report.context = context
59
+ report.automatic_context = context
60
60
  end
61
61
  end
62
62
  end
@@ -9,7 +9,7 @@ module Bugsnag::Middleware
9
9
 
10
10
  if data
11
11
  report.add_tab(:active_job, data)
12
- report.context = "#{data[:job_name]}@#{data[:queue]}"
12
+ report.automatic_context = "#{data[:job_name]}@#{data[:queue]}"
13
13
  end
14
14
 
15
15
  @bugsnag.call(report)
@@ -30,7 +30,7 @@ module Bugsnag::Middleware
30
30
  payload_data = construct_job_payload(job.payload_object)
31
31
 
32
32
  context = get_context(payload_data, job_data[:active_job])
33
- report.context = context unless context.nil?
33
+ report.automatic_context = context unless context.nil?
34
34
 
35
35
  job_data[:payload] = payload_data
36
36
  end
@@ -16,6 +16,8 @@ module Bugsnag::Middleware
16
16
 
17
17
  if exception.respond_to?(:bugsnag_context)
18
18
  context = exception.bugsnag_context
19
+ # note: this should set 'context' not 'automatic_context' as it's a
20
+ # user-supplied value
19
21
  report.context = context if context.is_a?(String)
20
22
  end
21
23
 
@@ -18,8 +18,8 @@ module Bugsnag::Middleware
18
18
  client_ip = request.ip.to_s rescue SPOOF
19
19
  session = env["rack.session"]
20
20
 
21
- # Set the context
22
- report.context = "#{request.request_method} #{request.path}"
21
+ # Set the automatic context
22
+ report.automatic_context = "#{request.request_method} #{request.path}"
23
23
 
24
24
  # Set a sensible default for user_id
25
25
  report.user["id"] = request.ip
@@ -15,8 +15,8 @@ module Bugsnag::Middleware
15
15
  client_ip = env["action_dispatch.remote_ip"].to_s rescue SPOOF
16
16
 
17
17
  if params
18
- # Set the context
19
- report.context = "#{params[:controller]}##{params[:action]}"
18
+ # Set the automatic context
19
+ report.automatic_context = "#{params[:controller]}##{params[:action]}"
20
20
 
21
21
  # Augment the request tab
22
22
  report.add_tab(:request, {
@@ -16,7 +16,7 @@ module Bugsnag::Middleware
16
16
  :arguments => task.arg_description
17
17
  })
18
18
 
19
- report.context ||= task.name
19
+ report.automatic_context ||= task.name
20
20
  end
21
21
 
22
22
  @bugsnag.call(report)
@@ -10,7 +10,7 @@ module Bugsnag::Middleware
10
10
  sidekiq = report.request_data[:sidekiq]
11
11
  if sidekiq
12
12
  report.add_tab(:sidekiq, sidekiq)
13
- report.context ||= "#{sidekiq[:msg]['wrapped'] || sidekiq[:msg]['class']}@#{sidekiq[:msg]['queue']}"
13
+ report.automatic_context ||= "#{sidekiq[:msg]['wrapped'] || sidekiq[:msg]['class']}@#{sidekiq[:msg]['queue']}"
14
14
  end
15
15
  @bugsnag.call(report)
16
16
  end
@@ -1,8 +1,10 @@
1
1
  require "json"
2
2
  require "pathname"
3
+ require "bugsnag/error"
3
4
  require "bugsnag/stacktrace"
4
5
 
5
6
  module Bugsnag
7
+ # rubocop:todo Metrics/ClassLength
6
8
  class Report
7
9
  NOTIFIER_NAME = "Ruby Bugsnag Notifier"
8
10
  NOTIFIER_VERSION = Bugsnag::VERSION
@@ -45,16 +47,13 @@ module Bugsnag
45
47
  # @return [Configuration]
46
48
  attr_accessor :configuration
47
49
 
48
- # Additional context for this report
49
- # @return [String, nil]
50
- attr_accessor :context
51
-
52
50
  # The delivery method that will be used for this report
53
51
  # @see Configuration#delivery_method
54
52
  # @return [Symbol]
55
53
  attr_accessor :delivery_method
56
54
 
57
55
  # The list of exceptions in this report
56
+ # @deprecated Use {#errors} instead
58
57
  # @return [Array<Hash>]
59
58
  attr_accessor :exceptions
60
59
 
@@ -72,10 +71,12 @@ module Bugsnag
72
71
  attr_accessor :grouping_hash
73
72
 
74
73
  # Arbitrary metadata attached to this report
74
+ # @deprecated Use {#metadata} instead
75
75
  # @return [Hash]
76
76
  attr_accessor :meta_data
77
77
 
78
78
  # The raw Exception instances for this report
79
+ # @deprecated Use {#original_error} instead
79
80
  # @see #exceptions
80
81
  # @return [Array<Exception>]
81
82
  attr_accessor :raw_exceptions
@@ -102,21 +103,35 @@ module Bugsnag
102
103
  # @return [Hash]
103
104
  attr_accessor :user
104
105
 
106
+ # A list of errors in this report
107
+ # @return [Array<Error>]
108
+ attr_reader :errors
109
+
110
+ # The Exception instance this report was created for
111
+ # @return [Exception]
112
+ attr_reader :original_error
113
+
105
114
  ##
106
115
  # Initializes a new report from an exception.
107
116
  def initialize(exception, passed_configuration, auto_notify=false)
117
+ # store the creation time for use as device.time
118
+ @created_at = Time.now.utc.iso8601(3)
119
+
108
120
  @should_ignore = false
109
121
  @unhandled = auto_notify
110
122
 
111
123
  self.configuration = passed_configuration
112
124
 
125
+ @original_error = exception
113
126
  self.raw_exceptions = generate_raw_exceptions(exception)
114
127
  self.exceptions = generate_exception_list
128
+ @errors = generate_error_list
115
129
 
116
130
  self.api_key = configuration.api_key
117
131
  self.app_type = configuration.app_type
118
132
  self.app_version = configuration.app_version
119
133
  self.breadcrumbs = []
134
+ self.context = configuration.context if configuration.context_set?
120
135
  self.delivery_method = configuration.delivery_method
121
136
  self.hostname = configuration.hostname
122
137
  self.runtime_versions = configuration.runtime_versions.dup
@@ -125,8 +140,29 @@ module Bugsnag
125
140
  self.severity = auto_notify ? "error" : "warning"
126
141
  self.severity_reason = auto_notify ? {:type => UNHANDLED_EXCEPTION} : {:type => HANDLED_EXCEPTION}
127
142
  self.user = {}
143
+
144
+ @metadata_delegate = Utility::MetadataDelegate.new
128
145
  end
129
146
 
147
+ ##
148
+ # Additional context for this report
149
+ # @!attribute context
150
+ # @return [String, nil]
151
+ def context
152
+ return @context if defined?(@context)
153
+
154
+ @automatic_context
155
+ end
156
+
157
+ attr_writer :context
158
+
159
+ ##
160
+ # Context set automatically by Bugsnag uses this attribute, which prevents
161
+ # it from overwriting the user-supplied context
162
+ # @api private
163
+ # @return [String, nil]
164
+ attr_accessor :automatic_context
165
+
130
166
  ##
131
167
  # Add a new metadata tab to this notification.
132
168
  #
@@ -135,6 +171,8 @@ module Bugsnag
135
171
  # exists, this will be merged with the existing values. If a Hash is not
136
172
  # given, the value will be placed into the 'custom' tab
137
173
  # @return [void]
174
+ #
175
+ # @deprecated Use {#add_metadata} instead
138
176
  def add_tab(name, value)
139
177
  return if name.nil?
140
178
 
@@ -153,6 +191,8 @@ module Bugsnag
153
191
  #
154
192
  # @param name [String]
155
193
  # @return [void]
194
+ #
195
+ # @deprecated Use {#clear_metadata} instead
156
196
  def remove_tab(name)
157
197
  return if name.nil?
158
198
 
@@ -175,7 +215,8 @@ module Bugsnag
175
215
  context: context,
176
216
  device: {
177
217
  hostname: hostname,
178
- runtimeVersions: runtime_versions
218
+ runtimeVersions: runtime_versions,
219
+ time: @created_at
179
220
  },
180
221
  exceptions: exceptions,
181
222
  groupingHash: grouping_hash,
@@ -257,6 +298,82 @@ module Bugsnag
257
298
  end
258
299
  end
259
300
 
301
+ # A Hash containing arbitrary metadata
302
+ # @!attribute metadata
303
+ # @return [Hash]
304
+ def metadata
305
+ @meta_data
306
+ end
307
+
308
+ # @param metadata [Hash]
309
+ # @return [void]
310
+ def metadata=(metadata)
311
+ @meta_data = metadata
312
+ end
313
+
314
+ ##
315
+ # Data from the current HTTP request. May be nil if no data has been recorded
316
+ #
317
+ # @return [Hash, nil]
318
+ def request
319
+ @meta_data[:request]
320
+ end
321
+
322
+ ##
323
+ # Add values to metadata
324
+ #
325
+ # @overload add_metadata(section, data)
326
+ # Merges data into the given section of metadata
327
+ # @param section [String, Symbol]
328
+ # @param data [Hash]
329
+ #
330
+ # @overload add_metadata(section, key, value)
331
+ # Sets key to value in the given section of metadata. If the value is nil
332
+ # the key will be deleted
333
+ # @param section [String, Symbol]
334
+ # @param key [String, Symbol]
335
+ # @param value
336
+ #
337
+ # @return [void]
338
+ def add_metadata(section, key_or_data, *args)
339
+ @metadata_delegate.add_metadata(@meta_data, section, key_or_data, *args)
340
+ end
341
+
342
+ ##
343
+ # Clear values from metadata
344
+ #
345
+ # @overload clear_metadata(section)
346
+ # Clears the given section of metadata
347
+ # @param section [String, Symbol]
348
+ #
349
+ # @overload clear_metadata(section, key)
350
+ # Clears the key in the given section of metadata
351
+ # @param section [String, Symbol]
352
+ # @param key [String, Symbol]
353
+ #
354
+ # @return [void]
355
+ def clear_metadata(section, *args)
356
+ @metadata_delegate.clear_metadata(@meta_data, section, *args)
357
+ end
358
+
359
+ ##
360
+ # Set information about the current user
361
+ #
362
+ # Additional user fields can be added as metadata in a "user" section
363
+ #
364
+ # Setting a field to 'nil' will remove it from the user data
365
+ #
366
+ # @param id [String, nil]
367
+ # @param email [String, nil]
368
+ # @param name [String, nil]
369
+ # @return [void]
370
+ def set_user(id = nil, email = nil, name = nil)
371
+ new_user = { id: id, email: email, name: name }
372
+ new_user.reject! { |key, value| value.nil? }
373
+
374
+ @user = new_user
375
+ end
376
+
260
377
  private
261
378
 
262
379
  def generate_exception_list
@@ -269,6 +386,12 @@ module Bugsnag
269
386
  end
270
387
  end
271
388
 
389
+ def generate_error_list
390
+ exceptions.map do |exception|
391
+ Error.new(exception[:errorClass], exception[:message], exception[:stacktrace])
392
+ end
393
+ end
394
+
272
395
  def error_class(exception)
273
396
  # The "Class" check is for some strange exceptions like Timeout::Error
274
397
  # which throw the error class instead of an instance
@@ -311,4 +434,5 @@ module Bugsnag
311
434
  exceptions
312
435
  end
313
436
  end
437
+ # rubocop:enable Metrics/ClassLength
314
438
  end
@@ -35,7 +35,8 @@ module Bugsnag
35
35
  #
36
36
  # This allows Bugsnag to track error rates for a release.
37
37
  def start_session
38
- return unless Bugsnag.configuration.enable_sessions
38
+ return unless Bugsnag.configuration.enable_sessions && Bugsnag.configuration.should_notify_release_stage?
39
+
39
40
  start_delivery_thread
40
41
  start_time = Time.now().utc().strftime('%Y-%m-%dT%H:%M:00')
41
42
  new_session = {
@@ -83,7 +84,7 @@ module Bugsnag
83
84
  end
84
85
  end
85
86
  end
86
- @delivery_thread = Concurrent::TimerTask.execute(execution_interval: 30) do
87
+ @delivery_thread = Concurrent::TimerTask.execute(execution_interval: 10) do
87
88
  if @session_counts.size > 0
88
89
  send_sessions
89
90
  end
@@ -106,11 +107,6 @@ module Bugsnag
106
107
  return
107
108
  end
108
109
 
109
- if !Bugsnag.configuration.should_notify_release_stage?
110
- Bugsnag.configuration.debug("Not delivering sessions due to notify_release_stages :#{Bugsnag.configuration.notify_release_stages.inspect}")
111
- return
112
- end
113
-
114
110
  body = {
115
111
  :notifier => {
116
112
  :name => Bugsnag::Report::NOTIFIER_NAME,
@@ -7,7 +7,7 @@ namespace :bugsnag do
7
7
  raise RuntimeError.new("Bugsnag test exception")
8
8
  rescue => e
9
9
  Bugsnag.notify(e) do |report|
10
- report.context = "rake#test_exception"
10
+ report.automatic_context = "rake#test_exception"
11
11
  end
12
12
  end
13
13
  end
@@ -0,0 +1,102 @@
1
+ module Bugsnag::Utility
2
+ # @api private
3
+ class MetadataDelegate
4
+ # nil is a valid metadata value, so we need a sentinel object so we can tell
5
+ # if the value parameter has been provided
6
+ NOT_PROVIDED = Object.new
7
+
8
+ ##
9
+ # Add values to metadata
10
+ #
11
+ # @overload add_metadata(metadata, section, data)
12
+ # Merges data into the given section of metadata
13
+ # @param metadata [Hash] The metadata hash to operate on
14
+ # @param section [String, Symbol]
15
+ # @param data [Hash]
16
+ #
17
+ # @overload add_metadata(metadata, section, key, value)
18
+ # Sets key to value in the given section of metadata. If the value is nil
19
+ # the key will be deleted
20
+ # @param metadata [Hash] The metadata hash to operate on
21
+ # @param section [String, Symbol]
22
+ # @param key [String, Symbol]
23
+ # @param value
24
+ #
25
+ # @return [void]
26
+ def add_metadata(metadata, section, key_or_data, value = NOT_PROVIDED)
27
+ case value
28
+ when NOT_PROVIDED
29
+ merge_metadata(metadata, section, key_or_data)
30
+ when nil
31
+ clear_metadata(metadata, section, key_or_data)
32
+ else
33
+ overwrite_metadata(metadata, section, key_or_data, value)
34
+ end
35
+ end
36
+
37
+ ##
38
+ # Clear values from metadata
39
+ #
40
+ # @overload clear_metadata(metadata, section)
41
+ # Clears the given section of metadata
42
+ # @param metadata [Hash] The metadata hash to operate on
43
+ # @param section [String, Symbol]
44
+ #
45
+ # @overload clear_metadata(metadata, section, key)
46
+ # Clears the key in the given section of metadata
47
+ # @param metadata [Hash] The metadata hash to operate on
48
+ # @param section [String, Symbol]
49
+ # @param key [String, Symbol]
50
+ #
51
+ # @return [void]
52
+ def clear_metadata(metadata, section, key = nil)
53
+ if key.nil?
54
+ metadata.delete(section)
55
+ elsif metadata[section]
56
+ metadata[section].delete(key)
57
+ end
58
+ end
59
+
60
+ private
61
+
62
+ ##
63
+ # Merge new metadata into the existing metadata
64
+ #
65
+ # Any keys with a 'nil' value in the new metadata will be deleted from the
66
+ # existing metadata
67
+ #
68
+ # @param existing_metadata [Hash]
69
+ # @param section [String, Symbol]
70
+ # @param new_metadata [Hash]
71
+ # @return [void]
72
+ def merge_metadata(existing_metadata, section, new_metadata)
73
+ return unless new_metadata.is_a?(Hash)
74
+
75
+ existing_metadata[section] ||= {}
76
+ data = existing_metadata[section]
77
+
78
+ new_metadata.each do |key, value|
79
+ if value.nil?
80
+ data.delete(key)
81
+ else
82
+ data[key] = value
83
+ end
84
+ end
85
+ end
86
+
87
+ ##
88
+ # Overwrite the value in metadata's section & key
89
+ #
90
+ # @param metadata [Hash]
91
+ # @param section [String, Symbol]
92
+ # @param key [String, Symbol]
93
+ # @param value
94
+ # @return [void]
95
+ def overwrite_metadata(metadata, section, key, value)
96
+ return unless key.is_a?(String) || key.is_a?(Symbol)
97
+
98
+ metadata[section] ||= {}
99
+ metadata[section][key] = value
100
+ end
101
+ end
102
+ end
data/lib/bugsnag.rb CHANGED
@@ -5,6 +5,7 @@ require "bugsnag/version"
5
5
  require "bugsnag/configuration"
6
6
  require "bugsnag/meta_data"
7
7
  require "bugsnag/report"
8
+ require "bugsnag/event"
8
9
  require "bugsnag/cleaner"
9
10
  require "bugsnag/helpers"
10
11
  require "bugsnag/session_tracker"
@@ -28,10 +29,13 @@ require "bugsnag/middleware/rake"
28
29
  require "bugsnag/middleware/classify_error"
29
30
  require "bugsnag/middleware/delayed_job"
30
31
 
32
+ require "bugsnag/breadcrumb_type"
31
33
  require "bugsnag/breadcrumbs/validator"
32
34
  require "bugsnag/breadcrumbs/breadcrumb"
33
35
  require "bugsnag/breadcrumbs/breadcrumbs"
34
36
 
37
+ require "bugsnag/utility/metadata_delegate"
38
+
35
39
  # rubocop:todo Metrics/ModuleLength
36
40
  module Bugsnag
37
41
  LOCK = Mutex.new
@@ -237,7 +241,7 @@ module Bugsnag
237
241
  #
238
242
  # @param name [String] the main breadcrumb name/message
239
243
  # @param meta_data [Hash] String, Numeric, or Boolean meta data to attach
240
- # @param type [String] the breadcrumb type, from Bugsnag::Breadcrumbs::VALID_BREADCRUMB_TYPES
244
+ # @param type [String] the breadcrumb type, see {Bugsnag::BreadcrumbType}
241
245
  # @param auto [Symbol] set to :auto if the breadcrumb is automatically created
242
246
  # @return [void]
243
247
  def leave_breadcrumb(name, meta_data={}, type=Bugsnag::Breadcrumbs::MANUAL_BREADCRUMB_TYPE, auto=:manual)
@@ -250,7 +254,7 @@ module Bugsnag
250
254
  # Skip if it's already invalid
251
255
  return if breadcrumb.ignore?
252
256
 
253
- # Run callbacks
257
+ # Run before_breadcrumb_callbacks
254
258
  configuration.before_breadcrumb_callbacks.each do |c|
255
259
  c.arity > 0 ? c.call(breadcrumb) : c.call
256
260
  break if breadcrumb.ignore?
@@ -259,6 +263,10 @@ module Bugsnag
259
263
  # Return early if ignored
260
264
  return if breadcrumb.ignore?
261
265
 
266
+ # Run on_breadcrumb callbacks
267
+ configuration.on_breadcrumb_callbacks.call(breadcrumb)
268
+ return if breadcrumb.ignore?
269
+
262
270
  # Validate again in case of callback alteration
263
271
  validator.validate(breadcrumb)
264
272
 
@@ -293,6 +301,44 @@ module Bugsnag
293
301
  configuration.remove_on_error(callback)
294
302
  end
295
303
 
304
+ ##
305
+ # Add the given callback to the list of on_breadcrumb callbacks
306
+ #
307
+ # The on_breadcrumb callbacks will be called when a breadcrumb is left and
308
+ # are passed the {Breadcrumbs::Breadcrumb Breadcrumb} object
309
+ #
310
+ # Returning false from an on_breadcrumb callback will cause the breadcrumb
311
+ # to be ignored and will prevent any remaining callbacks from being called
312
+ #
313
+ # @param callback [Proc, Method, #call]
314
+ # @return [void]
315
+ def add_on_breadcrumb(callback)
316
+ configuration.add_on_breadcrumb(callback)
317
+ end
318
+
319
+ ##
320
+ # Remove the given callback from the list of on_breadcrumb callbacks
321
+ #
322
+ # Note that this must be the same instance that was passed to
323
+ # {add_on_breadcrumb}, otherwise it will not be removed
324
+ #
325
+ # @param callback [Proc, Method, #call]
326
+ # @return [void]
327
+ def remove_on_breadcrumb(callback)
328
+ configuration.remove_on_breadcrumb(callback)
329
+ end
330
+
331
+ ##
332
+ # Returns the current list of breadcrumbs
333
+ #
334
+ # This is a per-thread circular buffer, containing at most 'max_breadcrumbs'
335
+ # breadcrumbs
336
+ #
337
+ # @return [Bugsnag::Utility::CircularBuffer]
338
+ def breadcrumbs
339
+ configuration.breadcrumbs
340
+ end
341
+
296
342
  ##
297
343
  # Returns the client's Cleaner object, or creates one if not yet created.
298
344
  #
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: bugsnag
3
3
  version: !ruby/object:Gem::Version
4
- version: 6.22.1
4
+ version: 6.23.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - James Smith
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-08-11 00:00:00.000000000 Z
11
+ date: 2021-09-21 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: concurrent-ruby
@@ -40,8 +40,10 @@ files:
40
40
  - VERSION
41
41
  - bugsnag.gemspec
42
42
  - lib/bugsnag.rb
43
+ - lib/bugsnag/breadcrumb_type.rb
43
44
  - lib/bugsnag/breadcrumbs/breadcrumb.rb
44
45
  - lib/bugsnag/breadcrumbs/breadcrumbs.rb
46
+ - lib/bugsnag/breadcrumbs/on_breadcrumb_callback_list.rb
45
47
  - lib/bugsnag/breadcrumbs/validator.rb
46
48
  - lib/bugsnag/cleaner.rb
47
49
  - lib/bugsnag/code_extractor.rb
@@ -49,6 +51,8 @@ files:
49
51
  - lib/bugsnag/delivery.rb
50
52
  - lib/bugsnag/delivery/synchronous.rb
51
53
  - lib/bugsnag/delivery/thread_queue.rb
54
+ - lib/bugsnag/error.rb
55
+ - lib/bugsnag/event.rb
52
56
  - lib/bugsnag/helpers.rb
53
57
  - lib/bugsnag/integrations/delayed_job.rb
54
58
  - lib/bugsnag/integrations/mailman.rb
@@ -90,6 +94,7 @@ files:
90
94
  - lib/bugsnag/tasks.rb
91
95
  - lib/bugsnag/tasks/bugsnag.rake
92
96
  - lib/bugsnag/utility/circular_buffer.rb
97
+ - lib/bugsnag/utility/metadata_delegate.rb
93
98
  - lib/bugsnag/version.rb
94
99
  - lib/generators/bugsnag/bugsnag_generator.rb
95
100
  homepage: https://github.com/bugsnag/bugsnag-ruby