bugsnag 6.22.1 → 6.23.0

Sign up to get free protection for your applications and to get access to all the features.
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