Skip to content

Commit 97641bf

Browse files
committed
refactor(sidekiq): isolate ActiveJob support
1 parent 2afc665 commit 97641bf

6 files changed

Lines changed: 183 additions & 102 deletions

File tree

‎sentry-sidekiq/lib/sentry-sidekiq.rb‎

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -20,19 +20,11 @@ def self.job_extension
2020
end
2121

2222
Sentry.register_job_extension(job_extension)
23-
24-
if defined?(::Rails::Railtie)
25-
class Railtie < ::Rails::Railtie
26-
config.after_initialize do
27-
next unless Sentry.initialized? && defined?(::Sentry::Rails)
28-
29-
Sentry.configuration.rails.skippable_job_adapters << JobExtension::ACTIVE_JOB_ADAPTER
30-
end
31-
end
32-
end
3323
end
3424
end
3525

26+
require "sentry/sidekiq/railtie" if defined?(::Rails::Railtie)
27+
3628
Sidekiq.configure_server do |config|
3729
config.error_handlers << Sentry::Sidekiq::ErrorHandler.new
3830
config.server_middleware do |chain|
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
# frozen_string_literal: true
2+
3+
module Sentry
4+
module Sidekiq
5+
module ActiveJobSupport
6+
module JobExtension
7+
ACTIVE_JOB_ADAPTER = "ActiveJob::QueueAdapters::SidekiqAdapter"
8+
SIDEKIQ_JOB_KEY = "_sentry_sidekiq_job"
9+
10+
def handles?(job)
11+
super || active_job?(job)
12+
end
13+
14+
def scope_data(job:)
15+
return super unless active_job?(job)
16+
17+
sidekiq_job = handed_over_sidekiq_job(job)
18+
sidekiq_job ? super(job: sidekiq_job) : {}
19+
end
20+
21+
def messaging_data(job:)
22+
return super unless active_job?(job)
23+
24+
sidekiq_job = handed_over_sidekiq_job(job)
25+
return {} unless sidekiq_job
26+
27+
super(job: sidekiq_job).merge(body_size: body_size(sidekiq_job)).compact
28+
end
29+
30+
def retry_info(job:)
31+
return super unless active_job?(job)
32+
33+
sidekiq_job = handed_over_sidekiq_job(job)
34+
super(job: sidekiq_job) if sidekiq_job
35+
end
36+
37+
def extract_trace_headers(job:, payload:)
38+
return super if sidekiq_job?(payload)
39+
40+
sidekiq_job = sidekiq_job_in(payload)
41+
super(job: job, payload: sidekiq_job) if sidekiq_job
42+
end
43+
44+
def transaction_name(job:)
45+
active_job?(job) ? "#{self.class::SIDEKIQ_NAME}/#{job.class.name}" : super
46+
end
47+
48+
def span_origin(job:)
49+
active_job?(job) ? self.class::SPAN_ORIGIN : super
50+
end
51+
52+
def integration(job:)
53+
active_job?(job) ? Sentry::Sidekiq : super
54+
end
55+
56+
def report_retried_exception?(job:, exception:)
57+
active_job?(job) ? false : super
58+
end
59+
60+
def performed_by_active_job?(sidekiq_job)
61+
!!sidekiq_job["wrapped"] && active_job_owns_adapter?
62+
end
63+
64+
def active_job_args(sidekiq_job, scope:)
65+
payload, *arguments = sidekiq_job["args"]
66+
return sidekiq_job["args"] unless payload.is_a?(Hash)
67+
68+
sentry_data = (payload["_sentry"] || {}).merge("delivery_queue" => sidekiq_job["queue"])
69+
sentry_data["execution_scope_id"] = scope.object_id
70+
[payload.merge(
71+
SIDEKIQ_JOB_KEY => sidekiq_job,
72+
"_sentry" => sentry_data
73+
), *arguments]
74+
end
75+
76+
private
77+
78+
def active_job?(job)
79+
return false unless defined?(::ActiveJob::Base) && job.is_a?(::ActiveJob::Base)
80+
81+
job.class.queue_adapter.class.name == ACTIVE_JOB_ADAPTER
82+
end
83+
84+
def active_job_owns_adapter?
85+
return false unless defined?(::Sentry::Rails::ActiveJobExtensions)
86+
return false unless ::Sentry::Rails.respond_to?(:active_job_owns_adapter?)
87+
88+
::Sentry::Rails.active_job_owns_adapter?(ACTIVE_JOB_ADAPTER)
89+
end
90+
91+
def handed_over_sidekiq_job(job)
92+
return unless active_job?(job) && job.respond_to?(:_sentry_job_data)
93+
94+
sidekiq_job_in(job._sentry_job_data)
95+
end
96+
97+
def sidekiq_job_in(active_job_payload)
98+
return unless active_job_payload.is_a?(Hash)
99+
100+
sidekiq_job = active_job_payload[SIDEKIQ_JOB_KEY]
101+
sidekiq_job if sidekiq_job?(sidekiq_job)
102+
end
103+
end
104+
end
105+
end
106+
end
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
# frozen_string_literal: true
2+
3+
module Sentry
4+
module Sidekiq
5+
module ActiveJobSupport
6+
module ServerMiddleware
7+
private
8+
9+
def execution_trace_headers(job, sidekiq_job)
10+
if job_extension.performed_by_active_job?(job) && job["args"].first.is_a?(Hash)
11+
headers = job["args"].first.dig("_sentry", "trace_propagation_headers")
12+
return headers if headers && !headers.empty?
13+
end
14+
15+
super
16+
end
17+
18+
def perform_job(job, sidekiq_job, scope:)
19+
return super unless job_extension.performed_by_active_job?(job)
20+
21+
args = job["args"]
22+
begin
23+
job["args"] = job_extension.active_job_args(sidekiq_job, scope: scope)
24+
super
25+
ensure
26+
job["args"] = args
27+
end
28+
end
29+
end
30+
end
31+
end
32+
end

