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 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