sentry-ruby icon indicating copy to clipboard operation
sentry-ruby copied to clipboard

Sentry support for GraphQL-Ruby

Open Prithvirajkumar opened this issue 1 year ago • 7 comments

GraphQL::Tracing::Trace provides hooks to observe and modify events during runtime, link to docs.

Several monitoring platforms are supported out-of-the box by GraphQL-Ruby:

  • AppOptics
  • Appsignal
  • New Relic
  • Scout
  • Skylight
  • Datadog
  • Prometheus
  • Statsd

The requirement is for Sentry support in GraphQL-Ruby.

There is a GitHub Issue created for this here - https://github.com/rmosolgo/graphql-ruby/issues/3655

And it looks like they would welcome a PR for it as detailed here - https://github.com/rmosolgo/graphql-ruby/issues/3655#issuecomment-1322711825

Prithvirajkumar avatar Aug 16 '23 21:08 Prithvirajkumar

FWIW this is my custom sentry tracer:

# typed: false
# frozen_string_literal: true

module Tracing
  class SentryTracing < ::GraphQL::Tracing::PlatformTracing
    self.platform_keys = {
      lex: "lex.graphql",
      parse: "parse.graphql",
      validate: "validate.graphql",
      analyze_query: "analyze.graphql",
      analyze_multiplex: "analyze.graphql",
      execute_multiplex: "execute.graphql",
      execute_query: "execute.graphql",
      execute_query_lazy: "execute.graphql",
    }.stringify_keys

    # @param set_action_name [Boolean] If true, the GraphQL operation name will be used as the transaction name.
    #   This is not advised if you run more than one query per HTTP request, for example,
    #   with `graphql-client` or multiplexing.
    #   It can also be specified per-query with `context[:set_sentry_action_name]`.
    def initialize(options = {})
      @set_action_name = options.fetch(:set_action_name, false)
      super
    end

    def platform_trace(_platform_key, key, data)
      if key == "execute_query"
        set_this_txn_name = data[:query].context[:set_sentry_action_name]
        if set_this_txn_name == true ||
             (set_this_txn_name.nil? && @set_transaction_name)
          Sentry.configure_scope do |scope|
            scope.set_transaction_name(transaction_name(data[:query]))
          end
        end
      end

      # Ideally we could use `transaction.with_child_span`
      # but that resulted in an infinite loop of exceptions that
      # i was unable to debug. This is less pretty but works.
      #
      # Temporary stop creating spans as the transaction will
      # be oversized and never sent to Sentry
      # Sentry.with_child_span(op: platform_key) do
      #   yield
      # end

      # Instead just yield
      yield
    end

    def platform_field_key(type, field)
      "#{type.graphql_name}.#{field.graphql_name}.graphql"
    end

    def platform_authorized_key(type)
      "#{type.graphql_name}.authorized.graphql"
    end

    def platform_resolve_type_key(type)
      "#{type.graphql_name}.resolve_type.graphql"
    end

    private

    def transaction_name(query)
      [query.context[:sentry_action_name_prefix], super(query)].join("/")
    end
  end
end

I basically just sets the transaction name based on the graphql query name. For our usecase we needed to add support for a prefix to that name (we have 2 graphql endpoints - one public and one for admins). There's also support for spans but I had to disable that as the payload to sentry would get too large and then just be dropped.

Feel free to use this as a starting point if it's of any help.

jgrau avatar Sep 04 '23 08:09 jgrau

We have the same issue, would be really helpful to get support

palexvs avatar Sep 19 '23 18:09 palexvs

thx @Prithvirajkumar we'll try to schedule some dev time on this in the next months

sl0thentr0py avatar Sep 28 '23 11:09 sl0thentr0py

I've opened a PR on the ruby-graphql repo this morning for this.

patch0 avatar Jan 10 '24 14:01 patch0

oh thanks @patch0, we would also be okay with this code living on our side, but if it fits better with the graphql gem's design, also fine. I'll try to review this in the next days.

sl0thentr0py avatar Jan 11 '24 11:01 sl0thentr0py

oh thanks @patch0, we would also be okay with this code living on our side, but if it fits better with the graphql gem's design, also fine. I'll try to review this in the next days.

Can do. I think that's how skylight are doing it. Everyone else has put the code in graphql-ruby. AppOptics have taken a different approach that seems to compare a Gem version, so maybe the tracing is now in two places for them?

patch0 avatar Jan 11 '24 11:01 patch0

follow up with steps outlined here

https://github.com/rmosolgo/graphql-ruby/pull/4775#issuecomment-1892244170

sl0thentr0py avatar Jan 15 '24 14:01 sl0thentr0py