diff --git a/sentry-rails/CHANGELOG.md b/sentry-rails/CHANGELOG.md index 9b75db936..9b15ba2e8 100644 --- a/sentry-rails/CHANGELOG.md +++ b/sentry-rails/CHANGELOG.md @@ -1,5 +1,9 @@ # Changelog +## Unreleased + +- Support performance monitoring on ActiveJob execution [#1304](https://github.com/getsentry/sentry-ruby/pull/1304) + ## 4.2.2 - Always define Sentry::SendEventJob to avoid eager load issues [#1286](https://github.com/getsentry/sentry-ruby/pull/1286) diff --git a/sentry-rails/lib/sentry/rails/active_job.rb b/sentry-rails/lib/sentry/rails/active_job.rb index 9d6e22822..3d32ea624 100644 --- a/sentry-rails/lib/sentry/rails/active_job.rb +++ b/sentry-rails/lib/sentry/rails/active_job.rb @@ -8,8 +8,8 @@ def self.included(base) if already_supported_by_specific_integration?(job) block.call else - Sentry.with_scope do - capture_and_reraise_with_sentry(job, block) + Sentry.with_scope do |scope| + capture_and_reraise_with_sentry(job, scope, block) end end else @@ -19,10 +19,20 @@ def self.included(base) end end - def capture_and_reraise_with_sentry(job, block) + def capture_and_reraise_with_sentry(job, scope, block) + scope.set_transaction_name(job.class.name) + span = Sentry.start_transaction(name: scope.transaction_name, op: "active_job") + + scope.set_span(span) + block.call + + span.set_http_status(200) + span.finish rescue Exception => e # rubocop:disable Lint/RescueException rescue_handler_result = rescue_with_handler(e) + span.set_http_status(500) + span.finish return rescue_handler_result if rescue_handler_result Sentry::Rails.capture_exception( diff --git a/sentry-rails/spec/sentry/rails/activejob_spec.rb b/sentry-rails/spec/sentry/rails/activejob_spec.rb index 265e1eb8f..6fe46b93c 100644 --- a/sentry-rails/spec/sentry/rails/activejob_spec.rb +++ b/sentry-rails/spec/sentry/rails/activejob_spec.rb @@ -19,6 +19,14 @@ def perform end end +class QueryPostJob < ActiveJob::Base + self.logger = nil + + def perform + Post.all.to_a + end +end + class RescuedActiveJob < MyActiveJob rescue_from TestError, :with => :rescue_callback @@ -85,6 +93,53 @@ def rescue_callback(error); end expect(Sentry.get_current_scope.extra).to eq({}) end + context "with tracing enabled" do + before do + make_basic_app do |config| + config.traces_sample_rate = 1.0 + end + end + + after do + transport.events = [] + + Sentry::Rails::Tracing.unsubscribe_tracing_events + Sentry::Rails::Tracing.remove_active_support_notifications_patch + end + + it "sends transaction" do + QueryPostJob.perform_now + + expect(transport.events.count).to eq(1) + transaction = transport.events.last + expect(transaction.transaction).to eq("QueryPostJob") + expect(transaction.contexts.dig(:trace, :trace_id)).to be_present + expect(transaction.contexts.dig(:trace, :span_id)).to be_present + expect(transaction.contexts.dig(:trace, :status)).to eq("ok") + + expect(transaction.spans.count).to eq(1) + expect(transaction.spans.first[:op]).to eq("sql.active_record") + end + + context "with error" do + it "sends transaction and associates it with the event" do + expect { MyActiveJob.perform_now }.to raise_error(MyActiveJob::TestError) + + expect(transport.events.count).to eq(2) + + transaction = transport.events.first + expect(transaction.transaction).to eq("MyActiveJob") + expect(transaction.contexts.dig(:trace, :trace_id)).to be_present + expect(transaction.contexts.dig(:trace, :span_id)).to be_present + expect(transaction.contexts.dig(:trace, :status)).to eq("internal_error") + + event = transport.events.last + expect(event.transaction).to eq("MyActiveJob") + expect(event.contexts.dig(:trace, :trace_id)).to eq(transaction.contexts.dig(:trace, :trace_id)) + end + end + end + context "when DeserializationError happens in user's jobs" do before do make_basic_app