Skip to content

Commit 500b2b4

Browse files
committed
Report Sidekiq errors when the job is discarded
Add the `sidekiq_report_errors` config option. If set to `discard`, it will only report the error if the job is discarded, or "dead" in Sidekiq terminology. This follows the same behavior as the `activejob_report_errors` config option. To make this behavior work, it uses a Sidekiq death handler to report the error when the job is discarded. This death handler is run before the error handler, so the error handler can stay responsible for completing the transaction. Death handlers were introduced in Sidekiq 5.1. Older versions will report all errors for jobs. Part of #1091
1 parent 6c424d8 commit 500b2b4

File tree

7 files changed

+273
-68
lines changed

7 files changed

+273
-68
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
bump: minor
3+
type: add
4+
---
5+
6+
Report Sidekiq errors when a job is dead/discarded. Configure the new `sidekiq_report_errors` config option to "discard" to only report errors when the job is not retried further.

lib/appsignal/config.rb

+8
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ class Config
4747
],
4848
:send_environment_metadata => true,
4949
:send_params => true,
50+
:sidekiq_report_errors => "all",
5051
:transaction_debug_mode => false
5152
}.freeze
5253

@@ -108,6 +109,7 @@ class Config
108109
"APPSIGNAL_SEND_ENVIRONMENT_METADATA" => :send_environment_metadata,
109110
"APPSIGNAL_SEND_PARAMS" => :send_params,
110111
"APPSIGNAL_SEND_SESSION_DATA" => :send_session_data,
112+
"APPSIGNAL_SIDEKIQ_REPORT_ERRORS" => :sidekiq_report_errors,
111113
"APPSIGNAL_SKIP_SESSION_DATA" => :skip_session_data,
112114
"APPSIGNAL_STATSD_PORT" => :statsd_port,
113115
"APPSIGNAL_TRANSACTION_DEBUG_MODE" => :transaction_debug_mode,
@@ -130,6 +132,7 @@ class Config
130132
APPSIGNAL_LOGGING_ENDPOINT
131133
APPSIGNAL_PUSH_API_ENDPOINT
132134
APPSIGNAL_PUSH_API_KEY
135+
APPSIGNAL_SIDEKIQ_REPORT_ERRORS
133136
APPSIGNAL_STATSD_PORT
134137
APPSIGNAL_WORKING_DIRECTORY_PATH
135138
APPSIGNAL_WORKING_DIR_PATH
@@ -563,6 +566,11 @@ def determine_overrides
563566
config[:activejob_report_errors] = "all"
564567
end
565568

569+
if config_hash[:sidekiq_report_errors] == "discard" &&
570+
!Appsignal::Hooks::SidekiqHook.version_5_1_or_higher?
571+
config[:sidekiq_report_errors] = "all"
572+
end
573+
566574
config
567575
end
568576

lib/appsignal/hooks/sidekiq.rb

+18-1
Original file line numberDiff line numberDiff line change
@@ -5,17 +5,34 @@ class Hooks
55
class SidekiqHook < Appsignal::Hooks::Hook
66
register :sidekiq
77

8-
def dependencies_present?
8+
def self.version_5_1_or_higher?
9+
@version_5_1_or_higher ||=
10+
if dependencies_present?
11+
Gem::Version.new(::Sidekiq::VERSION) >= Gem::Version.new("5.1.0")
12+
else
13+
false
14+
end
15+
end
16+
17+
def self.dependencies_present?
918
defined?(::Sidekiq)
1019
end
1120

21+
def dependencies_present?
22+
self.class.dependencies_present?
23+
end
24+
1225
def install
1326
require "appsignal/integrations/sidekiq"
1427
Appsignal::Probes.register :sidekiq, Appsignal::Probes::SidekiqProbe
1528

1629
::Sidekiq.configure_server do |config|
1730
config.error_handlers <<
1831
Appsignal::Integrations::SidekiqErrorHandler.new
32+
if config.respond_to? :death_handlers
33+
config.death_handlers <<
34+
Appsignal::Integrations::SidekiqDeathHandler.new
35+
end
1936

2037
config.server_middleware do |chain|
2138
if chain.respond_to? :prepend

lib/appsignal/integrations/sidekiq.rb

+37-18
Original file line numberDiff line numberDiff line change
@@ -4,32 +4,51 @@
44

