Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adds support for a retry count and raising #187

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 17 additions & 10 deletions lib/raygun.rb
Original file line number Diff line number Diff line change
Expand Up @@ -60,15 +60,15 @@ def configured?
!!configuration.api_key
end

def track_exception(exception_instance, env = {}, user = nil, retry_count = 1)
def track_exception(exception_instance, env = {}, user = nil, retries_remaining = configuration.error_report_max_attempts - 1)
log('tracking exception')

exception_instance.set_backtrace(caller) if exception_instance.is_a?(Exception) && exception_instance.backtrace.nil?

result = if configuration.send_in_background
track_exception_async(exception_instance, env, user, retry_count)
track_exception_async(exception_instance, env, user, retries_remaining)
else
track_exception_sync(exception_instance, env, user, retry_count)
track_exception_sync(exception_instance, env, user, retries_remaining)
end

result
Expand Down Expand Up @@ -128,10 +128,10 @@ def wait_for_futures

private

def track_exception_async(exception_instance, env, user, retry_count)
def track_exception_async(exception_instance, env, user, retries_remaining)
env[:rg_breadcrumb_store] = Raygun::Breadcrumbs::Store.take_until_size(Client::MAX_BREADCRUMBS_SIZE) if Raygun::Breadcrumbs::Store.any?

future = Concurrent::Future.execute { track_exception_sync(exception_instance, env, user, retry_count) }
future = Concurrent::Future.execute { track_exception_sync(exception_instance, env, user, retries_remaining) }
future.add_observer(lambda do |_, value, reason|
if value == nil || !value.responds_to?(:response) || value.response.code != "202"
log("unexpected response from Raygun, could indicate error: #{value.inspect}")
Expand All @@ -143,7 +143,7 @@ def track_exception_async(exception_instance, env, user, retry_count)
future
end

def track_exception_sync(exception_instance, env, user, retry_count)
def track_exception_sync(exception_instance, env, user, retries_remaining)
if should_report?(exception_instance)
log('attempting to send exception')
resp = Client.new.track_exception(exception_instance, env, user)
Expand All @@ -158,18 +158,25 @@ def track_exception_sync(exception_instance, env, user, retry_count)
failsafe_log("Problem reporting exception to Raygun: #{e.class}: #{e.message}\n\n#{e.backtrace.join("\n")}")
end

if retry_count > 0
if retries_remaining > 0
new_exception = e.exception("raygun4ruby encountered an exception processing your exception")
new_exception.set_backtrace(e.backtrace)

env[:custom_data] ||= {}
env[:custom_data].merge!(original_stacktrace: exception_instance.backtrace)
env[:custom_data].merge!(original_stacktrace: exception_instance.backtrace, retries_remaining: retries_remaining)

::Raygun::Breadcrumbs::Store.clear

track_exception(new_exception, env, user, retry_count - 1)
track_exception(new_exception, env, user, retries_remaining - 1)
else
raise e
if configuration.raise_on_failed_error_report
raise e
else
retries = configuration.error_report_max_attempts - retries_remaining
if configuration.failsafe_logger
failsafe_log("Gave up reporting exception to Raygun after #{retries} #{retries == 1 ? "retry" : "retries"}: #{e.class}: #{e.message}\n\n#{e.backtrace.join("\n")}")
end
end
end
end

Expand Down
8 changes: 8 additions & 0 deletions lib/raygun/configuration.rb
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,12 @@ def self.proc_config_option(name)
# How long to wait for the POST request to the API server before timing out
config_option :error_report_send_timeout

# How many times to try sending to Raygun before giving up
config_option :error_report_max_attempts

# Whether or not we should raise an exception if the error reporting fails
config_option :raise_on_failed_error_report

# Should we register an error handler with [Rails' built in API](https://edgeguides.rubyonrails.org/error_reporting.html)
config_option :register_rails_error_handler

Expand Down Expand Up @@ -147,6 +153,8 @@ def initialize
record_raw_data: false,
send_in_background: false,
error_report_send_timeout: 10,
error_report_max_attempts: 1,
raise_on_failed_error_report: false,
track_retried_sidekiq_jobs: true
)
end
Expand Down
39 changes: 39 additions & 0 deletions test/unit/raygun_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -103,4 +103,43 @@ def test_reset_configuration
assert_equal Raygun.default_configuration.api_url, Raygun.configuration.api_url
refute_equal original_api_url, Raygun.configuration.api_url
end

def test_retries
failsafe_logger = FakeLogger.new
Raygun.setup do |c|
c.error_report_max_attempts = 3
c.failsafe_logger = failsafe_logger
end

WebMock.reset!
report_request = stub_request(:post, "https://api.raygun.com/entries").to_timeout

error = StandardError.new
Raygun.track_exception(error)

assert_requested report_request, times: 3

assert_match(/Gave up reporting exception to Raygun after 3 retries/, failsafe_logger.get)
ensure
Raygun.reset_configuration
end

def test_raising_on_retry_failure
Raygun.setup do |c|
c.error_report_max_attempts = 1
c.raise_on_failed_error_report = true
end

report_request = stub_request(:post, "https://api.raygun.com/entries").to_timeout

error = StandardError.new

assert_raises(StandardError) do
Raygun.track_exception(error)
assert_requested report_request
end

ensure
Raygun.reset_configuration
end
end
26 changes: 13 additions & 13 deletions test/unit/sidekiq_failure_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -48,16 +48,16 @@ def test_failure_backend_unwraps_retries
raise Sidekiq::JobRetry::Handled
end

rescue Sidekiq::JobRetry::Handled => e
rescue Sidekiq::JobRetry::Handled => e

response = Raygun::SidekiqReporter.call(
e,
{ sidekick_name: "robin" },
{} # config
)
response = Raygun::SidekiqReporter.call(
e,
{ sidekick_name: "robin" },
{} # config
)

assert_requested unwrapped_stub
assert response && response.success?, "Expected success, got #{response.class}: #{response.inspect}"
assert_requested unwrapped_stub
assert response && response.success?, "Expected success, got #{response.class}: #{response.inspect}"
end

def test_failured_backend_ignores_retries_if_configured
Expand All @@ -69,12 +69,12 @@ def test_failured_backend_ignores_retries_if_configured
raise Sidekiq::JobRetry::Handled
end

rescue Sidekiq::JobRetry::Handled => e
rescue Sidekiq::JobRetry::Handled => e

refute Raygun::SidekiqReporter.call(e,
{ sidekick_name: "robin" },
{} # config
)
refute Raygun::SidekiqReporter.call(e,
{ sidekick_name: "robin" },
{} # config
)
ensure
Raygun.configuration.track_retried_sidekiq_jobs = true
end
Expand Down