From dd7c691bc7a437e6036f6d8105db2a51e8cc3933 Mon Sep 17 00:00:00 2001 From: Siva Gollapalli Date: Wed, 16 Jul 2025 12:19:51 +0530 Subject: [PATCH 1/2] [#1444] Passing parent trace from web to sidekiq --- lib/elastic_apm/spies/sidekiq.rb | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/lib/elastic_apm/spies/sidekiq.rb b/lib/elastic_apm/spies/sidekiq.rb index 0857f36eb..5b7526a14 100644 --- a/lib/elastic_apm/spies/sidekiq.rb +++ b/lib/elastic_apm/spies/sidekiq.rb @@ -30,7 +30,13 @@ class SidekiqSpy class Middleware def call(_worker, job, queue) name = SidekiqSpy.name_for(job) - transaction = ElasticAPM.start_transaction(name, 'Sidekiq') + transaction = if job['trace_id'] + ElasticAPM.start_transaction(name, 'Sidekiq', trace_context: ElasticAPM::TraceContext.new( + traceparent: ElasticAPM::TraceContext::Traceparent.new(trace_id: job['trace_id']) + )) + else + ElasticAPM.start_transaction(name, 'Sidekiq') + end ElasticAPM.set_label(:queue, queue) yield @@ -47,6 +53,14 @@ def call(_worker, job, queue) end end + class ParentTraceMiddleware + def call(job_class_or_string, job, queue, redis_pool) + job.merge!( + 'trace_id' => ElasticAPM.current_transaction&.trace_id + ) + end + end + def self.name_for(job) klass = job['class'] @@ -64,6 +78,12 @@ def install_middleware chain.add Middleware end end + + Sidekiq.configure_client do |config| + config.client_middleware do |chain| + chain.add ParentTraceMiddleware + end + end end # @api private From e2e54d3f475f0cb95e050476f78d36a00cfa2648 Mon Sep 17 00:00:00 2001 From: Siva Gollapalli Date: Wed, 13 Aug 2025 15:33:44 +0530 Subject: [PATCH 2/2] [#1444] Added tests for passing job trace --- spec/elastic_apm/spies/sidekiq_spec.rb | 54 ++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/spec/elastic_apm/spies/sidekiq_spec.rb b/spec/elastic_apm/spies/sidekiq_spec.rb index d7f7ddbaa..a862a4b59 100644 --- a/spec/elastic_apm/spies/sidekiq_spec.rb +++ b/spec/elastic_apm/spies/sidekiq_spec.rb @@ -105,6 +105,60 @@ def perform expect(transaction['outcome']).to eq 'success' end + it 'creates child transaction when trace_id is present in job' do + with_agent do + parent_trace_id = 'abc123def456' + + # Simulate a job with trace_id from parent + job_with_trace = { + 'class' => 'ElasticAPM::HardWorker', + 'trace_id' => parent_trace_id, + 'args' => [] + } + + middleware = Spies::SidekiqSpy::Middleware.new + worker = HardWorker.new + + middleware.call(worker, job_with_trace, 'default') do + worker.perform + end + end + + wait_for transactions: 1 + + transaction, = @mock_intake.transactions + expect(transaction).to_not be_nil + expect(transaction['name']).to eq 'ElasticAPM::HardWorker' + expect(transaction['type']).to eq 'Sidekiq' + expect(transaction['trace_id']).to eq('abc123def456') + end + + it 'creates independent transaction when no trace_id is present' do + with_agent do + # Simulate a job without trace_id + job_without_trace = { + 'class' => 'ElasticAPM::HardWorker', + 'args' => [] + } + + middleware = Spies::SidekiqSpy::Middleware.new + worker = HardWorker.new + + middleware.call(worker, job_without_trace, 'default') do + worker.perform + end + end + + wait_for transactions: 1 + + transaction, = @mock_intake.transactions + expect(transaction).to_not be_nil + expect(transaction['name']).to eq 'ElasticAPM::HardWorker' + expect(transaction['type']).to eq 'Sidekiq' + # Should have its own trace_id since no parent was provided + expect(transaction['trace_id']).to_not be_nil + end + it 'reports errors' do with_agent do Sidekiq::Testing.inline! do