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
|