‎sentry-sidekiq/lib/sentry/sidekiq/job_extension.rb‎

Lines changed: 17 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,15 @@
22

33
require "sentry/sidekiq/retry_policy"
44
require "sentry/sidekiq/context_filter"
5+
require "sentry/sidekiq/active_job_support/job_extension"
56

67
module Sentry
78
module Sidekiq
89
class JobExtension < Sentry::JobExtensions::Extension
10+
prepend ActiveJobSupport::JobExtension
11+
912
SPAN_ORIGIN = "auto.queue.sidekiq"
1013
SIDEKIQ_NAME = "Sidekiq"
11-
ACTIVE_JOB_ADAPTER = "ActiveJob::QueueAdapters::SidekiqAdapter"
12-
SIDEKIQ_JOB_KEY = "_sentry_sidekiq_job"
1314
ENQUEUED_AT_IN_MILLISECONDS = ::Gem::Version.new(::Sidekiq::VERSION) >= ::Gem::Version.new("8.0.0")
1415

1516
def initialize(retry_policy: RetryPolicy.build)
@@ -18,23 +19,7 @@ def initialize(retry_policy: RetryPolicy.build)
1819
end
1920

2021
def handles?(job)
21-
sidekiq_job?(job) || active_job?(job)
22-
end
23-
24-
def performed_by_active_job?(sidekiq_job)
25-
!!sidekiq_job["wrapped"] && active_job_owns_adapter?
26-
end
27-
28-
def active_job_args(sidekiq_job, scope:)
29-
payload, *arguments = sidekiq_job["args"]
30-
return sidekiq_job["args"] unless payload.is_a?(Hash)
31-
32-
sentry_data = (payload["_sentry"] || {}).merge("delivery_queue" => sidekiq_job["queue"])
33-
sentry_data["execution_scope_id"] = scope.object_id
34-
[payload.merge(
35-
SIDEKIQ_JOB_KEY => sidekiq_job,
36-
"_sentry" => sentry_data
37-
), *arguments]
22+
sidekiq_job?(job)
3823
end
3924

4025
def error_context_transaction_name(context)
@@ -45,28 +30,25 @@ def error_context_transaction_name(context)
4530
end
4631

4732
def scope_data(job:)
48-
sidekiq_job = sidekiq_job_for(job)
49-
return {} unless sidekiq_job
33+
return {} unless sidekiq_job?(job)
5034

5135
data = {
52-
contexts: { sidekiq: ContextFilter.new(sidekiq_job).filtered },
53-
tags: { queue: sidekiq_job["queue"], jid: sidekiq_job["jid"] }.merge(job_tags(sidekiq_job))
36+
contexts: { sidekiq: ContextFilter.new(job).filtered },
37+
tags: { queue: job["queue"], jid: job["jid"] }.merge(job_tags(job))
5438
}
55-
user = sidekiq_job["sentry_user"]
39+
user = job["sentry_user"]
5640
data[:user] = user if user.is_a?(Hash)
5741
data
5842
end
5943

6044
def messaging_data(job:)
61-
sidekiq_job = sidekiq_job_for(job)
62-
return {} unless sidekiq_job
45+
return {} unless sidekiq_job?(job)
6346

