getsentry/sentry-ruby

Tracing does not work with ActiveController::Live actions

Opened this issue · 3 comments

Issue Description

ActiveController::Live is a Rails module to enable livestreaming of server responses. It's quite useful to do things like generating CSVs.

The way it works internally, however, makes it problematic to use with Sentry. ActionController::Live executes all actions in a separate thread from the original controller thread. This means that any Sentry traces will not work, as the middleware which starts a transaction only applies to a particular thread.

I was actually able to fix this by creating the following patch. It basically tells sentry to treat the spawned thread like it is a new part of a distributed trace. I'm not exactly familiar with this library, though, so it's sort of... Gross. There's probably a better way to do it.

module ActionControllerLiveSentryPatch
  def new_controller_thread
    opts = sentry_propagation_hash
    super do
      scope = Sentry.get_current_scope
      transaction = scope && Sentry.start_transaction(
        op: "action_controller.live_action",
        name: scope.transaction_name,
        source: scope.transaction_source,
        **opts
      )
      scope&.set_span(transaction)

      res = yield
      transaction&.set_http_status(response&.status)
      res
    rescue StandardError
      transaction&.set_http_status(500)
      raise
    ensure
      transaction&.finish
    end
  end

  def sentry_propagation_hash
    context = Sentry.get_current_scope&.propagation_context
    span = Sentry.get_current_scope&.get_span

    return {} unless context && span

    {
      trace_id: span.trace_id,
      parent_span_id: span.span_id,
      parent_sampled: span.sampled
    }.tap do |hash|
      baggage_str = span.to_baggage
      next unless baggage_str.present?

      hash.merge!(baggage: Sentry::Baggage.from_incoming_header(baggage_str))
    end
  end
end

Reproduction Steps

  1. Create a Rails application
  2. Add sentry with tracing enabled
  3. Include ActiveController::Live in some controller and add actions that would log to sentry (IE, they do DB queries or whatever)
  4. Make requests against that controller

Expected Behavior

We should see traces from the controller action in Sentry.

Actual Behavior

We only see one overall HTTP trace in Sentry for the entire action, with no sub-traces.

Ruby Version

3.3.3

SDK Version

5.18.2

Integration and Its Version

Rails 5.18.2

Sentry Config

Sentry.init do |config|
  config.dsn = ENV.fetch("SENTRY_DSN", nil)
  config.breadcrumbs_logger = %i[active_support_logger]

  # Set traces_sample_rate to 1.0 to capture 100%
  # of transactions for performance monitoring.
  # We recommend adjusting this value in production.
  config.traces_sample_rate = 1
  # Set profiles_sample_rate to profile 100%
  # of sampled transactions.
  # We recommend adjusting this value in production.
  config.profiles_sample_rate = 1

  config.logger = Logger.new($stdout)
  config.logger.level = :debug

  # config.enabled_environments = %w[production]
end

thanks @AnthonySuper yea it would be nice to have first class support for this. I'll add it to our backlog and hopefully add it in the coming month or so.

Awesome! If you would like I could also try to make a patch - The code I have now could probably be cleaned up and be automatically prepended by the gem.

yes PRs are always welcome to speed things up!