bugsnag 6.24.2 → 6.25.1

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: b0c0062b72786fa763b8b17ff0f72647808c2c0372c08b098b70007acc52dec9
4
- data.tar.gz: c467bdbbe899f9e6f2ec1bf47071c8f70228f7cac51f292e8b77a6c9724da368
3
+ metadata.gz: 3925f676929998c1f9738c6b7f82a174d3c87f13b5cc289da021c0d39160a0e5
4
+ data.tar.gz: 222b3cba78251ff98b80acab3c7129028714e714dbff8160a423e0b17e39db87
5
5
  SHA512:
6
- metadata.gz: c2cd2286a921e1cb2ad6e78e620c097a7a5937be53ebb75f020cc7eaba31505c904315083b545a1f9758ddb14c2e902c8e47e8845eef3a242fd8aec810860496
7
- data.tar.gz: 15410f4d0280b5abb8f9804a6b66c6d05eed52c025e394ed0fbc642507d7ac64b2cb90db5a1dda0a946143d85dbab4d148dafab88697fa47bb79363d4ff6df3e
6
+ metadata.gz: afb3ffe1e94f6a2ce20b22d16ab216f64a48c0f133618f6dc94ea5700e3e7f3a9554db70ebf98bf2db94acc884a8acb06d26c82996d3f8cce17ad164bb44a931
7
+ data.tar.gz: a988e117863633052de9bcbce5ca436823c106e075a2ef7f783afd33f74ca508392022fd2489e96f912d9815fd11137e572639855fb354edeb3c45b141018cc9
data/.yardopts CHANGED
@@ -4,6 +4,7 @@
4
4
  --no-private
5
5
  --protected
6
6
  --title "bugsnag-ruby API Documentation"
7
+ --embed-mixins
7
8
  lib/**/*.rb
8
9
  -
9
10
  README.md
data/CHANGELOG.md CHANGED
@@ -1,6 +1,25 @@
1
1
  Changelog
2
2
  =========
3
3
 
4
+ ## v6.25.1 (5 January 2023)
5
+
6
+ ### Fixes
7
+
8
+ * Allow Gem paths to be stripped from file names in stacktraces when they contain a Regexp special character
9
+ | [#764](https://github.com/bugsnag/bugsnag-ruby/pull/764)
10
+
11
+ ### Enhancements
12
+
13
+ * Use `Exception#detailed_message` instead of `Exception#message` when available
14
+ | [#761](https://github.com/bugsnag/bugsnag-ruby/pull/761)
15
+
16
+ ## v6.25.0 (1 December 2022)
17
+
18
+ ### Enhancements
19
+
20
+ * Add support for feature flags & experiments. For more information, please see https://docs.bugsnag.com/product/features-experiments
21
+ | [#758](https://github.com/bugsnag/bugsnag-ruby/pull/758)
22
+
4
23
  ## v6.24.2 (21 January 2022)
5
24
 
6
25
  ### Fixes
data/VERSION CHANGED
@@ -1 +1 @@
1
- 6.24.2
1
+ 6.25.1
data/bugsnag.gemspec CHANGED
@@ -18,5 +18,22 @@ Gem::Specification.new do |s|
18
18
  ]
19
19
  s.require_paths = ["lib"]
20
20
  s.required_ruby_version = '>= 1.9.2'
21
- s.add_runtime_dependency 'concurrent-ruby', '~> 1.0'
21
+
22
+ ruby_version = Gem::Version.new(RUBY_VERSION.dup)
23
+
24
+ if ruby_version < Gem::Version.new('2.2.0')
25
+ # concurrent-ruby 1.1.10 requires Ruby 2.2+
26
+ s.add_runtime_dependency 'concurrent-ruby', '~> 1.0', '< 1.1.10'
27
+ else
28
+ s.add_runtime_dependency 'concurrent-ruby', '~> 1.0'
29
+ end
30
+
31
+ if s.respond_to?(:metadata=)
32
+ s.metadata = {
33
+ "changelog_uri" => "https://github.com/bugsnag/bugsnag-ruby/blob/HEAD/CHANGELOG.md",
34
+ "documentation_uri" => "https://docs.bugsnag.com/platforms/ruby/",
35
+ "source_code_uri" => "https://github.com/bugsnag/bugsnag-ruby/",
36
+ "rubygems_mfa_required" => "true"
37
+ }
38
+ end
22
39
  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
@@ -10,23 +10,25 @@ module Bugsnag::Middleware
10
10
  @bugsnag = bugsnag
11
11
  end
12
12
 
13
- def call(report)
13
+ def call(event)
14
14
  matches = []
15
- report.raw_exceptions.each do |exception|
16
- match = CAPTURE_REGEX.match(exception.message)
15
+
16
+ event.errors.each do |error|
17
+ match = CAPTURE_REGEX.match(error.error_message)
18
+
17
19
  next unless match
18
20
 
19
21
  suggestions = match.captures[0].split(DELIMITER)
20
- matches.concat suggestions.map{ |suggestion| suggestion.strip }
22
+ matches.concat(suggestions.map(&:strip))
21
23
  end
22
24
 
23
25
  if matches.size == 1
24
- report.add_tab(:error, {:suggestion => matches.first})
26
+ event.add_metadata(:error, { suggestion: matches.first })
25
27
  elsif matches.size > 1
26
- report.add_tab(:error, {:suggestions => matches})
28
+ event.add_metadata(:error, { suggestions: matches })
27
29
  end
28
30
 
29
- @bugsnag.call(report)
31
+ @bugsnag.call(event)
30
32
  end
31
33
  end
32
34
  end
@@ -6,6 +6,8 @@ require "bugsnag/stacktrace"
6
6
  module Bugsnag
7
7
  # rubocop:todo Metrics/ClassLength
8
8
  class Report
9
+ include Utility::FeatureDataStore
10
+
9
11
  NOTIFIER_NAME = "Ruby Bugsnag Notifier"
10
12
  NOTIFIER_VERSION = Bugsnag::VERSION
11
13
  NOTIFIER_URL = "https://www.bugsnag.com"
@@ -143,6 +145,7 @@ module Bugsnag
143
145
  self.user = {}
144
146
 
145
147
  @metadata_delegate = Utility::MetadataDelegate.new
148
+ @feature_flag_delegate = Bugsnag.feature_flag_delegate.dup
146
149
  end
147
150
 
148
151
  ##
@@ -220,6 +223,7 @@ module Bugsnag
220
223
  time: @created_at
221
224
  },
222
225
  exceptions: exceptions,
226
+ featureFlags: @feature_flag_delegate.as_json,
223
227
  groupingHash: grouping_hash,
224
228
  metaData: meta_data,
225
229
  session: session,
@@ -239,6 +243,7 @@ module Bugsnag
239
243
  :version => NOTIFIER_VERSION,
240
244
  :url => NOTIFIER_URL
241
245
  },
