Skip to content

Commit 13eba8d

Browse files
committed
Record the Delayed Job enqueue as a producer span
In collector mode the `enqueue.delayed_job` event now opens a producer span, matching the other job backends and OpenTelemetry's own Delayed Job instrumentation. Delayed Job has no envelope to carry trace context across the enqueue/perform boundary, so -- like OpenTelemetry -- nothing is injected and the producer and consumer spans are not linked. Dual-modes the Delayed Job specs so the collector-mode span shapes are covered alongside the agent-mode ones.
1 parent 46f1e4f commit 13eba8d

2 files changed

Lines changed: 63 additions & 8 deletions

File tree

lib/appsignal/integrations/delayed_job_plugin.rb

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,11 @@ def self.enqueue_with_instrumentation(job, block)
3232
return block.call(job)
3333
end
3434

35-
Appsignal.instrument("enqueue.delayed_job", "enqueue #{job.name} job") do
35+
Appsignal.instrument(
36+
"enqueue.delayed_job",
37+
"enqueue #{job.name} job",
38+
:opentelemetry_kind => :producer
39+
) do
3640
block.call(job)
3741
end
3842
end

spec/lib/appsignal/integrations/delayed_job_plugin_spec.rb

Lines changed: 58 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ def perform_job(job)
3535

3636
describe "enqueueing a job" do
3737
context "with an active transaction" do
38-
it "records an enqueue event titled after the job" do
38+
it "records an enqueue event titled after the job", :agent_mode do
3939
start_agent
4040
transaction = http_request_transaction
4141
set_current_transaction(transaction)
@@ -46,15 +46,40 @@ def perform_job(job)
4646
expect(event).to_not be_nil
4747
expect(event["title"]).to eq("enqueue DelayedTestJob job")
4848
end
49+
50+
it "records the enqueue as a producer span", :collector_mode do
51+
start_collector_agent
52+
transaction = http_request_transaction
53+
set_current_transaction(transaction)
54+
55+
Delayed::Job.enqueue(DelayedTestJob.new)
56+
Appsignal::Transaction.complete_current!
57+
58+
# Delayed Job has no envelope to carry trace context, so -- like
59+
# OpenTelemetry's own instrumentation -- nothing is injected; the
60+
# producer span is not linked to the later perform.
61+
producer = event_spans.find { |s| s.name == "enqueue DelayedTestJob job" }
62+
expect(producer.attributes["appsignal.category"]).to eq("enqueue.delayed_job")
63+
expect(producer.kind).to eq(:producer)
64+
expect(producer.parent_span_id).to eq(root_span.span_id)
65+
end
4966
end
5067

5168
context "without an active transaction" do
52-
it "is a transparent pass-through" do
69+
it "is a transparent pass-through", :agent_mode do
5370
start_agent
5471

5572
expect { Delayed::Job.enqueue(DelayedTestJob.new) }
5673
.to change { Delayed::Backend::Test::Job.count }.by(1)
5774
end
75+
76+
it "emits no enqueue span", :collector_mode do
77+
start_collector_agent
78+
79+
Delayed::Job.enqueue(DelayedTestJob.new)
80+
81+
expect(span_exporter.finished_spans.map(&:name)).to_not include("enqueue.delayed_job")
82+
end
5883
end
5984

6085
if DependencyHelper.active_job_present?
@@ -72,7 +97,7 @@ def perform(*)
7297
end)
7398
end
7499

75-
it "does not record a second enqueue event" do
100+
it "does not record a second enqueue event", :agent_mode do
76101
start_agent
77102
transaction = http_request_transaction
78103
set_current_transaction(transaction)
@@ -89,7 +114,7 @@ def perform(*)
89114

90115
describe "performing a job" do
91116
context "with a normal job" do
92-
it "wraps it in a background_job transaction" do
117+
it "wraps it in a background_job transaction", :agent_mode do
93118
start_agent
94119
job = Delayed::Job.enqueue(DelayedTestJob.new)
95120

@@ -102,6 +127,19 @@ def perform(*)
102127
expect(transaction).to include_event(:name => "perform_job.delayed_job")
103128
expect(transaction).to include_tags("attempts" => 0, "priority" => 0)
104129
end
130+
131+
it "wraps it in a consumer span", :collector_mode do
132+
start_collector_agent
133+
job = Delayed::Job.enqueue(DelayedTestJob.new)
134+
135+
perform_job(job)
136+
Appsignal::Transaction.complete_current!
137+
138+
expect(root_span.kind).to eq(:consumer)
139+
expect(root_span.attributes["appsignal.action_name"]).to eq("DelayedTestJob#perform")
140+
expect(root_span.attributes["appsignal.namespace"]).to eq("background")
141+
expect(event_spans.map(&:name)).to include("perform_job.delayed_job")
142+
end
105143
end
106144

107145
context "with a job that raises" do
@@ -113,7 +151,7 @@ def perform
113151
end)
114152
end
115153

116-
it "records the error on the transaction" do
154+
it "records the error on the transaction", :agent_mode do
117155
start_agent
118156
job = Delayed::Job.enqueue(DelayedErrorJob.new)
119157

@@ -126,6 +164,19 @@ def perform
126164
expect(transaction).to have_action("DelayedErrorJob#perform")
127165
expect(transaction).to have_error("ExampleException", "uh oh")
128166
end
167+
168+
it "records the error on the consumer span", :collector_mode do
169+
start_collector_agent
170+
job = Delayed::Job.enqueue(DelayedErrorJob.new)
171+
172+
expect { perform_job(job) }.to raise_error(ExampleException, "uh oh")
173+
Appsignal::Transaction.complete_current!
174+
175+
expect(root_span.kind).to eq(:consumer)
176+
event = root_span.events.find { |e| e.name == "exception" }
177+
expect(event.attributes["exception.type"]).to eq("ExampleException")
178+
expect(event.attributes["exception.message"]).to eq("uh oh")
179+
end
129180
end
130181

131182
context "with a custom appsignal_name" do
@@ -140,7 +191,7 @@ def appsignal_name
140191
end)
141192
end
142193

143-
it "uses the custom name as the action" do
194+
it "uses the custom name as the action", :agent_mode do
144195
start_agent
145196
job = Delayed::Job.enqueue(DelayedNamedJob.new)
146197

@@ -163,7 +214,7 @@ def perform(*)
163214
end)
164215
end
165216

166-
it "uses the Active Job class as the action" do
217+
it "uses the Active Job class as the action", :agent_mode do
167218
start_agent
168219

169220
keep_transactions do

0 commit comments

Comments
 (0)