From 306164535845027f0152d53c1f60130db74c02c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20Daxb=C3=B6ck?= Date: Mon, 2 Mar 2026 09:47:44 +0100 Subject: [PATCH] fix(rails): Track request queue time in Rails middleware The Rails CaptureExceptions middleware subclass overrode start_transaction without calling super, so the http.server.request.time_in_queue attachment added in #2838 was silently skipped for all Rails apps. Extract queue time attachment into a private attach_queue_time method on the Rack base class so the Rails subclass can invoke it explicitly while keeping its own options hash (preserving the correct SPAN_ORIGIN constant "auto.http.rails" via lexical scoping). Fixes #2873 Co-Authored-By: Claude --- .../lib/sentry/rails/capture_exceptions.rb | 8 +++--- .../spec/sentry/rails/tracing_spec.rb | 27 +++++++++++++++++++ .../lib/sentry/rack/capture_exceptions.rb | 12 +++++---- 3 files changed, 38 insertions(+), 9 deletions(-) diff --git a/sentry-rails/lib/sentry/rails/capture_exceptions.rb b/sentry-rails/lib/sentry/rails/capture_exceptions.rb index 4f11a5eec..703ac97a9 100644 --- a/sentry-rails/lib/sentry/rails/capture_exceptions.rb +++ b/sentry-rails/lib/sentry/rails/capture_exceptions.rb @@ -43,12 +43,12 @@ def start_transaction(env, scope) origin: SPAN_ORIGIN } - if @assets_regexp && scope.transaction_name.match?(@assets_regexp) - options.merge!(sampled: false) - end + options.merge!(sampled: false) if @assets_regexp && scope.transaction_name.match?(@assets_regexp) transaction = Sentry.continue_trace(env, **options) - Sentry.start_transaction(transaction: transaction, custom_sampling_context: { env: env }, **options) + transaction = Sentry.start_transaction(transaction: transaction, custom_sampling_context: { env: env }, **options) + attach_queue_time(transaction, env) + transaction end def show_exceptions?(exception, env) diff --git a/sentry-rails/spec/sentry/rails/tracing_spec.rb b/sentry-rails/spec/sentry/rails/tracing_spec.rb index 6364c19f7..1adf2d7fb 100644 --- a/sentry-rails/spec/sentry/rails/tracing_spec.rb +++ b/sentry-rails/spec/sentry/rails/tracing_spec.rb @@ -110,6 +110,33 @@ expect(transport.events.count).to eq(1) end + + context "with X-Request-Start header" do + it "attaches queue time to the Rails transaction" do + p = Post.create! + timestamp = Time.now.to_f - 0.05 # 50ms ago + + get "/posts/#{p.id}", headers: { "X-Request-Start" => "t=#{timestamp}" } + + expect(response).to have_http_status(:ok) + transaction = transport.events.last + queue_time = transaction.contexts.dig(:trace, :data, "http.server.request.time_in_queue") + expect(queue_time).not_to be_nil + expect(queue_time).to be >= 0 + end + + it "does not attach queue time when capture_queue_time is disabled" do + p = Post.create! + timestamp = Time.now.to_f - 0.05 + + Sentry.configuration.capture_queue_time = false + get "/posts/#{p.id}", headers: { "X-Request-Start" => "t=#{timestamp}" } + + transaction = transport.events.last + queue_time = transaction.contexts.dig(:trace, :data, "http.server.request.time_in_queue") + expect(queue_time).to be_nil + end + end end context "with report_rescued_exceptions and public error pages" do diff --git a/sentry-ruby/lib/sentry/rack/capture_exceptions.rb b/sentry-ruby/lib/sentry/rack/capture_exceptions.rb index 8b38f6aac..a97f93079 100644 --- a/sentry-ruby/lib/sentry/rack/capture_exceptions.rb +++ b/sentry-ruby/lib/sentry/rack/capture_exceptions.rb @@ -73,13 +73,15 @@ def start_transaction(env, scope) transaction = Sentry.continue_trace(env, **options) transaction = Sentry.start_transaction(transaction: transaction, custom_sampling_context: { env: env }, **options) + attach_queue_time(transaction, env) + transaction + end - # attach queue time if available - if transaction && (queue_time = extract_queue_time(env)) - transaction.set_data(Span::DataConventions::HTTP_QUEUE_TIME_MS, queue_time) - end + def attach_queue_time(transaction, env) + return unless transaction + return unless (queue_time = extract_queue_time(env)) - transaction + transaction.set_data(Span::DataConventions::HTTP_QUEUE_TIME_MS, queue_time) end