6447
{
6548
system: name,
66-
message_id: sidekiq_job["jid"],
67-
destination: sidekiq_job["queue"],
68-
receive_latency: receive_latency(sidekiq_job),
69-
body_size: active_job?(job) ? body_size(sidekiq_job) : nil
49+
message_id: job["jid"],
50+
destination: job["queue"],
51+
receive_latency: receive_latency(job)
7052
}.compact
7153
end
7254

@@ -75,10 +57,9 @@ def consumer_messaging_data(job)
7557
end
7658

7759
def retry_info(job:)
78-
sidekiq_job = sidekiq_job_for(job)
79-
return unless sidekiq_job
60+
return unless sidekiq_job?(job)
8061

81-
{ count: @retry_policy.retry_count(sidekiq_job) || 0, will_retry: @retry_policy.will_retry?(sidekiq_job) }
62+
{ count: @retry_policy.retry_count(job) || 0, will_retry: @retry_policy.will_retry?(job) }
8263
end
8364

8465
def inject_trace_headers(job:, payload:, headers:)
@@ -89,33 +70,20 @@ def inject_trace_headers(job:, payload:, headers:)
8970
end
9071

9172
def extract_trace_headers(job:, payload:)
92-
sidekiq_job = sidekiq_job_for_payload(payload)
93-
return unless sidekiq_job
73+
return unless sidekiq_job?(payload)
9474

95-
sidekiq_job["trace_propagation_headers"] || {}
75+
payload["trace_propagation_headers"] || {}
9676
end
9777

9878
def transaction_name(job:)
99-
name = active_job?(job) ? job.class.name : job_class(job)
79+
name = job_class(job)
10080
"#{SIDEKIQ_NAME}/#{name}" if name
10181
end
10282

103-
def span_origin(job:)
104-
SPAN_ORIGIN if active_job?(job)
105-
end
106-
107-
def integration(job:)
108-
Sentry::Sidekiq if active_job?(job)
109-
end
110-
11183
def producer(job:)
11284
@producer ||= Sentry::JobExtensions::Producer::Delegated.new
11385
end
11486

115-
def report_retried_exception?(job:, exception:)
116-
false if active_job?(job)
117-
end
118-
11987
private
12088

12189
def sidekiq_job?(job)
@@ -126,40 +94,6 @@ def job_class(job)
12694
job["wrapped"] || job["class"] if job.is_a?(Hash)
12795
end
12896

129-
def active_job?(job)
130-
return false unless defined?(::ActiveJob::Base) && job.is_a?(::ActiveJob::Base)
131-
132-
job.class.queue_adapter.class.name == ACTIVE_JOB_ADAPTER
133-
end
134-
135-
def active_job_owns_adapter?
136-
return false unless defined?(::Sentry::Rails::ActiveJobExtensions)
137-
return false unless ::Sentry::Rails.respond_to?(:active_job_owns_adapter?)
138-
139-
::Sentry::Rails.active_job_owns_adapter?(ACTIVE_JOB_ADAPTER)
140-
end
141-
142-
def sidekiq_job_for(job)
143-
sidekiq_job?(job) ? job : handed_over_sidekiq_job(job)
144-
end
145-
146-
def handed_over_sidekiq_job(job)
147-
return unless active_job?(job) && job.respond_to?(:_sentry_job_data)
148-
149-
sidekiq_job_in(job._sentry_job_data)
150-
end
151-
152-
def sidekiq_job_for_payload(payload)
153-
sidekiq_job?(payload) ? payload : sidekiq_job_in(payload)
154-
end
155-
156-
def sidekiq_job_in(active_job_payload)
157-
return unless active_job_payload.is_a?(Hash)
158-
159-
sidekiq_job = active_job_payload[SIDEKIQ_JOB_KEY]
160-
sidekiq_job if sidekiq_job?(sidekiq_job)
161-
end
162-
16397
def job_tags(job)
16498
Array(job["tags"]).each_with_object({}) { |name, tags_hash| tags_hash[:"sidekiq.#{name}"] = true }
16599
end
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# frozen_string_literal: true
2+
3+
module Sentry
4+
module Sidekiq
5+
class Railtie < ::Rails::Railtie
6+
config.after_initialize do
7+
next unless Sentry.initialized? && defined?(::Sentry::Rails)
8+
9+
Sentry.configuration.rails.skippable_job_adapters << ActiveJobSupport::JobExtension::ACTIVE_JOB_ADAPTER
10+
end
11+
end
12+
end
13+
end

0 commit comments

Comments
 (0)