246
+ :payloadVersion => CURRENT_PAYLOAD_VERSION,
242
247
  :events => [payload_event]
243
248
  }
244
249
  end
@@ -357,6 +362,13 @@ module Bugsnag
357
362
  @metadata_delegate.clear_metadata(@meta_data, section, *args)
358
363
  end
359
364
 
365
+ # Get the array of stored feature flags
366
+ #
367
+ # @return [Array<Bugsnag::FeatureFlag>]
368
+ def feature_flags
369
+ @feature_flag_delegate.to_a
370
+ end
371
+
360
372
  ##
361
373
  # Set information about the current user
362
374
  #
@@ -393,6 +405,8 @@ module Bugsnag
393
405
 
394
406
  private
395
407
 
408
+ attr_reader :feature_flag_delegate
409
+
396
410
  def update_handled_counts(is_unhandled, was_unhandled)
397
411
  # do nothing if there is no session to update
398
412
  return if @session.nil?
@@ -414,9 +428,11 @@ module Bugsnag
414
428
 
415
429
  def generate_exception_list
416
430
  raw_exceptions.map do |exception|
431
+ class_name = error_class(exception)
432
+
417
433
  {
418
- errorClass: error_class(exception),
419
- message: exception.message,
434
+ errorClass: class_name,
435
+ message: error_message(exception, class_name),
420
436
  stacktrace: Stacktrace.process(exception.backtrace, configuration)
421
437
  }
422
438
  end
@@ -434,6 +450,26 @@ module Bugsnag
434
450
  (exception.is_a? Class) ? exception.name : exception.class.name
435
451
  end
436
452
 
453
+ def error_message(exception, class_name)
454
+ # Ruby 3.2 added Exception#detailed_message for Gems like "Did you mean"
455
+ # to annotate an exception's message
456
+ return exception.message unless exception.respond_to?(:detailed_message)
457
+
458
+ # the "highlight" argument may add terminal escape codes to the output,
459
+ # which we don't want to include
460
+ # it _should_ always be present but it's possible to forget to add it or
461
+ # to have implemented this method before Ruby 3.2
462
+ message =
463
+ begin
464
+ exception.detailed_message(highlight: false)
465
+ rescue ArgumentError
466
+ exception.detailed_message
467
+ end
468
+
469
+ # remove the class name to be consistent with Exception#message
470
+ message.sub(" (#{class_name})", '')
471
+ end
472
+
437
473
  def generate_raw_exceptions(exception)
