graphql 2.4.15 → 2.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/graphql/dashboard/detailed_traces.rb +47 -0
- data/lib/graphql/dashboard/installable.rb +22 -0
- data/lib/graphql/dashboard/limiters.rb +93 -0
- data/lib/graphql/dashboard/operation_store.rb +199 -0
- data/lib/graphql/dashboard/statics/charts.min.css +1 -0
- data/lib/graphql/dashboard/statics/dashboard.css +27 -0
- data/lib/graphql/dashboard/statics/dashboard.js +74 -9
- data/lib/graphql/dashboard/subscriptions.rb +96 -0
- data/lib/graphql/dashboard/views/graphql/dashboard/detailed_traces/traces/index.html.erb +45 -0
- data/lib/graphql/dashboard/views/graphql/dashboard/limiters/limiters/show.html.erb +62 -0
- data/lib/graphql/dashboard/views/graphql/dashboard/not_installed.html.erb +18 -0
- data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/clients/_form.html.erb +23 -0
- data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/clients/edit.html.erb +21 -0
- data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/clients/index.html.erb +69 -0
- data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/clients/new.html.erb +7 -0
- data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/index_entries/index.html.erb +39 -0
- data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/index_entries/show.html.erb +32 -0
- data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/operations/index.html.erb +81 -0
- data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/operations/show.html.erb +71 -0
- data/lib/graphql/dashboard/views/graphql/dashboard/subscriptions/subscriptions/show.html.erb +41 -0
- data/lib/graphql/dashboard/views/graphql/dashboard/subscriptions/topics/index.html.erb +55 -0
- data/lib/graphql/dashboard/views/graphql/dashboard/subscriptions/topics/show.html.erb +40 -0
- data/lib/graphql/dashboard/views/layouts/graphql/dashboard/application.html.erb +49 -1
- data/lib/graphql/dashboard.rb +45 -29
- data/lib/graphql/execution/interpreter.rb +3 -5
- data/lib/graphql/execution/multiplex.rb +1 -1
- data/lib/graphql/language/parser.rb +13 -6
- data/lib/graphql/query.rb +2 -4
- data/lib/graphql/static_validation/all_rules.rb +1 -1
- data/lib/graphql/static_validation/rules/not_single_subscription_error.rb +25 -0
- data/lib/graphql/static_validation/rules/subscription_root_exists_and_single_subscription_selection.rb +26 -0
- data/lib/graphql/tracing/active_support_notifications_trace.rb +7 -0
- data/lib/graphql/tracing/appoptics_tracing.rb +5 -0
- data/lib/graphql/tracing/appsignal_trace.rb +26 -61
- data/lib/graphql/tracing/data_dog_trace.rb +41 -164
- data/lib/graphql/tracing/monitor_trace.rb +285 -0
- data/lib/graphql/tracing/new_relic_trace.rb +34 -166
- data/lib/graphql/tracing/notifications_trace.rb +181 -37
- data/lib/graphql/tracing/perfetto_trace.rb +15 -18
- data/lib/graphql/tracing/prometheus_trace.rb +47 -74
- data/lib/graphql/tracing/scout_trace.rb +25 -59
- data/lib/graphql/tracing/sentry_trace.rb +57 -99
- data/lib/graphql/tracing/statsd_trace.rb +24 -47
- data/lib/graphql/tracing/trace.rb +0 -17
- data/lib/graphql/tracing.rb +1 -0
- data/lib/graphql/version.rb +1 -1
- metadata +25 -4
- data/lib/graphql/dashboard/views/graphql/dashboard/traces/index.html.erb +0 -63
- data/lib/graphql/static_validation/rules/subscription_root_exists.rb +0 -17
@@ -1,6 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require "graphql/tracing/platform_trace"
|
2
|
+
require "graphql/tracing/monitor_trace"
|
4
3
|
|
5
4
|
module GraphQL
|
6
5
|
module Tracing
|
@@ -10,80 +9,46 @@ module GraphQL
|
|
10
9
|
# class MySchema < GraphQL::Schema
|
11
10
|
# trace_with GraphQL::Tracing::AppsignalTrace
|
12
11
|
# end
|
12
|
+
AppsignalTrace = MonitorTrace.create_module("appsignal")
|
13
13
|
module AppsignalTrace
|
14
|
-
include PlatformTrace
|
15
|
-
|
16
14
|
# @param set_action_name [Boolean] If true, the GraphQL operation name will be used as the transaction name.
|
17
15
|
# This is not advised if you run more than one query per HTTP request, for example, with `graphql-client` or multiplexing.
|
18
16
|
# It can also be specified per-query with `context[:set_appsignal_action_name]`.
|
19
17
|
def initialize(set_action_name: false, **rest)
|
20
|
-
|
18
|
+
rest[:set_transaction_name] ||= set_action_name
|
19
|
+
setup_appsignal_monitor(**rest)
|
21
20
|
super
|
22
21
|
end
|
23
22
|
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
"analyze_multiplex" => "analyze.graphql",
|
32
|
-
"execute_multiplex" => "execute.graphql",
|
33
|
-
"execute_query" => "execute.graphql",
|
34
|
-
"execute_query_lazy" => "execute.graphql",
|
35
|
-
}.each do |trace_method, platform_key|
|
36
|
-
module_eval <<-RUBY, __FILE__, __LINE__
|
37
|
-
def #{trace_method}(**data)
|
38
|
-
#{
|
39
|
-
if trace_method == "execute_query"
|
40
|
-
<<-RUBY
|
41
|
-
set_this_txn_name = data[:query].context[:set_appsignal_action_name]
|
42
|
-
if set_this_txn_name == true || (set_this_txn_name.nil? && @set_action_name)
|
43
|
-
Appsignal::Transaction.current.set_action(transaction_name(data[:query]))
|
44
|
-
end
|
45
|
-
RUBY
|
46
|
-
end
|
47
|
-
}
|
48
|
-
|
49
|
-
Appsignal.instrument("#{platform_key}") do
|
50
|
-
super
|
23
|
+
class AppsignalMonitor < MonitorTrace::Monitor
|
24
|
+
def instrument(keyword, object)
|
25
|
+
if keyword == :execute
|
26
|
+
query = object.queries.first
|
27
|
+
set_this_txn_name = query.context[:set_appsignal_action_name]
|
28
|
+
if set_this_txn_name == true || (set_this_txn_name.nil? && @set_transaction_name)
|
29
|
+
Appsignal::Transaction.current.set_action(transaction_name(query))
|
51
30
|
end
|
52
31
|
end
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
# rubocop:enable Development/NoEvalCop
|
57
|
-
|
58
|
-
def platform_execute_field(platform_key)
|
59
|
-
Appsignal.instrument(platform_key) do
|
60
|
-
yield
|
32
|
+
Appsignal.instrument(name_for(keyword, object)) do
|
33
|
+
yield
|
34
|
+
end
|
61
35
|
end
|
62
|
-
end
|
63
36
|
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
37
|
+
include MonitorTrace::Monitor::GraphQLSuffixNames
|
38
|
+
class Event < GraphQL::Tracing::MonitorTrace::Monitor::Event
|
39
|
+
def start
|
40
|
+
Appsignal::Transaction.current.start_event
|
41
|
+
end
|
69
42
|
|
70
|
-
|
71
|
-
|
72
|
-
|
43
|
+
def finish
|
44
|
+
Appsignal::Transaction.current.finish_event(
|
45
|
+
@monitor.name_for(@keyword, @object),
|
46
|
+
"",
|
47
|
+
""
|
48
|
+
)
|
49
|
+
end
|
73
50
|
end
|
74
51
|
end
|
75
|
-
|
76
|
-
def platform_field_key(field)
|
77
|
-
"#{field.owner.graphql_name}.#{field.graphql_name}.graphql"
|
78
|
-
end
|
79
|
-
|
80
|
-
def platform_authorized_key(type)
|
81
|
-
"#{type.graphql_name}.authorized.graphql"
|
82
|
-
end
|
83
|
-
|
84
|
-
def platform_resolve_type_key(type)
|
85
|
-
"#{type.graphql_name}.resolve_type.graphql"
|
86
|
-
end
|
87
52
|
end
|
88
53
|
end
|
89
54
|
end
|
@@ -1,6 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require "graphql/tracing/platform_trace"
|
2
|
+
require "graphql/tracing/monitor_trace"
|
4
3
|
|
5
4
|
module GraphQL
|
6
5
|
module Tracing
|
@@ -9,186 +8,64 @@ module GraphQL
|
|
9
8
|
# class MySchema < GraphQL::Schema
|
10
9
|
# trace_with GraphQL::Tracing::DataDogTrace
|
11
10
|
# end
|
11
|
+
# @example Skipping `resolve_type` and `authorized` events
|
12
|
+
# trace_with GraphQL::Tracing::DataDogTrace, trace_authorized: false, trace_resolve_type: false
|
13
|
+
DataDogTrace = MonitorTrace.create_module("datadog")
|
12
14
|
module DataDogTrace
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
15
|
+
class DatadogMonitor < MonitorTrace::Monitor
|
16
|
+
def initialize(set_transaction_name:, service: nil, tracer: nil, **_rest)
|
17
|
+
super
|
18
|
+
if tracer.nil?
|
19
|
+
tracer = defined?(Datadog::Tracing) ? Datadog::Tracing : Datadog.tracer
|
20
|
+
end
|
21
|
+
@tracer = tracer
|
22
|
+
@service_name = service
|
23
|
+
@has_prepare_span = @trace.respond_to?(:prepare_span)
|
19
24
|
end
|
20
|
-
@tracer = tracer
|
21
|
-
|
22
|
-
@analytics_enabled = analytics_enabled
|
23
|
-
@analytics_sample_rate = analytics_sample_rate
|
24
|
-
|
25
|
-
@service_name = service
|
26
|
-
@has_prepare_span = respond_to?(:prepare_span)
|
27
|
-
super
|
28
|
-
end
|
29
|
-
|
30
|
-
# rubocop:disable Development/NoEvalCop This eval takes static inputs at load-time
|
31
25
|
|
32
|
-
|
33
|
-
'lex' => 'lex.graphql',
|
34
|
-
'parse' => 'parse.graphql',
|
35
|
-
'validate' => 'validate.graphql',
|
36
|
-
'analyze_query' => 'analyze.graphql',
|
37
|
-
'analyze_multiplex' => 'analyze.graphql',
|
38
|
-
'execute_multiplex' => 'execute.graphql',
|
39
|
-
'execute_query' => 'execute.graphql',
|
40
|
-
'execute_query_lazy' => 'execute.graphql',
|
41
|
-
}.each do |trace_method, trace_key|
|
42
|
-
module_eval <<-RUBY, __FILE__, __LINE__
|
43
|
-
def #{trace_method}(**data)
|
44
|
-
@tracer.trace("#{trace_key}", service: @service_name, type: 'custom') do |span|
|
45
|
-
span.set_tag('component', 'graphql')
|
46
|
-
span.set_tag('operation', '#{trace_method}')
|
26
|
+
attr_reader :tracer, :service_name
|
47
27
|
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
span.set_metric('_dd1.sr.eausr', @analytics_sample_rate) if @analytics_enabled
|
63
|
-
RUBY
|
64
|
-
elsif trace_method == 'execute_query'
|
65
|
-
<<-RUBY
|
66
|
-
span.set_tag(:selected_operation_name, data[:query].selected_operation_name)
|
67
|
-
span.set_tag(:selected_operation_type, data[:query].selected_operation.operation_type)
|
68
|
-
span.set_tag(:query_string, data[:query].query_string)
|
69
|
-
RUBY
|
70
|
-
end
|
71
|
-
}
|
72
|
-
if @has_prepare_span
|
73
|
-
prepare_span("#{trace_method.sub("platform_", "")}", data, span)
|
28
|
+
def instrument(keyword, object)
|
29
|
+
trace_key = name_for(keyword, object)
|
30
|
+
@tracer.trace(trace_key, service: @service_name, type: 'custom') do |span|
|
31
|
+
span.set_tag('component', 'graphql')
|
32
|
+
op_name = keyword.respond_to?(:name) ? keyword.name : keyword.to_s
|
33
|
+
span.set_tag('operation', op_name)
|
34
|
+
|
35
|
+
if keyword == :execute
|
36
|
+
operations = object.queries.map(&:selected_operation_name).join(', ')
|
37
|
+
first_query = object.queries.first
|
38
|
+
resource = if operations.empty?
|
39
|
+
fallback_transaction_name(first_query && first_query.context)
|
40
|
+
else
|
41
|
+
operations
|
74
42
|
end
|
75
|
-
|
76
|
-
end
|
77
|
-
end
|
78
|
-
RUBY
|
79
|
-
end
|
43
|
+
span.resource = resource if resource
|
80
44
|
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
trace_field = if return_type.kind.scalar? || return_type.kind.enum?
|
86
|
-
(field.trace.nil? && @trace_scalars) || field.trace
|
87
|
-
else
|
88
|
-
true
|
89
|
-
end
|
90
|
-
platform_key = if trace_field
|
91
|
-
@platform_key_cache[DataDogTrace].platform_field_key_cache[field]
|
92
|
-
else
|
93
|
-
nil
|
94
|
-
end
|
95
|
-
if platform_key && trace_field
|
96
|
-
@tracer.trace(platform_key, service: @service_name, type: 'custom') do |span|
|
97
|
-
span.set_tag('component', 'graphql')
|
98
|
-
span.set_tag('operation', span_key)
|
45
|
+
span.set_tag("selected_operation_name", first_query.selected_operation_name)
|
46
|
+
span.set_tag("selected_operation_type", first_query.selected_operation&.operation_type)
|
47
|
+
span.set_tag("query_string", first_query.query_string)
|
48
|
+
end
|
99
49
|
|
100
50
|
if @has_prepare_span
|
101
|
-
|
102
|
-
prepare_span(span_key, prepare_span_data, span)
|
51
|
+
@trace.prepare_span(keyword, object, span)
|
103
52
|
end
|
104
53
|
yield
|
105
54
|
end
|
106
|
-
else
|
107
|
-
yield
|
108
|
-
end
|
109
|
-
end
|
110
|
-
def execute_field(query:, field:, ast_node:, arguments:, object:)
|
111
|
-
execute_field_span("execute_field", query, field, ast_node, arguments, object) do
|
112
|
-
super(query: query, field: field, ast_node: ast_node, arguments: arguments, object: object)
|
113
|
-
end
|
114
|
-
end
|
115
|
-
|
116
|
-
def execute_field_lazy(query:, field:, ast_node:, arguments:, object:)
|
117
|
-
execute_field_span("execute_field_lazy", query, field, ast_node, arguments, object) do
|
118
|
-
super(query: query, field: field, ast_node: ast_node, arguments: arguments, object: object)
|
119
55
|
end
|
120
|
-
end
|
121
56
|
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
def authorized_span(span_key, object, type, query)
|
129
|
-
platform_key = @platform_key_cache[DataDogTrace].platform_authorized_key_cache[type]
|
130
|
-
@tracer.trace(platform_key, service: @service_name, type: 'custom') do |span|
|
131
|
-
span.set_tag('component', 'graphql')
|
132
|
-
span.set_tag('operation', span_key)
|
133
|
-
|
134
|
-
if @has_prepare_span
|
135
|
-
prepare_span(span_key, {object: object, type: type, query: query}, span)
|
57
|
+
include MonitorTrace::Monitor::GraphQLSuffixNames
|
58
|
+
class Event < MonitorTrace::Monitor::Event
|
59
|
+
def start
|
60
|
+
name = @monitor.name_for(keyword, object)
|
61
|
+
@dd_span = @monitor.tracer.trace(name, service: @monitor.service_name, type: 'custom')
|
136
62
|
end
|
137
|
-
yield
|
138
|
-
end
|
139
|
-
end
|
140
|
-
|
141
|
-
def authorized_lazy(object:, type:, query:)
|
142
|
-
authorized_span("authorized_lazy", object, type, query) do
|
143
|
-
super(query: query, type: type, object: object)
|
144
|
-
end
|
145
|
-
end
|
146
|
-
|
147
|
-
def resolve_type(object:, type:, query:)
|
148
|
-
resolve_type_span("resolve_type", object, type, query) do
|
149
|
-
super(object: object, query: query, type: type)
|
150
|
-
end
|
151
|
-
end
|
152
63
|
|
153
|
-
|
154
|
-
|
155
|
-
super(object: object, query: query, type: type)
|
156
|
-
end
|
157
|
-
end
|
158
|
-
|
159
|
-
def resolve_type_span(span_key, object, type, query)
|
160
|
-
platform_key = @platform_key_cache[DataDogTrace].platform_resolve_type_key_cache[type]
|
161
|
-
@tracer.trace(platform_key, service: @service_name, type: 'custom') do |span|
|
162
|
-
span.set_tag('component', 'graphql')
|
163
|
-
span.set_tag('operation', span_key)
|
164
|
-
|
165
|
-
if @has_prepare_span
|
166
|
-
prepare_span(span_key, {object: object, type: type, query: query}, span)
|
64
|
+
def finish
|
65
|
+
@dd_span.finish
|
167
66
|
end
|
168
|
-
yield
|
169
67
|
end
|
170
68
|
end
|
171
|
-
|
172
|
-
include PlatformTrace
|
173
|
-
|
174
|
-
# Implement this method in a subclass to apply custom tags to datadog spans
|
175
|
-
# @param key [String] The event being traced
|
176
|
-
# @param data [Hash] The runtime data for this event (@see GraphQL::Tracing for keys for each event)
|
177
|
-
# @param span [Datadog::Tracing::SpanOperation] The datadog span for this event
|
178
|
-
# def prepare_span(key, data, span)
|
179
|
-
# end
|
180
|
-
|
181
|
-
def platform_field_key(field)
|
182
|
-
field.path
|
183
|
-
end
|
184
|
-
|
185
|
-
def platform_authorized_key(type)
|
186
|
-
"#{type.graphql_name}.authorized"
|
187
|
-
end
|
188
|
-
|
189
|
-
def platform_resolve_type_key(type)
|
190
|
-
"#{type.graphql_name}.resolve_type"
|
191
|
-
end
|
192
69
|
end
|
193
70
|
end
|
194
71
|
end
|
@@ -0,0 +1,285 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module GraphQL
|
4
|
+
module Tracing
|
5
|
+
# This module is the basis for Ruby-level integration with third-party monitoring platforms.
|
6
|
+
# Platform-specific traces include this module and implement an adapter.
|
7
|
+
#
|
8
|
+
# @see ActiveSupportNotificationsTrace Integration via ActiveSupport::Notifications, an alternative approach.
|
9
|
+
module MonitorTrace
|
10
|
+
class Monitor
|
11
|
+
def initialize(trace:, set_transaction_name:, **_rest)
|
12
|
+
@trace = trace
|
13
|
+
@set_transaction_name = set_transaction_name
|
14
|
+
@platform_field_key_cache = Hash.new { |h, k| h[k] = platform_field_key(k) }.compare_by_identity
|
15
|
+
@platform_authorized_key_cache = Hash.new { |h, k| h[k] = platform_authorized_key(k) }.compare_by_identity
|
16
|
+
@platform_resolve_type_key_cache = Hash.new { |h, k| h[k] = platform_resolve_type_key(k) }.compare_by_identity
|
17
|
+
@platform_source_class_key_cache = Hash.new { |h, source_cls| h[source_cls] = platform_source_class_key(source_cls) }.compare_by_identity
|
18
|
+
end
|
19
|
+
|
20
|
+
def instrument(keyword, object, &block)
|
21
|
+
raise "Implement #{self.class}#instrument to measure the block"
|
22
|
+
end
|
23
|
+
|
24
|
+
def start_event(keyword, object)
|
25
|
+
ev = self.class::Event.new(self, keyword, object)
|
26
|
+
ev.start
|
27
|
+
ev
|
28
|
+
end
|
29
|
+
|
30
|
+
# Get the transaction name based on the operation type and name if possible, or fall back to a user provided
|
31
|
+
# one. Useful for anonymous queries.
|
32
|
+
def transaction_name(query)
|
33
|
+
selected_op = query.selected_operation
|
34
|
+
txn_name = if selected_op
|
35
|
+
op_type = selected_op.operation_type
|
36
|
+
op_name = selected_op.name || fallback_transaction_name(query.context) || "anonymous"
|
37
|
+
"#{op_type}.#{op_name}"
|
38
|
+
else
|
39
|
+
"query.anonymous"
|
40
|
+
end
|
41
|
+
"GraphQL/#{txn_name}"
|
42
|
+
end
|
43
|
+
|
44
|
+
def fallback_transaction_name(context)
|
45
|
+
context[:tracing_fallback_transaction_name]
|
46
|
+
end
|
47
|
+
|
48
|
+
def name_for(keyword, object)
|
49
|
+
case keyword
|
50
|
+
when :execute_field
|
51
|
+
@platform_field_key_cache[object]
|
52
|
+
when :authorized
|
53
|
+
@platform_authorized_key_cache[object]
|
54
|
+
when :resolve_type
|
55
|
+
@platform_resolve_type_key_cache[object]
|
56
|
+
when :dataloader_source
|
57
|
+
@platform_source_class_key_cache[object.class]
|
58
|
+
when :parse then self.class::PARSE_NAME
|
59
|
+
when :lex then self.class::LEX_NAME
|
60
|
+
when :execute then self.class::EXECUTE_NAME
|
61
|
+
when :analyze then self.class::ANALYZE_NAME
|
62
|
+
when :validate then self.class::VALIDATE_NAME
|
63
|
+
else
|
64
|
+
raise "No name for #{keyword.inspect}"
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
class Event
|
69
|
+
def initialize(monitor, keyword, object)
|
70
|
+
@monitor = monitor
|
71
|
+
@keyword = keyword
|
72
|
+
@object = object
|
73
|
+
end
|
74
|
+
|
75
|
+
attr_reader :keyword, :object
|
76
|
+
|
77
|
+
def start
|
78
|
+
raise "Implement #{self.class}#start to begin a new event (#{inspect})"
|
79
|
+
end
|
80
|
+
|
81
|
+
def finish
|
82
|
+
raise "Implement #{self.class}#finish to end this event (#{inspect})"
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
module GraphQLSuffixNames
|
87
|
+
PARSE_NAME = "parse.graphql"
|
88
|
+
LEX_NAME = "lex.graphql"
|
89
|
+
VALIDATE_NAME = "validate.graphql"
|
90
|
+
EXECUTE_NAME = "execute.graphql"
|
91
|
+
ANALYZE_NAME = "analyze.graphql"
|
92
|
+
|
93
|
+
private
|
94
|
+
|
95
|
+
def platform_field_key(field)
|
96
|
+
"#{field.path}.graphql"
|
97
|
+
end
|
98
|
+
|
99
|
+
def platform_authorized_key(type)
|
100
|
+
"#{type.graphql_name}.authorized.graphql"
|
101
|
+
end
|
102
|
+
|
103
|
+
def platform_resolve_type_key(type)
|
104
|
+
"#{type.graphql_name}.resolve_type.graphql"
|
105
|
+
end
|
106
|
+
|
107
|
+
def platform_source_key(source_class)
|
108
|
+
"#{source_class.name.gsub("::", "_").name.underscore}.fetch.graphql"
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
module GraphQLPrefixNames
|
113
|
+
PARSE_NAME = "graphql.parse"
|
114
|
+
LEX_NAME = "graphql.lex"
|
115
|
+
VALIDATE_NAME = "graphql.validate"
|
116
|
+
EXECUTE_NAME = "graphql.execute"
|
117
|
+
ANALYZE_NAME = "graphql.analyze"
|
118
|
+
|
119
|
+
def platform_field_key(field)
|
120
|
+
"graphql.#{field.path}"
|
121
|
+
end
|
122
|
+
|
123
|
+
def platform_authorized_key(type)
|
124
|
+
"graphql.authorized.#{type.graphql_name}"
|
125
|
+
end
|
126
|
+
|
127
|
+
def platform_resolve_type_key(type)
|
128
|
+
"graphql.resolve_type.#{type.graphql_name}"
|
129
|
+
end
|
130
|
+
|
131
|
+
def platform_source_class_key(source_class)
|
132
|
+
"graphql.fetch.#{source_class.name.gsub("::", "_")}"
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
def self.create_module(monitor_name)
|
138
|
+
if !monitor_name.match?(/[a-z]+/)
|
139
|
+
raise ArgumentError, "monitor name must be [a-z]+, not: #{monitor_name.inspect}"
|
140
|
+
end
|
141
|
+
|
142
|
+
trace_module = Module.new
|
143
|
+
code = MODULE_TEMPLATE % {
|
144
|
+
monitor: monitor_name,
|
145
|
+
monitor_class: monitor_name.capitalize + "Monitor",
|
146
|
+
}
|
147
|
+
trace_module.module_eval(code, __FILE__, __LINE__ + 5) # rubocop:disable Development/NoEvalCop This is build-time with a validated string
|
148
|
+
trace_module
|
149
|
+
end
|
150
|
+
|
151
|
+
MODULE_TEMPLATE = <<~RUBY
|
152
|
+
# @param set_transaction_name [Boolean] If `true`, use the GraphQL operation name as the request name on the monitoring platform
|
153
|
+
# @param trace_scalars [Boolean] If `true`, leaf fields will be traced too (Scalars _and_ Enums)
|
154
|
+
# @param trace_authorized [Boolean] If `false`, skip tracing `authorized?` calls
|
155
|
+
# @param trace_resolve_type [Boolean] If `false`, skip tracing `resolve_type?` calls
|
156
|
+
def initialize(...)
|
157
|
+
setup_%{monitor}_monitor(...)
|
158
|
+
super
|
159
|
+
end
|
160
|
+
|
161
|
+
def setup_%{monitor}_monitor(trace_scalars: false, trace_authorized: true, trace_resolve_type: true, set_transaction_name: false, **kwargs)
|
162
|
+
@trace_scalars = trace_scalars
|
163
|
+
@trace_authorized = trace_authorized
|
164
|
+
@trace_resolve_type = trace_resolve_type
|
165
|
+
@set_transaction_name = set_transaction_name
|
166
|
+
@%{monitor} = %{monitor_class}.new(trace: self, set_transaction_name: @set_transaction_name, **kwargs)
|
167
|
+
end
|
168
|
+
|
169
|
+
def parse(query_string:)
|
170
|
+
@%{monitor}.instrument(:parse, query_string) do
|
171
|
+
super
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
def lex(query_string:)
|
176
|
+
@%{monitor}.instrument(:lex, query_string) do
|
177
|
+
super
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
181
|
+
def validate(query:, validate:)
|
182
|
+
@%{monitor}.instrument(:validate, query) do
|
183
|
+
super
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
187
|
+
def begin_analyze_multiplex(multiplex, analyzers)
|
188
|
+
begin_%{monitor}_event(:analyze, nil)
|
189
|
+
super
|
190
|
+
end
|
191
|
+
|
192
|
+
def end_analyze_multiplex(multiplex, analyzers)
|
193
|
+
finish_%{monitor}_event
|
194
|
+
super
|
195
|
+
end
|
196
|
+
|
197
|
+
def execute_multiplex(multiplex:)
|
198
|
+
@%{monitor}.instrument(:execute, multiplex) do
|
199
|
+
super
|
200
|
+
end
|
201
|
+
end
|
202
|
+
|
203
|
+
def begin_execute_field(field, object, arguments, query)
|
204
|
+
return_type = field.type.unwrap
|
205
|
+
trace_field = if return_type.kind.scalar? || return_type.kind.enum?
|
206
|
+
(field.trace.nil? && @trace_scalars) || field.trace
|
207
|
+
else
|
208
|
+
true
|
209
|
+
end
|
210
|
+
|
211
|
+
if trace_field
|
212
|
+
begin_%{monitor}_event(:execute_field, field)
|
213
|
+
end
|
214
|
+
super
|
215
|
+
end
|
216
|
+
|
217
|
+
def end_execute_field(field, object, arguments, query, result)
|
218
|
+
finish_%{monitor}_event
|
219
|
+
super
|
220
|
+
end
|
221
|
+
|
222
|
+
def dataloader_fiber_yield(source)
|
223
|
+
Fiber[PREVIOUS_EV_KEY] = finish_%{monitor}_event
|
224
|
+
super
|
225
|
+
end
|
226
|
+
|
227
|
+
def dataloader_fiber_resume(source)
|
228
|
+
prev_ev = Fiber[PREVIOUS_EV_KEY]
|
229
|
+
if prev_ev
|
230
|
+
begin_%{monitor}_event(prev_ev.keyword, prev_ev.object)
|
231
|
+
end
|
232
|
+
super
|
233
|
+
end
|
234
|
+
|
235
|
+
def begin_authorized(type, object, context)
|
236
|
+
@trace_authorized && begin_%{monitor}_event(:authorized, type)
|
237
|
+
super
|
238
|
+
end
|
239
|
+
|
240
|
+
def end_authorized(type, object, context, result)
|
241
|
+
finish_%{monitor}_event
|
242
|
+
super
|
243
|
+
end
|
244
|
+
|
245
|
+
def begin_resolve_type(type, value, context)
|
246
|
+
@trace_resolve_type && begin_%{monitor}_event(:resolve_type, type)
|
247
|
+
super
|
248
|
+
end
|
249
|
+
|
250
|
+
def end_resolve_type(type, value, context, resolved_type)
|
251
|
+
finish_%{monitor}_event
|
252
|
+
super
|
253
|
+
end
|
254
|
+
|
255
|
+
def begin_dataloader_source(source)
|
256
|
+
begin_%{monitor}_event(:dataloader_source, source)
|
257
|
+
super
|
258
|
+
end
|
259
|
+
|
260
|
+
def end_dataloader_source(source)
|
261
|
+
finish_%{monitor}_event
|
262
|
+
super
|
263
|
+
end
|
264
|
+
|
265
|
+
CURRENT_EV_KEY = :__graphql_%{monitor}_trace_event
|
266
|
+
PREVIOUS_EV_KEY = :__graphql_%{monitor}_trace_previous_event
|
267
|
+
|
268
|
+
private
|
269
|
+
|
270
|
+
def begin_%{monitor}_event(keyword, object)
|
271
|
+
Fiber[CURRENT_EV_KEY] = @%{monitor}.start_event(keyword, object)
|
272
|
+
end
|
273
|
+
|
274
|
+
def finish_%{monitor}_event
|
275
|
+
if ev = Fiber[CURRENT_EV_KEY]
|
276
|
+
ev.finish
|
277
|
+
# Use `false` to prevent grabbing an event from a parent fiber
|
278
|
+
Fiber[CURRENT_EV_KEY] = false
|
279
|
+
ev
|
280
|
+
end
|
281
|
+
end
|
282
|
+
RUBY
|
283
|
+
end
|
284
|
+
end
|
285
|
+
end
|