bugsnag 6.24.2 → 6.25.1
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 +4 -4
- data/.yardopts +1 -0
- data/CHANGELOG.md +19 -0
- data/VERSION +1 -1
- data/bugsnag.gemspec +18 -1
- data/lib/bugsnag/feature_flag.rb +74 -0
- data/lib/bugsnag/middleware/suggestion_data.rb +9 -7
- data/lib/bugsnag/report.rb +38 -2
- data/lib/bugsnag/stacktrace.rb +3 -1
- data/lib/bugsnag/utility/feature_data_store.rb +41 -0
- data/lib/bugsnag/utility/feature_flag_delegate.rb +89 -0
- data/lib/bugsnag.rb +16 -0
- metadata +11 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3925f676929998c1f9738c6b7f82a174d3c87f13b5cc289da021c0d39160a0e5
|
4
|
+
data.tar.gz: 222b3cba78251ff98b80acab3c7129028714e714dbff8160a423e0b17e39db87
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: afb3ffe1e94f6a2ce20b22d16ab216f64a48c0f133618f6dc94ea5700e3e7f3a9554db70ebf98bf2db94acc884a8acb06d26c82996d3f8cce17ad164bb44a931
|
7
|
+
data.tar.gz: a988e117863633052de9bcbce5ca436823c106e075a2ef7f783afd33f74ca508392022fd2489e96f912d9815fd11137e572639855fb354edeb3c45b141018cc9
|
data/.yardopts
CHANGED
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.
|
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
|
-
|
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(
|
13
|
+
def call(event)
|
14
14
|
matches = []
|
15
|
-
|
16
|
-
|
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
|
22
|
+
matches.concat(suggestions.map(&:strip))
|
21
23
|
end
|
22
24
|
|
23
25
|
if matches.size == 1
|
24
|
-
|
26
|
+
event.add_metadata(:error, { suggestion: matches.first })
|
25
27
|
elsif matches.size > 1
|
26
|
-
|
28
|
+
event.add_metadata(:error, { suggestions: matches })
|
27
29
|
end
|
28
30
|
|
29
|
-
@bugsnag.call(
|
31
|
+
@bugsnag.call(event)
|
30
32
|
end
|
31
33
|
end
|
32
34
|
end
|
data/lib/bugsnag/report.rb
CHANGED
@@ -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:
|
419
|
-
message: exception
|
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
|
|
data/lib/bugsnag/stacktrace.rb
CHANGED
@@ -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.
|
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:
|
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.
|
129
|
+
rubygems_version: 3.3.7
|
123
130
|
signing_key:
|
124
131
|
specification_version: 4
|
125
132
|
summary: Ruby notifier for bugsnag.com
|