438
474
  exceptions = []
439
475
 
@@ -48,7 +48,9 @@ module Bugsnag
48
48
 
49
49
  # Strip common gem path prefixes
50
50
  if defined?(Gem)
51
- file = Gem.path.inject(file) {|line, path| line.sub(/#{path}\//, "") }
51
+ Gem.path.each do |path|
52
+ file.sub!("#{path}/", "")
53
+ end
52
54
  end
53
55
 
54
56
  trace_hash[:file] = file
@@ -0,0 +1,41 @@
1
+ module Bugsnag::Utility
2
+ # @abstract Requires a #feature_flag_delegate method returning a
3
+ # {Bugsnag::Utility::FeatureFlagDelegate}
4
+ module FeatureDataStore
5
+ # Add a feature flag with the given name & variant
6
+ #
7
+ # @param name [String]
8
+ # @param variant [String, nil]
9
+ # @return [void]
10
+ def add_feature_flag(name, variant = nil)
11
+ feature_flag_delegate.add(name, variant)
12
+ end
13
+
14
+ # Merge the given array of FeatureFlag instances into the stored feature
15
+ # flags
16
+ #
17
+ # New flags will be appended to the array. Flags with the same name will be
18
+ # overwritten, but their position in the array will not change
19
+ #
20
+ # @param feature_flags [Array<Bugsnag::FeatureFlag>]
21
+ # @return [void]
22
+ def add_feature_flags(feature_flags)
23
+ feature_flag_delegate.merge(feature_flags)
24
+ end
25
+
26
+ # Remove the stored flag with the given name
27
+ #
28
+ # @param name [String]
29
+ # @return [void]
30
+ def clear_feature_flag(name)
31
+ feature_flag_delegate.remove(name)
32
+ end
33
+
34
+ # Remove all the stored flags
35
+ #
36
+ # @return [void]
37
+ def clear_feature_flags
38
+ feature_flag_delegate.clear
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,89 @@
1
+ module Bugsnag::Utility
2
+ # @api private
3
+ class FeatureFlagDelegate
4
+ def initialize
5
+ # feature flags are stored internally in a hash of "name" => <FeatureFlag>
6
+ # we don't use a Set because new feature flags should overwrite old ones
7
+ # that share a name, but FeatureFlag equality also uses the variant
8
+ @storage = {}
9
+ end
10
+
11
+ def initialize_dup(original)
12
+ super
13
+
14
+ # copy the internal storage when 'dup' is called
15
+ @storage = @storage.dup
16
+ end
17
+
18
+ # Add a feature flag with the given name & variant
19
+ #
20
+ # @param name [String]
21
+ # @param variant [String, nil]
22
+ # @return [void]
23
+ def add(name, variant)
24
+ flag = Bugsnag::FeatureFlag.new(name, variant)
25
+
26
+ return unless flag.valid?
27
+
28
+ @storage[flag.name] = flag
29
+ end
30
+
31
+ # Merge the given array of FeatureFlag instances into the stored feature
32
+ # flags
33
+ #
34
+ # New flags will be appended to the array. Flags with the same name will be
35
+ # overwritten, but their position in the array will not change
36
+ #
37
+ # @param feature_flags [Array<Bugsnag::FeatureFlag>]
38
+ # @return [void]
39
+ def merge(feature_flags)
40
+ feature_flags.each do |flag|
41
+ next unless flag.is_a?(Bugsnag::FeatureFlag)
42
+ next unless flag.valid?
43
+
44
+ @storage[flag.name] = flag
45
+ end
46
+ end
47
+
48
+ # Remove the stored flag with the given name
49
+ #
50
+ # @param name [String]
51
+ # @return [void]
52
+ def remove(name)
53
+ @storage.delete(name)
54
+ end
55
+
56
+ # Remove all the stored flags
57
+ #
58
+ # @return [void]
59
+ def clear
60
+ @storage.clear
61
+ end
62
+
63
+ # Get an array of FeatureFlag instances
64
+ #
65
+ # @example
66
+ # [
67
+ # <#Bugsnag::FeatureFlag>,
68
+ # <#Bugsnag::FeatureFlag>,
69
+ # ]
70
+ #
71
+ # @return [Array<Bugsnag::FeatureFlag>]
72
+ def to_a
73
+ @storage.values
74
+ end
75
+
76
+ # Get the feature flags in their JSON representation
77
+ #
78
+ # @example
79
+ # [
80
+ # { "featureFlag" => "name", "variant" => "variant" },
81
+ # { "featureFlag" => "another name" },
82
+ # ]
83
+ #
84
+ # @return [Array<Hash{String => String}>]
85
+ def as_json
86
+ to_a.map(&:to_h)
87
+ end
88
+ end
89
+ end
data/lib/bugsnag.rb CHANGED
@@ -2,6 +2,7 @@ require "rubygems"
2
2
  require "thread"
3
3
 
4
4
  require "bugsnag/version"
5
+ require "bugsnag/utility/feature_data_store"
5
6
  require "bugsnag/configuration"
6
7
  require "bugsnag/meta_data"
7
8
  require "bugsnag/report"
@@ -14,6 +15,8 @@ require "bugsnag/delivery"
14
15
  require "bugsnag/delivery/synchronous"
15
16
  require "bugsnag/delivery/thread_queue"
16
17
 
18
+ require "bugsnag/feature_flag"
19
+
17
20
  # Rack is not bundled with the other integrations
18
21
  # as it doesn't auto-configure when loaded
19
22
  require "bugsnag/integrations/rack"
@@ -36,6 +39,7 @@ require "bugsnag/breadcrumbs/breadcrumbs"
36
39
 
37
40
  require "bugsnag/utility/duplicator"
38
41
  require "bugsnag/utility/metadata_delegate"
42
+ require "bugsnag/utility/feature_flag_delegate"
39
43
 
40
44
  # rubocop:todo Metrics/ModuleLength
41
45
  module Bugsnag
@@ -45,6 +49,8 @@ module Bugsnag
45
49
  NIL_EXCEPTION_DESCRIPTION = "'nil' was notified as an exception"
46
50
 
47
51
  class << self
52
+ include Utility::FeatureDataStore
53
+
48
54
  ##
49
55
  # Configure the Bugsnag notifier application-wide settings.
50
56
  #
@@ -426,6 +432,16 @@ module Bugsnag
426
432
  configuration.clear_metadata(section, *args)
427
433
  end
428
434
 
435
+ # Expose the feature flag delegate internally for use when creating new Events
436
+ #
437
+ # The Bugsnag module's feature_flag_delegate is request-specific
438
+ #
439
+ # @return [Bugsnag::Utility::FeatureFlagDelegate]
440
+ # @api private
441
+ def feature_flag_delegate
442
+ configuration.request_data[:feature_flag_delegate] ||= Utility::FeatureFlagDelegate.new
443
+ end
444
+
429
445
  private
430
446
 
431
447
  def should_deliver_notification?(exception, auto_notify)
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.24.2
4
+ version: 6.25.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - James Smith
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-01-21 00:00:00.000000000 Z
11
+ date: 2023-01-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: concurrent-ruby
@@ -55,6 +55,7 @@ files:
55
55
  - lib/bugsnag/endpoint_validator.rb
56
56
  - lib/bugsnag/error.rb
57
57
  - lib/bugsnag/event.rb
58
+ - lib/bugsnag/feature_flag.rb
58
59
  - lib/bugsnag/helpers.rb
59
60
  - lib/bugsnag/integrations/delayed_job.rb
60
61
  - lib/bugsnag/integrations/mailman.rb
@@ -97,13 +98,19 @@ files:
97
98
  - lib/bugsnag/tasks/bugsnag.rake
98
99
  - lib/bugsnag/utility/circular_buffer.rb
99
100
  - lib/bugsnag/utility/duplicator.rb
101
+ - lib/bugsnag/utility/feature_data_store.rb
102
+ - lib/bugsnag/utility/feature_flag_delegate.rb
100
103
  - lib/bugsnag/utility/metadata_delegate.rb
101
104
  - lib/bugsnag/version.rb
102
105
  - lib/generators/bugsnag/bugsnag_generator.rb
103
106
  homepage: https://github.com/bugsnag/bugsnag-ruby
104
107
  licenses:
105
108
  - MIT
106
- metadata: {}
109
+ metadata:
110
+ changelog_uri: https://github.com/bugsnag/bugsnag-ruby/blob/HEAD/CHANGELOG.md
111
+ documentation_uri: https://docs.bugsnag.com/platforms/ruby/
112
+ source_code_uri: https://github.com/bugsnag/bugsnag-ruby/
113
+ rubygems_mfa_required: 'true'
107
114
  post_install_message:
108
115
  rdoc_options: []
109
116
  require_paths:
@@ -119,7 +126,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
119
126
  - !ruby/object:Gem::Version
120
127
  version: '0'
121
128
  requirements: []
122
- rubygems_version: 3.2.22
129
+ rubygems_version: 3.3.7
123
130
  signing_key:
124
131
  specification_version: 4
125
132
  summary: Ruby notifier for bugsnag.com