/
platform_tracing.rb
125 lines (113 loc) · 3.86 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
# 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"
if data[:context]
field = data[:context].field
platform_key = field.metadata[:platform_key]
trace_field = true # implemented with instrumenter
else
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) { platform_field_key(data[:owner], field) }
else
nil
end
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) { 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) { 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 = {})
tracer = self.new(**options)
schema_defn.tracer(tracer)
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`.
#
# @return [String]
def cached_platform_key(ctx, key)
cache = ctx.namespace(self.class)[:platform_key_cache] ||= {}
cache.fetch(key) { cache[key] = yield }
end
end
end
end