File: platform_tracing.rb

package info (click to toggle)
ruby-graphql 2.2.17-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 9,584 kB
  • sloc: ruby: 67,505; ansic: 1,753; yacc: 831; javascript: 331; makefile: 6
file content (136 lines) | stat: -rw-r--r-- 4,882 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
# frozen_string_literal: true

module GraphQL
  module Tracing
    # Each platform provides:
    # - `.platform_keys`
    # - `#platform_trace`
    # - `#platform_field_key(type, field)`
    # @api private
    class PlatformTracing
      class << self
        attr_accessor :platform_keys

        def inherited(child_class)
          child_class.platform_keys = self.platform_keys
        end
      end

      def initialize(options = {})
        @options = options
        @platform_keys = self.class.platform_keys
        @trace_scalars = options.fetch(:trace_scalars, false)
      end

      def trace(key, data)
        case key
        when "lex", "parse", "validate", "analyze_query", "analyze_multiplex", "execute_query", "execute_query_lazy", "execute_multiplex"
          platform_key = @platform_keys.fetch(key)
          platform_trace(platform_key, key, data) do
            yield
          end
        when "execute_field", "execute_field_lazy"
          field = data[:field]
          return_type = field.type.unwrap
          trace_field = if return_type.kind.scalar? || return_type.kind.enum?
            (field.trace.nil? && @trace_scalars) || field.trace
          else
            true
          end

          platform_key = if trace_field
            context = data.fetch(:query).context
            cached_platform_key(context, field, :field) { platform_field_key(field.owner, field) }
          else
            nil
          end

          if platform_key && trace_field
            platform_trace(platform_key, key, data) do
              yield
            end
          else
            yield
          end
        when "authorized", "authorized_lazy"
          type = data.fetch(:type)
          context = data.fetch(:context)
          platform_key = cached_platform_key(context, type, :authorized) { platform_authorized_key(type) }
          platform_trace(platform_key, key, data) do
            yield
          end
        when "resolve_type", "resolve_type_lazy"
          type = data.fetch(:type)
          context = data.fetch(:context)
          platform_key = cached_platform_key(context, type, :resolve_type) { platform_resolve_type_key(type) }
          platform_trace(platform_key, key, data) do
            yield
          end
        else
          # it's a custom key
          yield
        end
      end

      def self.use(schema_defn, options = {})
        if options[:legacy_tracing]
          tracer = self.new(**options)
          schema_defn.tracer(tracer)
        else
          tracing_name = self.name.split("::").last
          trace_name = tracing_name.sub("Tracing", "Trace")
          if GraphQL::Tracing.const_defined?(trace_name, false)
            trace_module = GraphQL::Tracing.const_get(trace_name)
            warn("`use(#{self.name})` is deprecated, use the equivalent `trace_with(#{trace_module.name})` instead. More info: https://graphql-ruby.org/queries/tracing.html")
            schema_defn.trace_with(trace_module, **options)
          else
            warn("`use(#{self.name})` and `Tracing::PlatformTracing` are deprecated. Use a `trace_with(...)` module instead. More info: https://graphql-ruby.org/queries/tracing.html. Please open an issue on the GraphQL-Ruby repo if you want to discuss further!")
            tracer = self.new(**options)
            schema_defn.tracer(tracer)
          end
        end
      end

      private

      # Get the transaction name based on the operation type and name if possible, or fall back to a user provided
      # one. Useful for anonymous queries.
      def transaction_name(query)
        selected_op = query.selected_operation
        txn_name = if selected_op
          op_type = selected_op.operation_type
          op_name = selected_op.name || fallback_transaction_name(query.context) || "anonymous"
          "#{op_type}.#{op_name}"
        else
          "query.anonymous"
        end
        "GraphQL/#{txn_name}"
      end

      def fallback_transaction_name(context)
        context[:tracing_fallback_transaction_name]
      end

      attr_reader :options

      # Different kind of schema objects have different kinds of keys:
      #
      # - Object types: `.authorized`
      # - Union/Interface types: `.resolve_type`
      # - Fields: execution
      #
      # So, they can all share one cache.
      #
      # If the key isn't present, the given block is called and the result is cached for `key`.
      #
      # @param ctx [GraphQL::Query::Context]
      # @param key [Class, GraphQL::Field] A part of the schema
      # @param trace_phase [Symbol] The stage of execution being traced (used by OpenTelementry tracing)
      # @return [String]
      def cached_platform_key(ctx, key, trace_phase)
        cache = ctx.namespace(self.class)[:platform_key_cache] ||= {}
        cache.fetch(key) { cache[key] = yield }
      end
    end
  end
end