55
module Appsignal
66
module Integrations
7+
# Handler for job death events. We get notified when a job has exhausted
8+
# its retries.
9+
#
10+
# This is called before the SidekiqErrorHandler so it doesn't need to worry
11+
# about completing the transaction.
12+
#
13+
# Introduced in Sidekiq 5.1.
14+
class SidekiqDeathHandler
15+
def call(_job_context, exception)
16+
return unless Appsignal.config[:sidekiq_report_errors] == "discard"
17+
18+
transaction = Appsignal::Transaction.current
19+
transaction.set_error(exception)
20+
end
21+
end
22+
723
# Error handler for Sidekiq to report errors from jobs and internal Sidekiq
824
# errors.
925
#
1026
# @api private
1127
class SidekiqErrorHandler
28+
# Sidekiq 7.1.5 introduced the third sidekiq_config argument. It is not
29+
# given on older Sidekiq versions.
1230
def call(exception, sidekiq_context, _sidekiq_config = nil)
13-
transaction =
14-
if Appsignal::Transaction.current?
15-
Appsignal::Transaction.current
16-
else
17-
# Sidekiq error outside of the middleware scope.
18-
# Can be a job JSON parse error or some other error happening in
19-
# Sidekiq.
20-
transaction =
21-
Appsignal::Transaction.create(
22-
SecureRandom.uuid, # Newly generated job id
23-
Appsignal::Transaction::BACKGROUND_JOB,
24-
Appsignal::Transaction::GenericRequest.new({})
25-
)
26-
transaction.set_action_if_nil("SidekiqInternal")
27-
transaction.set_metadata("sidekiq_error", sidekiq_context[:context])
28-
transaction.params = { :jobstr => sidekiq_context[:jobstr] }
29-
transaction
31+
if Appsignal::Transaction.current?
32+
if Appsignal.config[:sidekiq_report_errors] == "all"
33+
transaction = Appsignal::Transaction.current
34+
transaction.set_error(exception)
3035
end
36+
else
37+
# Sidekiq error outside of the middleware scope.
38+
# Can be a job JSON parse error or some other error happening in
39+
# Sidekiq.
40+
transaction =
41+
Appsignal::Transaction.create(
42+
SecureRandom.uuid, # Newly generated job id
43+
Appsignal::Transaction::BACKGROUND_JOB,
44+
Appsignal::Transaction::GenericRequest.new({})
45+
)
46+
transaction.set_action_if_nil("SidekiqInternal")
47+
transaction.set_metadata("sidekiq_error", sidekiq_context[:context])
48+
transaction.params = { :jobstr => sidekiq_context[:jobstr] }
49+
transaction.set_error(exception)
50+
end
3151

32-
transaction.set_error(exception)
3352
Appsignal::Transaction.complete_current!
3453
end
3554
end

spec/lib/appsignal/config_spec.rb

+28
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,7 @@
187187
:send_environment_metadata => true,
188188
:send_params => true,
189189
:send_session_data => true,
190+
:sidekiq_report_errors => "all",
190191
:transaction_debug_mode => false
191192
)
192193
end
@@ -583,6 +584,33 @@
583584
end
584585
end
585586
end
587+
588+
context "sidekiq_report_errors" do
589+
let(:config_options) { { :sidekiq_report_errors => "discard" } }
590+
before do
591+
if Appsignal::Hooks::SidekiqHook.instance_variable_defined?(:@version_5_1_or_higher)
592+
Appsignal::Hooks::SidekiqHook.remove_instance_variable(:@version_5_1_or_higher)
593+
end
594+
end
595+
596+
context "when Sidekiq >= 5.1 and 'discard'" do
597+
before { stub_const("Sidekiq::VERSION", "5.1.0") }
598+
599+
it "does not override the sidekiq_report_errors value" do
600+
expect(config[:sidekiq_report_errors]).to eq("discard")
601+
expect(config.override_config[:sidekiq_report_errors]).to be_nil
602+
end
603+
end
604+
605+
context "when Sidekiq < 5.1 and 'discard'" do
606+
before { stub_const("Sidekiq::VERSION", "5.0.0") }
607+
608+
it "sets sidekiq_report_errors to 'all'" do
609+
expect(config[:sidekiq_report_errors]).to eq("all")
610+
expect(config.override_config[:sidekiq_report_errors]).to eq("all")
611+
end
612+
end
613+
end
586614
end
587615

588616
describe "config keys" do

0 commit comments

Comments
 (0)