-
Notifications
You must be signed in to change notification settings - Fork 1.4k
/
platform_tracing.rb
136 lines (124 loc) · 4.77 KB
/
platform_tracing.rb
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