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.
Files changed (50) hide show
  1. checksums.yaml +4 -4
  2. data/lib/graphql/dashboard/detailed_traces.rb +47 -0
  3. data/lib/graphql/dashboard/installable.rb +22 -0
  4. data/lib/graphql/dashboard/limiters.rb +93 -0
  5. data/lib/graphql/dashboard/operation_store.rb +199 -0
  6. data/lib/graphql/dashboard/statics/charts.min.css +1 -0
  7. data/lib/graphql/dashboard/statics/dashboard.css +27 -0
  8. data/lib/graphql/dashboard/statics/dashboard.js +74 -9
  9. data/lib/graphql/dashboard/subscriptions.rb +96 -0
  10. data/lib/graphql/dashboard/views/graphql/dashboard/detailed_traces/traces/index.html.erb +45 -0
  11. data/lib/graphql/dashboard/views/graphql/dashboard/limiters/limiters/show.html.erb +62 -0
  12. data/lib/graphql/dashboard/views/graphql/dashboard/not_installed.html.erb +18 -0
  13. data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/clients/_form.html.erb +23 -0
  14. data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/clients/edit.html.erb +21 -0
  15. data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/clients/index.html.erb +69 -0
  16. data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/clients/new.html.erb +7 -0
  17. data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/index_entries/index.html.erb +39 -0
  18. data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/index_entries/show.html.erb +32 -0
  19. data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/operations/index.html.erb +81 -0
  20. data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/operations/show.html.erb +71 -0
  21. data/lib/graphql/dashboard/views/graphql/dashboard/subscriptions/subscriptions/show.html.erb +41 -0
  22. data/lib/graphql/dashboard/views/graphql/dashboard/subscriptions/topics/index.html.erb +55 -0
  23. data/lib/graphql/dashboard/views/graphql/dashboard/subscriptions/topics/show.html.erb +40 -0
  24. data/lib/graphql/dashboard/views/layouts/graphql/dashboard/application.html.erb +49 -1
  25. data/lib/graphql/dashboard.rb +45 -29
  26. data/lib/graphql/execution/interpreter.rb +3 -5
  27. data/lib/graphql/execution/multiplex.rb +1 -1
  28. data/lib/graphql/language/parser.rb +13 -6
  29. data/lib/graphql/query.rb +2 -4
  30. data/lib/graphql/static_validation/all_rules.rb +1 -1
  31. data/lib/graphql/static_validation/rules/not_single_subscription_error.rb +25 -0
  32. data/lib/graphql/static_validation/rules/subscription_root_exists_and_single_subscription_selection.rb +26 -0
  33. data/lib/graphql/tracing/active_support_notifications_trace.rb +7 -0
  34. data/lib/graphql/tracing/appoptics_tracing.rb +5 -0
  35. data/lib/graphql/tracing/appsignal_trace.rb +26 -61
  36. data/lib/graphql/tracing/data_dog_trace.rb +41 -164
  37. data/lib/graphql/tracing/monitor_trace.rb +285 -0
  38. data/lib/graphql/tracing/new_relic_trace.rb +34 -166
  39. data/lib/graphql/tracing/notifications_trace.rb +181 -37
  40. data/lib/graphql/tracing/perfetto_trace.rb +15 -18
  41. data/lib/graphql/tracing/prometheus_trace.rb +47 -74
  42. data/lib/graphql/tracing/scout_trace.rb +25 -59
  43. data/lib/graphql/tracing/sentry_trace.rb +57 -99
  44. data/lib/graphql/tracing/statsd_trace.rb +24 -47
  45. data/lib/graphql/tracing/trace.rb +0 -17
  46. data/lib/graphql/tracing.rb +1 -0
  47. data/lib/graphql/version.rb +1 -1
  48. metadata +25 -4
  49. data/lib/graphql/dashboard/views/graphql/dashboard/traces/index.html.erb +0 -63
  50. 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
- @set_action_name = set_action_name
18
+ rest[:set_transaction_name] ||= set_action_name
19
+ setup_appsignal_monitor(**rest)
21
20
  super
22
21
  end
23
22
 
24
- # rubocop:disable Development/NoEvalCop This eval takes static inputs at load-time
25
-
26
- {
27
- "lex" => "lex.graphql",
28
- "parse" => "parse.graphql",
29
- "validate" => "validate.graphql",
30
- "analyze_query" => "analyze.graphql",
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
- RUBY
54
- end
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
- def platform_authorized(platform_key)
65
- Appsignal.instrument(platform_key) do
66
- yield
67
- end
68
- end
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
- def platform_resolve_type(platform_key)
71
- Appsignal.instrument(platform_key) do
72
- yield
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
- # @param tracer [#trace] Deprecated
14
- # @param analytics_enabled [Boolean] Deprecated
15
- # @param analytics_sample_rate [Float] Deprecated
16
- def initialize(tracer: nil, analytics_enabled: false, analytics_sample_rate: 1.0, service: nil, **rest)
17
- if tracer.nil?
18
- tracer = defined?(Datadog::Tracing) ? Datadog::Tracing : Datadog.tracer
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
- if trace_method == 'execute_multiplex'
50
- <<-RUBY
51
- operations = data[:multiplex].queries.map(&:selected_operation_name).join(', ')
52
-
53
- resource = if operations.empty?
54
- first_query = data[:multiplex].queries.first
55
- fallback_transaction_name(first_query && first_query.context)
56
- else
57
- operations
58
- end
59
- span.resource = resource if resource
60
-
61
- # [Deprecated] will be removed in the future
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
- super
76
- end
77
- end
78
- RUBY
79
- end
43
+ span.resource = resource if resource
80
44
 
81
- # rubocop:enable Development/NoEvalCop
82
-
83
- def execute_field_span(span_key, query, field, ast_node, arguments, object)
84
- return_type = field.type.unwrap
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
- prepare_span_data = { query: query, field: field, ast_node: ast_node, arguments: arguments, object: object }
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
- def authorized(query:, type:, object:)
123
- authorized_span("authorized", object, type, query) do
124
- super(query: query, type: type, object: object)
125
- end
126
- end
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
- def resolve_type_lazy(object:, type:, query:)
154
- resolve_type_span("resolve_type_lazy", object, type, query) do
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