raygun4ruby 4.0.0 → 4.0.2
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/CHANGELOG.md +4 -0
- data/README.md +4 -0
- data/lib/raygun/configuration.rb +13 -1
- data/lib/raygun/error_subscriber.rb +5 -1
- data/lib/raygun/sidekiq.rb +11 -1
- data/lib/raygun/version.rb +1 -1
- data/lib/raygun.rb +17 -10
- data/raygun4ruby.gemspec +1 -0
- data/test/unit/raygun_test.rb +39 -0
- data/test/unit/sidekiq_failure_test.rb +80 -0
- metadata +16 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f1f1acdc84e42b8f39dd817b3e3b74e39dd18b07c675f46afa1cb57bf42bb435
|
4
|
+
data.tar.gz: 6ca0a6036d1d56d23e66105c5535ccc8687973b55b32e461d7643543693e475a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 318c63ba10cb3645f7c75b81f1f4da06de5f361650f3ae7b3a32cc229fa9670589b742952e3cbb1929cfb02d581bdac3fd45c120b957645bea5c9339096021ff
|
7
|
+
data.tar.gz: 396e058ef6bb5fe5a802a6430c548d3133e2b50000af67bdca300cb280c211d2f5464fb12afe4c3c694e658c5887c1983f1d7af08e4cbc2b535c88be75917558
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,7 @@
|
|
1
|
+
## 4.0.1 (29/07/2024):
|
2
|
+
|
3
|
+
- Adds the ability to unwrap `Sidekiq::JobRetry::Handled` exceptions (or ignore them entirely) ([#185](https://github.com/MindscapeHQ/raygun4ruby/pull/185))
|
4
|
+
|
1
5
|
## 4.0.0 (20/05/2024):
|
2
6
|
|
3
7
|
- BREAKING CHANGE: Remove support for end-of-life Ruby verisons and Rails versions prior to 6.0.0
|
data/README.md
CHANGED
@@ -402,6 +402,10 @@ class FailingWorker
|
|
402
402
|
end
|
403
403
|
```
|
404
404
|
|
405
|
+
##### Sidekiq Retries
|
406
|
+
|
407
|
+
By default, Raygun4Ruby will unwrap `Sidekiq::JobRetry::Handled` exceptions and report the original error via `Exception#cause`. If you would prefer not to hear about retries, you can set `config.track_retried_sidekiq_jobs` to `false` in your Raygun configuration.
|
408
|
+
|
405
409
|
### Other Configuration options
|
406
410
|
|
407
411
|
For a complete list of configuration options see the [configuration.rb](https://github.com/MindscapeHQ/raygun4ruby/blob/master/lib/raygun/configuration.rb) file
|
data/lib/raygun/configuration.rb
CHANGED
@@ -86,9 +86,18 @@ module Raygun
|
|
86
86
|
# How long to wait for the POST request to the API server before timing out
|
87
87
|
config_option :error_report_send_timeout
|
88
88
|
|
89
|
+
# How many times to try sending to Raygun before giving up
|
90
|
+
config_option :error_report_max_attempts
|
91
|
+
|
92
|
+
# Whether or not we should raise an exception if the error reporting fails
|
93
|
+
config_option :raise_on_failed_error_report
|
94
|
+
|
89
95
|
# Should we register an error handler with [Rails' built in API](https://edgeguides.rubyonrails.org/error_reporting.html)
|
90
96
|
config_option :register_rails_error_handler
|
91
97
|
|
98
|
+
# Should we track jobs that are retried in Sidekiq (ones that raise Sidekiq::JobRetry::Handled). Set to "false" to ignore.
|
99
|
+
config_option :track_retried_sidekiq_jobs
|
100
|
+
|
92
101
|
# Exception classes to ignore by default
|
93
102
|
IGNORE_DEFAULT = ['ActiveRecord::RecordNotFound',
|
94
103
|
'ActionController::RoutingError',
|
@@ -143,7 +152,10 @@ module Raygun
|
|
143
152
|
breadcrumb_level: :info,
|
144
153
|
record_raw_data: false,
|
145
154
|
send_in_background: false,
|
146
|
-
error_report_send_timeout: 10
|
155
|
+
error_report_send_timeout: 10,
|
156
|
+
error_report_max_attempts: 1,
|
157
|
+
raise_on_failed_error_report: false,
|
158
|
+
track_retried_sidekiq_jobs: true
|
147
159
|
)
|
148
160
|
end
|
149
161
|
|
@@ -16,6 +16,10 @@ class Raygun::ErrorSubscriber
|
|
16
16
|
tags: ["rails_error_reporter", *tags].compact
|
17
17
|
}
|
18
18
|
|
19
|
-
|
19
|
+
if source == "job.sidekiq" && defined?(Sidekiq)
|
20
|
+
Raygun::SidekiqReporter.call(error, data)
|
21
|
+
else
|
22
|
+
Raygun.track_exception(error, data)
|
23
|
+
end
|
20
24
|
end
|
21
25
|
end
|
data/lib/raygun/sidekiq.rb
CHANGED
@@ -6,7 +6,7 @@
|
|
6
6
|
module Raygun
|
7
7
|
|
8
8
|
class SidekiqReporter
|
9
|
-
def self.call(exception, context_hash, config)
|
9
|
+
def self.call(exception, context_hash = {}, config = nil)
|
10
10
|
user = affected_user(context_hash)
|
11
11
|
data = {
|
12
12
|
custom_data: {
|
@@ -14,6 +14,16 @@ module Raygun
|
|
14
14
|
},
|
15
15
|
tags: ['sidekiq']
|
16
16
|
}
|
17
|
+
|
18
|
+
if exception.is_a?(Sidekiq::JobRetry::Handled) && exception.cause
|
19
|
+
if Raygun.configuration.track_retried_sidekiq_jobs
|
20
|
+
data.merge!(sidekiq_retried: true)
|
21
|
+
exception = exception.cause
|
22
|
+
else
|
23
|
+
return false
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
17
27
|
if exception.instance_variable_defined?(:@__raygun_correlation_id) && correlation_id = exception.instance_variable_get(:@__raygun_correlation_id)
|
18
28
|
data.merge!(correlation_id: correlation_id)
|
19
29
|
end
|
data/lib/raygun/version.rb
CHANGED
data/lib/raygun.rb
CHANGED
@@ -60,15 +60,15 @@ module Raygun
|
|
60
60
|
!!configuration.api_key
|
61
61
|
end
|
62
62
|
|
63
|
-
def track_exception(exception_instance, env = {}, user = nil,
|
63
|
+
def track_exception(exception_instance, env = {}, user = nil, retries_remaining = configuration.error_report_max_attempts - 1)
|
64
64
|
log('tracking exception')
|
65
65
|
|
66
66
|
exception_instance.set_backtrace(caller) if exception_instance.is_a?(Exception) && exception_instance.backtrace.nil?
|
67
67
|
|
68
68
|
result = if configuration.send_in_background
|
69
|
-
track_exception_async(exception_instance, env, user,
|
69
|
+
track_exception_async(exception_instance, env, user, retries_remaining)
|
70
70
|
else
|
71
|
-
track_exception_sync(exception_instance, env, user,
|
71
|
+
track_exception_sync(exception_instance, env, user, retries_remaining)
|
72
72
|
end
|
73
73
|
|
74
74
|
result
|
@@ -128,10 +128,10 @@ module Raygun
|
|
128
128
|
|
129
129
|
private
|
130
130
|
|
131
|
-
def track_exception_async(exception_instance, env, user,
|
131
|
+
def track_exception_async(exception_instance, env, user, retries_remaining)
|
132
132
|
env[:rg_breadcrumb_store] = Raygun::Breadcrumbs::Store.take_until_size(Client::MAX_BREADCRUMBS_SIZE) if Raygun::Breadcrumbs::Store.any?
|
133
133
|
|
134
|
-
future = Concurrent::Future.execute { track_exception_sync(exception_instance, env, user,
|
134
|
+
future = Concurrent::Future.execute { track_exception_sync(exception_instance, env, user, retries_remaining) }
|
135
135
|
future.add_observer(lambda do |_, value, reason|
|
136
136
|
if value == nil || !value.responds_to?(:response) || value.response.code != "202"
|
137
137
|
log("unexpected response from Raygun, could indicate error: #{value.inspect}")
|
@@ -143,7 +143,7 @@ module Raygun
|
|
143
143
|
future
|
144
144
|
end
|
145
145
|
|
146
|
-
def track_exception_sync(exception_instance, env, user,
|
146
|
+
def track_exception_sync(exception_instance, env, user, retries_remaining)
|
147
147
|
if should_report?(exception_instance)
|
148
148
|
log('attempting to send exception')
|
149
149
|
resp = Client.new.track_exception(exception_instance, env, user)
|
@@ -158,18 +158,25 @@ module Raygun
|
|
158
158
|
failsafe_log("Problem reporting exception to Raygun: #{e.class}: #{e.message}\n\n#{e.backtrace.join("\n")}")
|
159
159
|
end
|
160
160
|
|
161
|
-
if
|
161
|
+
if retries_remaining > 0
|
162
162
|
new_exception = e.exception("raygun4ruby encountered an exception processing your exception")
|
163
163
|
new_exception.set_backtrace(e.backtrace)
|
164
164
|
|
165
165
|
env[:custom_data] ||= {}
|
166
|
-
env[:custom_data].merge!(original_stacktrace: exception_instance.backtrace)
|
166
|
+
env[:custom_data].merge!(original_stacktrace: exception_instance.backtrace, retries_remaining: retries_remaining)
|
167
167
|
|
168
168
|
::Raygun::Breadcrumbs::Store.clear
|
169
169
|
|
170
|
-
track_exception(new_exception, env, user,
|
170
|
+
track_exception(new_exception, env, user, retries_remaining - 1)
|
171
171
|
else
|
172
|
-
|
172
|
+
if configuration.raise_on_failed_error_report
|
173
|
+
raise e
|
174
|
+
else
|
175
|
+
retries = configuration.error_report_max_attempts - retries_remaining
|
176
|
+
if configuration.failsafe_logger
|
177
|
+
failsafe_log("Gave up reporting exception to Raygun after #{retries} #{retries == 1 ? "retry" : "retries"}: #{e.class}: #{e.message}\n\n#{e.backtrace.join("\n")}")
|
178
|
+
end
|
179
|
+
end
|
173
180
|
end
|
174
181
|
end
|
175
182
|
|
data/raygun4ruby.gemspec
CHANGED
@@ -24,6 +24,7 @@ Gem::Specification.new do |spec|
|
|
24
24
|
spec.add_runtime_dependency "json"
|
25
25
|
spec.add_runtime_dependency "rack"
|
26
26
|
spec.add_runtime_dependency "concurrent-ruby"
|
27
|
+
spec.add_runtime_dependency "ostruct"
|
27
28
|
|
28
29
|
spec.add_development_dependency "bundler", ">= 2.3"
|
29
30
|
spec.add_development_dependency "rake", ">= 12.3.3"
|
data/test/unit/raygun_test.rb
CHANGED
@@ -103,4 +103,43 @@ class RaygunTest < Raygun::UnitTest
|
|
103
103
|
assert_equal Raygun.default_configuration.api_url, Raygun.configuration.api_url
|
104
104
|
refute_equal original_api_url, Raygun.configuration.api_url
|
105
105
|
end
|
106
|
+
|
107
|
+
def test_retries
|
108
|
+
failsafe_logger = FakeLogger.new
|
109
|
+
Raygun.setup do |c|
|
110
|
+
c.error_report_max_attempts = 3
|
111
|
+
c.failsafe_logger = failsafe_logger
|
112
|
+
end
|
113
|
+
|
114
|
+
WebMock.reset!
|
115
|
+
report_request = stub_request(:post, "https://api.raygun.com/entries").to_timeout
|
116
|
+
|
117
|
+
error = StandardError.new
|
118
|
+
Raygun.track_exception(error)
|
119
|
+
|
120
|
+
assert_requested report_request, times: 3
|
121
|
+
|
122
|
+
assert_match(/Gave up reporting exception to Raygun after 3 retries/, failsafe_logger.get)
|
123
|
+
ensure
|
124
|
+
Raygun.reset_configuration
|
125
|
+
end
|
126
|
+
|
127
|
+
def test_raising_on_retry_failure
|
128
|
+
Raygun.setup do |c|
|
129
|
+
c.error_report_max_attempts = 1
|
130
|
+
c.raise_on_failed_error_report = true
|
131
|
+
end
|
132
|
+
|
133
|
+
report_request = stub_request(:post, "https://api.raygun.com/entries").to_timeout
|
134
|
+
|
135
|
+
error = StandardError.new
|
136
|
+
|
137
|
+
assert_raises(StandardError) do
|
138
|
+
Raygun.track_exception(error)
|
139
|
+
assert_requested report_request
|
140
|
+
end
|
141
|
+
|
142
|
+
ensure
|
143
|
+
Raygun.reset_configuration
|
144
|
+
end
|
106
145
|
end
|
@@ -1,6 +1,7 @@
|
|
1
1
|
require_relative "../test_helper.rb"
|
2
2
|
|
3
3
|
require "sidekiq"
|
4
|
+
|
4
5
|
# Convince Sidekiq it's on a server :)
|
5
6
|
module Sidekiq
|
6
7
|
class << self
|
@@ -15,6 +16,8 @@ require "raygun/sidekiq"
|
|
15
16
|
class SidekiqFailureTest < Raygun::UnitTest
|
16
17
|
|
17
18
|
def setup
|
19
|
+
require "sidekiq/job_retry"
|
20
|
+
|
18
21
|
super
|
19
22
|
Raygun.configuration.send_in_background = false
|
20
23
|
|
@@ -32,6 +35,61 @@ class SidekiqFailureTest < Raygun::UnitTest
|
|
32
35
|
assert response && response.success?, "Expected success, got #{response.class}: #{response.inspect}"
|
33
36
|
end
|
34
37
|
|
38
|
+
def test_failure_backend_unwraps_retries
|
39
|
+
WebMock.reset!
|
40
|
+
|
41
|
+
unwrapped_stub = stub_request(:post, 'https://api.raygun.com/entries').
|
42
|
+
with(body: /StandardError/).
|
43
|
+
to_return(status: 202)
|
44
|
+
|
45
|
+
begin
|
46
|
+
raise StandardError.new("Some job in Sidekiq failed, oh dear!")
|
47
|
+
rescue
|
48
|
+
raise Sidekiq::JobRetry::Handled
|
49
|
+
end
|
50
|
+
|
51
|
+
rescue Sidekiq::JobRetry::Handled => e
|
52
|
+
|
53
|
+
response = Raygun::SidekiqReporter.call(
|
54
|
+
e,
|
55
|
+
{ sidekick_name: "robin" },
|
56
|
+
{} # config
|
57
|
+
)
|
58
|
+
|
59
|
+
assert_requested unwrapped_stub
|
60
|
+
assert response && response.success?, "Expected success, got #{response.class}: #{response.inspect}"
|
61
|
+
end
|
62
|
+
|
63
|
+
def test_failured_backend_ignores_retries_if_configured
|
64
|
+
Raygun.configuration.track_retried_sidekiq_jobs = false
|
65
|
+
|
66
|
+
begin
|
67
|
+
raise StandardError.new("Some job in Sidekiq failed, oh dear!")
|
68
|
+
rescue
|
69
|
+
raise Sidekiq::JobRetry::Handled
|
70
|
+
end
|
71
|
+
|
72
|
+
rescue Sidekiq::JobRetry::Handled => e
|
73
|
+
|
74
|
+
refute Raygun::SidekiqReporter.call(e,
|
75
|
+
{ sidekick_name: "robin" },
|
76
|
+
{} # config
|
77
|
+
)
|
78
|
+
ensure
|
79
|
+
Raygun.configuration.track_retried_sidekiq_jobs = true
|
80
|
+
end
|
81
|
+
|
82
|
+
# See https://github.com/MindscapeHQ/raygun4ruby/issues/183
|
83
|
+
# (This is how Sidekiq pre 7.1.5 calls error handlers: https://github.com/sidekiq/sidekiq/blob/1ba89bbb22d2fd574b11702d8b6ed63ae59e2256/lib/sidekiq/config.rb#L269)
|
84
|
+
def test_failure_backend_appears_to_work_without_config_argument
|
85
|
+
response = Raygun::SidekiqReporter.call(
|
86
|
+
StandardError.new("Oh no! Your Sidekiq has failed!"),
|
87
|
+
{ sidekick_name: "robin" }
|
88
|
+
)
|
89
|
+
|
90
|
+
assert response && response.success?, "Expected success, got #{response.class}: #{response.inspect}"
|
91
|
+
end
|
92
|
+
|
35
93
|
def test_we_are_in_sidekiqs_list_of_error_handlers
|
36
94
|
# Sidekiq 7.x stores error handlers inside a configuration object, while 6.x and below stores them directly against the Sidekiq module
|
37
95
|
error_handlers = Sidekiq.respond_to?(:error_handlers) ? Sidekiq.error_handlers : Sidekiq.default_configuration.error_handlers
|
@@ -39,4 +97,26 @@ class SidekiqFailureTest < Raygun::UnitTest
|
|
39
97
|
assert error_handlers.include?(Raygun::SidekiqReporter)
|
40
98
|
end
|
41
99
|
|
100
|
+
def test_rails_error_reporter_uses_sidekiq_reporter
|
101
|
+
WebMock.reset!
|
102
|
+
|
103
|
+
tagged_request = stub_request(:post, 'https://api.raygun.com/entries').
|
104
|
+
with(body: /"sidekiq"/). # should have a sidekiq tag!
|
105
|
+
to_return(status: 202)
|
106
|
+
|
107
|
+
error = StandardError.new("Oh no! Your Sidekiq has failed!")
|
108
|
+
|
109
|
+
response = Raygun::ErrorSubscriber.new.report(
|
110
|
+
error,
|
111
|
+
handled: true,
|
112
|
+
severity: "error",
|
113
|
+
context: { sidekick_name: "robin" },
|
114
|
+
source: "job.sidekiq"
|
115
|
+
)
|
116
|
+
|
117
|
+
assert response && response.success?, "Expected success, got #{response.class}: #{response.inspect}"
|
118
|
+
|
119
|
+
assert_requested tagged_request
|
120
|
+
end
|
121
|
+
|
42
122
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: raygun4ruby
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 4.0.
|
4
|
+
version: 4.0.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Mindscape
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2024-
|
12
|
+
date: 2024-11-14 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: httparty
|
@@ -67,6 +67,20 @@ dependencies:
|
|
67
67
|
- - ">="
|
68
68
|
- !ruby/object:Gem::Version
|
69
69
|
version: '0'
|
70
|
+
- !ruby/object:Gem::Dependency
|
71
|
+
name: ostruct
|
72
|
+
requirement: !ruby/object:Gem::Requirement
|
73
|
+
requirements:
|
74
|
+
- - ">="
|
75
|
+
- !ruby/object:Gem::Version
|
76
|
+
version: '0'
|
77
|
+
type: :runtime
|
78
|
+
prerelease: false
|
79
|
+
version_requirements: !ruby/object:Gem::Requirement
|
80
|
+
requirements:
|
81
|
+
- - ">="
|
82
|
+
- !ruby/object:Gem::Version
|
83
|
+
version: '0'
|
70
84
|
- !ruby/object:Gem::Dependency
|
71
85
|
name: bundler
|
72
86
|
requirement: !ruby/object:Gem::Requirement
|