graphql 2.4.3 → 2.5.2
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/analysis/analyzer.rb +2 -1
- data/lib/graphql/analysis/visitor.rb +38 -41
- data/lib/graphql/analysis.rb +15 -12
- data/lib/graphql/autoload.rb +38 -0
- data/lib/graphql/backtrace/table.rb +118 -55
- data/lib/graphql/backtrace.rb +1 -19
- data/lib/graphql/current.rb +6 -1
- 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/bootstrap-5.3.3.min.css +6 -0
- data/lib/graphql/dashboard/statics/bootstrap-5.3.3.min.js +7 -0
- data/lib/graphql/dashboard/statics/charts.min.css +1 -0
- data/lib/graphql/dashboard/statics/dashboard.css +30 -0
- data/lib/graphql/dashboard/statics/dashboard.js +143 -0
- data/lib/graphql/dashboard/statics/header-icon.png +0 -0
- data/lib/graphql/dashboard/statics/icon.png +0 -0
- 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/landings/show.html.erb +18 -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 +108 -0
- data/lib/graphql/dashboard.rb +158 -0
- data/lib/graphql/dataloader/active_record_association_source.rb +64 -0
- data/lib/graphql/dataloader/active_record_source.rb +26 -0
- data/lib/graphql/dataloader/async_dataloader.rb +21 -9
- data/lib/graphql/dataloader/null_dataloader.rb +1 -1
- data/lib/graphql/dataloader/source.rb +3 -3
- data/lib/graphql/dataloader.rb +43 -14
- data/lib/graphql/execution/interpreter/resolve.rb +3 -3
- data/lib/graphql/execution/interpreter/runtime/graphql_result.rb +34 -4
- data/lib/graphql/execution/interpreter/runtime.rb +94 -51
- data/lib/graphql/execution/interpreter.rb +16 -7
- data/lib/graphql/execution/multiplex.rb +1 -5
- data/lib/graphql/introspection/directive_location_enum.rb +1 -1
- data/lib/graphql/invalid_name_error.rb +1 -1
- data/lib/graphql/invalid_null_error.rb +5 -15
- data/lib/graphql/language/cache.rb +13 -0
- data/lib/graphql/language/document_from_schema_definition.rb +8 -7
- data/lib/graphql/language/lexer.rb +11 -4
- data/lib/graphql/language/nodes.rb +3 -0
- data/lib/graphql/language/parser.rb +15 -8
- data/lib/graphql/language/printer.rb +8 -8
- data/lib/graphql/language/static_visitor.rb +37 -33
- data/lib/graphql/language/visitor.rb +59 -55
- data/lib/graphql/pagination/connection.rb +1 -1
- data/lib/graphql/query/context/scoped_context.rb +1 -1
- data/lib/graphql/query/context.rb +6 -5
- data/lib/graphql/query/variable_validation_error.rb +1 -1
- data/lib/graphql/query.rb +19 -23
- data/lib/graphql/railtie.rb +7 -0
- data/lib/graphql/schema/addition.rb +1 -1
- data/lib/graphql/schema/argument.rb +7 -8
- data/lib/graphql/schema/build_from_definition.rb +99 -53
- data/lib/graphql/schema/directive/flagged.rb +3 -1
- data/lib/graphql/schema/directive.rb +2 -2
- data/lib/graphql/schema/enum.rb +36 -1
- data/lib/graphql/schema/enum_value.rb +1 -1
- data/lib/graphql/schema/field/scope_extension.rb +1 -1
- data/lib/graphql/schema/field.rb +27 -13
- data/lib/graphql/schema/field_extension.rb +1 -1
- data/lib/graphql/schema/has_single_input_argument.rb +3 -1
- data/lib/graphql/schema/input_object.rb +77 -40
- data/lib/graphql/schema/interface.rb +3 -2
- data/lib/graphql/schema/loader.rb +1 -1
- data/lib/graphql/schema/member/has_arguments.rb +25 -17
- data/lib/graphql/schema/member/has_dataloader.rb +60 -0
- data/lib/graphql/schema/member/has_deprecation_reason.rb +15 -0
- data/lib/graphql/schema/member/has_directives.rb +4 -4
- data/lib/graphql/schema/member/has_fields.rb +19 -1
- data/lib/graphql/schema/member/has_interfaces.rb +5 -5
- data/lib/graphql/schema/member/has_validators.rb +1 -1
- data/lib/graphql/schema/member/scoped.rb +1 -1
- data/lib/graphql/schema/member/type_system_helpers.rb +1 -1
- data/lib/graphql/schema/member.rb +1 -0
- data/lib/graphql/schema/object.rb +25 -8
- data/lib/graphql/schema/relay_classic_mutation.rb +0 -1
- data/lib/graphql/schema/resolver.rb +12 -10
- data/lib/graphql/schema/subscription.rb +52 -6
- data/lib/graphql/schema/union.rb +1 -1
- data/lib/graphql/schema/validator/required_validator.rb +23 -6
- data/lib/graphql/schema/validator.rb +1 -1
- data/lib/graphql/schema/visibility/migration.rb +1 -0
- data/lib/graphql/schema/visibility/profile.rb +95 -243
- data/lib/graphql/schema/visibility/visit.rb +190 -0
- data/lib/graphql/schema/visibility.rb +169 -28
- data/lib/graphql/schema/warden.rb +18 -5
- data/lib/graphql/schema.rb +93 -44
- data/lib/graphql/static_validation/all_rules.rb +1 -1
- data/lib/graphql/static_validation/rules/argument_names_are_unique.rb +1 -1
- data/lib/graphql/static_validation/rules/fields_have_appropriate_selections.rb +1 -1
- data/lib/graphql/static_validation/rules/fields_will_merge.rb +1 -1
- data/lib/graphql/static_validation/rules/no_definitions_are_present.rb +1 -1
- data/lib/graphql/static_validation/rules/not_single_subscription_error.rb +25 -0
- data/lib/graphql/static_validation/rules/required_arguments_are_present.rb +1 -1
- data/lib/graphql/static_validation/rules/subscription_root_exists_and_single_subscription_selection.rb +26 -0
- data/lib/graphql/static_validation/rules/unique_directives_per_location.rb +1 -1
- data/lib/graphql/static_validation/rules/variable_names_are_unique.rb +1 -1
- data/lib/graphql/static_validation/rules/variable_usages_are_allowed.rb +1 -1
- data/lib/graphql/static_validation/validation_context.rb +1 -0
- data/lib/graphql/static_validation/validator.rb +6 -1
- data/lib/graphql/subscriptions/action_cable_subscriptions.rb +1 -1
- data/lib/graphql/subscriptions/default_subscription_resolve_extension.rb +12 -10
- data/lib/graphql/subscriptions/event.rb +12 -1
- data/lib/graphql/subscriptions/serialize.rb +1 -1
- data/lib/graphql/subscriptions.rb +1 -1
- data/lib/graphql/testing/helpers.rb +7 -4
- data/lib/graphql/tracing/active_support_notifications_trace.rb +14 -3
- data/lib/graphql/tracing/active_support_notifications_tracing.rb +1 -1
- data/lib/graphql/tracing/appoptics_trace.rb +9 -1
- data/lib/graphql/tracing/appoptics_tracing.rb +7 -0
- data/lib/graphql/tracing/appsignal_trace.rb +32 -55
- data/lib/graphql/tracing/appsignal_tracing.rb +2 -0
- data/lib/graphql/tracing/call_legacy_tracers.rb +66 -0
- data/lib/graphql/tracing/data_dog_trace.rb +46 -158
- data/lib/graphql/tracing/data_dog_tracing.rb +2 -0
- data/lib/graphql/tracing/detailed_trace/memory_backend.rb +60 -0
- data/lib/graphql/tracing/detailed_trace/redis_backend.rb +72 -0
- data/lib/graphql/tracing/detailed_trace.rb +93 -0
- data/lib/graphql/tracing/legacy_hooks_trace.rb +1 -0
- data/lib/graphql/tracing/legacy_trace.rb +4 -61
- data/lib/graphql/tracing/monitor_trace.rb +283 -0
- data/lib/graphql/tracing/new_relic_trace.rb +47 -54
- data/lib/graphql/tracing/new_relic_tracing.rb +2 -0
- data/lib/graphql/tracing/notifications_trace.rb +182 -34
- data/lib/graphql/tracing/notifications_tracing.rb +2 -0
- data/lib/graphql/tracing/null_trace.rb +9 -0
- data/lib/graphql/tracing/perfetto_trace/trace.proto +141 -0
- data/lib/graphql/tracing/perfetto_trace/trace_pb.rb +33 -0
- data/lib/graphql/tracing/perfetto_trace.rb +734 -0
- data/lib/graphql/tracing/platform_trace.rb +5 -0
- data/lib/graphql/tracing/prometheus_trace/graphql_collector.rb +2 -0
- data/lib/graphql/tracing/prometheus_trace.rb +72 -68
- data/lib/graphql/tracing/prometheus_tracing.rb +2 -0
- data/lib/graphql/tracing/scout_trace.rb +32 -55
- data/lib/graphql/tracing/scout_tracing.rb +2 -0
- data/lib/graphql/tracing/sentry_trace.rb +62 -94
- data/lib/graphql/tracing/statsd_trace.rb +33 -41
- data/lib/graphql/tracing/statsd_tracing.rb +2 -0
- data/lib/graphql/tracing/trace.rb +111 -1
- data/lib/graphql/tracing.rb +31 -30
- data/lib/graphql/types/relay/connection_behaviors.rb +3 -3
- data/lib/graphql/types/relay/edge_behaviors.rb +2 -2
- data/lib/graphql/types.rb +18 -11
- data/lib/graphql/version.rb +1 -1
- data/lib/graphql.rb +55 -47
- metadata +146 -11
- data/lib/graphql/backtrace/inspect_result.rb +0 -38
- data/lib/graphql/backtrace/trace.rb +0 -93
- data/lib/graphql/backtrace/tracer.rb +0 -80
- data/lib/graphql/schema/null_mask.rb +0 -11
- data/lib/graphql/static_validation/rules/subscription_root_exists.rb +0 -17
@@ -0,0 +1,283 @@
|
|
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
|
+
def platform_field_key(field)
|
94
|
+
"#{field.path}.graphql"
|
95
|
+
end
|
96
|
+
|
97
|
+
def platform_authorized_key(type)
|
98
|
+
"#{type.graphql_name}.authorized.graphql"
|
99
|
+
end
|
100
|
+
|
101
|
+
def platform_resolve_type_key(type)
|
102
|
+
"#{type.graphql_name}.resolve_type.graphql"
|
103
|
+
end
|
104
|
+
|
105
|
+
def platform_source_class_key(source_class)
|
106
|
+
"#{source_class.name.gsub("::", "_")}.fetch.graphql"
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
module GraphQLPrefixNames
|
111
|
+
PARSE_NAME = "graphql.parse"
|
112
|
+
LEX_NAME = "graphql.lex"
|
113
|
+
VALIDATE_NAME = "graphql.validate"
|
114
|
+
EXECUTE_NAME = "graphql.execute"
|
115
|
+
ANALYZE_NAME = "graphql.analyze"
|
116
|
+
|
117
|
+
def platform_field_key(field)
|
118
|
+
"graphql.#{field.path}"
|
119
|
+
end
|
120
|
+
|
121
|
+
def platform_authorized_key(type)
|
122
|
+
"graphql.authorized.#{type.graphql_name}"
|
123
|
+
end
|
124
|
+
|
125
|
+
def platform_resolve_type_key(type)
|
126
|
+
"graphql.resolve_type.#{type.graphql_name}"
|
127
|
+
end
|
128
|
+
|
129
|
+
def platform_source_class_key(source_class)
|
130
|
+
"graphql.fetch.#{source_class.name.gsub("::", "_")}"
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
def self.create_module(monitor_name)
|
136
|
+
if !monitor_name.match?(/[a-z]+/)
|
137
|
+
raise ArgumentError, "monitor name must be [a-z]+, not: #{monitor_name.inspect}"
|
138
|
+
end
|
139
|
+
|
140
|
+
trace_module = Module.new
|
141
|
+
code = MODULE_TEMPLATE % {
|
142
|
+
monitor: monitor_name,
|
143
|
+
monitor_class: monitor_name.capitalize + "Monitor",
|
144
|
+
}
|
145
|
+
trace_module.module_eval(code, __FILE__, __LINE__ + 5) # rubocop:disable Development/NoEvalCop This is build-time with a validated string
|
146
|
+
trace_module
|
147
|
+
end
|
148
|
+
|
149
|
+
MODULE_TEMPLATE = <<~RUBY
|
150
|
+
# @param set_transaction_name [Boolean] If `true`, use the GraphQL operation name as the request name on the monitoring platform
|
151
|
+
# @param trace_scalars [Boolean] If `true`, leaf fields will be traced too (Scalars _and_ Enums)
|
152
|
+
# @param trace_authorized [Boolean] If `false`, skip tracing `authorized?` calls
|
153
|
+
# @param trace_resolve_type [Boolean] If `false`, skip tracing `resolve_type?` calls
|
154
|
+
def initialize(...)
|
155
|
+
setup_%{monitor}_monitor(...)
|
156
|
+
super
|
157
|
+
end
|
158
|
+
|
159
|
+
def setup_%{monitor}_monitor(trace_scalars: false, trace_authorized: true, trace_resolve_type: true, set_transaction_name: false, **kwargs)
|
160
|
+
@trace_scalars = trace_scalars
|
161
|
+
@trace_authorized = trace_authorized
|
162
|
+
@trace_resolve_type = trace_resolve_type
|
163
|
+
@set_transaction_name = set_transaction_name
|
164
|
+
@%{monitor} = %{monitor_class}.new(trace: self, set_transaction_name: @set_transaction_name, **kwargs)
|
165
|
+
end
|
166
|
+
|
167
|
+
def parse(query_string:)
|
168
|
+
@%{monitor}.instrument(:parse, query_string) do
|
169
|
+
super
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
def lex(query_string:)
|
174
|
+
@%{monitor}.instrument(:lex, query_string) do
|
175
|
+
super
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
def validate(query:, validate:)
|
180
|
+
@%{monitor}.instrument(:validate, query) do
|
181
|
+
super
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
def begin_analyze_multiplex(multiplex, analyzers)
|
186
|
+
begin_%{monitor}_event(:analyze, nil)
|
187
|
+
super
|
188
|
+
end
|
189
|
+
|
190
|
+
def end_analyze_multiplex(multiplex, analyzers)
|
191
|
+
finish_%{monitor}_event
|
192
|
+
super
|
193
|
+
end
|
194
|
+
|
195
|
+
def execute_multiplex(multiplex:)
|
196
|
+
@%{monitor}.instrument(:execute, multiplex) do
|
197
|
+
super
|
198
|
+
end
|
199
|
+
end
|
200
|
+
|
201
|
+
def begin_execute_field(field, object, arguments, query)
|
202
|
+
return_type = field.type.unwrap
|
203
|
+
trace_field = if return_type.kind.scalar? || return_type.kind.enum?
|
204
|
+
(field.trace.nil? && @trace_scalars) || field.trace
|
205
|
+
else
|
206
|
+
true
|
207
|
+
end
|
208
|
+
|
209
|
+
if trace_field
|
210
|
+
begin_%{monitor}_event(:execute_field, field)
|
211
|
+
end
|
212
|
+
super
|
213
|
+
end
|
214
|
+
|
215
|
+
def end_execute_field(field, object, arguments, query, result)
|
216
|
+
finish_%{monitor}_event
|
217
|
+
super
|
218
|
+
end
|
219
|
+
|
220
|
+
def dataloader_fiber_yield(source)
|
221
|
+
Fiber[PREVIOUS_EV_KEY] = finish_%{monitor}_event
|
222
|
+
super
|
223
|
+
end
|
224
|
+
|
225
|
+
def dataloader_fiber_resume(source)
|
226
|
+
prev_ev = Fiber[PREVIOUS_EV_KEY]
|
227
|
+
if prev_ev
|
228
|
+
begin_%{monitor}_event(prev_ev.keyword, prev_ev.object)
|
229
|
+
end
|
230
|
+
super
|
231
|
+
end
|
232
|
+
|
233
|
+
def begin_authorized(type, object, context)
|
234
|
+
@trace_authorized && begin_%{monitor}_event(:authorized, type)
|
235
|
+
super
|
236
|
+
end
|
237
|
+
|
238
|
+
def end_authorized(type, object, context, result)
|
239
|
+
finish_%{monitor}_event
|
240
|
+
super
|
241
|
+
end
|
242
|
+
|
243
|
+
def begin_resolve_type(type, value, context)
|
244
|
+
@trace_resolve_type && begin_%{monitor}_event(:resolve_type, type)
|
245
|
+
super
|
246
|
+
end
|
247
|
+
|
248
|
+
def end_resolve_type(type, value, context, resolved_type)
|
249
|
+
finish_%{monitor}_event
|
250
|
+
super
|
251
|
+
end
|
252
|
+
|
253
|
+
def begin_dataloader_source(source)
|
254
|
+
begin_%{monitor}_event(:dataloader_source, source)
|
255
|
+
super
|
256
|
+
end
|
257
|
+
|
258
|
+
def end_dataloader_source(source)
|
259
|
+
finish_%{monitor}_event
|
260
|
+
super
|
261
|
+
end
|
262
|
+
|
263
|
+
CURRENT_EV_KEY = :__graphql_%{monitor}_trace_event
|
264
|
+
PREVIOUS_EV_KEY = :__graphql_%{monitor}_trace_previous_event
|
265
|
+
|
266
|
+
private
|
267
|
+
|
268
|
+
def begin_%{monitor}_event(keyword, object)
|
269
|
+
Fiber[CURRENT_EV_KEY] = @%{monitor}.start_event(keyword, object)
|
270
|
+
end
|
271
|
+
|
272
|
+
def finish_%{monitor}_event
|
273
|
+
if ev = Fiber[CURRENT_EV_KEY]
|
274
|
+
ev.finish
|
275
|
+
# Use `false` to prevent grabbing an event from a parent fiber
|
276
|
+
Fiber[CURRENT_EV_KEY] = false
|
277
|
+
ev
|
278
|
+
end
|
279
|
+
end
|
280
|
+
RUBY
|
281
|
+
end
|
282
|
+
end
|
283
|
+
end
|
@@ -1,74 +1,67 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "graphql/tracing/monitor_trace"
|
4
|
+
|
3
5
|
module GraphQL
|
4
6
|
module Tracing
|
7
|
+
# A tracer for reporting GraphQL-Ruby time to New Relic
|
8
|
+
#
|
9
|
+
# @example Installing the tracer
|
10
|
+
# class MySchema < GraphQL::Schema
|
11
|
+
# trace_with GraphQL::Tracing::NewRelicTrace
|
12
|
+
#
|
13
|
+
# # Optional, use the operation name to set the new relic transaction name:
|
14
|
+
# # trace_with GraphQL::Tracing::NewRelicTrace, set_transaction_name: true
|
15
|
+
# end
|
16
|
+
#
|
17
|
+
# @example Installing without trace events for `authorized?` or `resolve_type` calls
|
18
|
+
# trace_with GraphQL::Tracing::NewRelicTrace, trace_authorized: false, trace_resolve_type: false
|
19
|
+
NewRelicTrace = MonitorTrace.create_module("newrelic")
|
5
20
|
module NewRelicTrace
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
@set_transaction_name = set_transaction_name
|
13
|
-
super
|
14
|
-
end
|
21
|
+
class NewrelicMonitor < MonitorTrace::Monitor
|
22
|
+
PARSE_NAME = "GraphQL/parse"
|
23
|
+
LEX_NAME = "GraphQL/lex"
|
24
|
+
VALIDATE_NAME = "GraphQL/validate"
|
25
|
+
EXECUTE_NAME = "GraphQL/execute"
|
26
|
+
ANALYZE_NAME = "GraphQL/analyze"
|
15
27
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
super
|
23
|
-
end
|
24
|
-
end
|
25
|
-
|
26
|
-
{
|
27
|
-
"lex" => "GraphQL/lex",
|
28
|
-
"parse" => "GraphQL/parse",
|
29
|
-
"validate" => "GraphQL/validate",
|
30
|
-
"analyze_query" => "GraphQL/analyze",
|
31
|
-
"analyze_multiplex" => "GraphQL/analyze",
|
32
|
-
"execute_multiplex" => "GraphQL/execute",
|
33
|
-
"execute_query_lazy" => "GraphQL/execute",
|
34
|
-
}.each do |trace_method, platform_key|
|
35
|
-
module_eval <<-RUBY, __FILE__, __LINE__
|
36
|
-
def #{trace_method}(**_keys)
|
37
|
-
NewRelic::Agent::MethodTracerHelpers.trace_execution_scoped("#{platform_key}") do
|
38
|
-
super
|
28
|
+
def instrument(keyword, payload, &block)
|
29
|
+
if keyword == :execute
|
30
|
+
query = payload.queries.first
|
31
|
+
set_this_txn_name = query.context[:set_new_relic_transaction_name]
|
32
|
+
if set_this_txn_name || (set_this_txn_name.nil? && @set_transaction_name)
|
33
|
+
NewRelic::Agent.set_transaction_name(transaction_name(query))
|
39
34
|
end
|
40
35
|
end
|
41
|
-
|
42
|
-
|
36
|
+
::NewRelic::Agent::MethodTracerHelpers.trace_execution_scoped(name_for(keyword, payload), &block)
|
37
|
+
end
|
43
38
|
|
44
|
-
|
45
|
-
|
46
|
-
yield
|
39
|
+
def platform_source_class_key(source_class)
|
40
|
+
"GraphQL/Source/#{source_class.name}"
|
47
41
|
end
|
48
|
-
end
|
49
42
|
|
50
|
-
|
51
|
-
|
52
|
-
yield
|
43
|
+
def platform_field_key(field)
|
44
|
+
"GraphQL/#{field.owner.graphql_name}/#{field.graphql_name}"
|
53
45
|
end
|
54
|
-
end
|
55
46
|
|
56
|
-
|
57
|
-
|
58
|
-
yield
|
47
|
+
def platform_authorized_key(type)
|
48
|
+
"GraphQL/Authorized/#{type.graphql_name}"
|
59
49
|
end
|
60
|
-
end
|
61
50
|
|
62
|
-
|
63
|
-
|
64
|
-
|
51
|
+
def platform_resolve_type_key(type)
|
52
|
+
"GraphQL/ResolveType/#{type.graphql_name}"
|
53
|
+
end
|
65
54
|
|
66
|
-
|
67
|
-
|
68
|
-
|
55
|
+
class Event < MonitorTrace::Monitor::Event
|
56
|
+
def start
|
57
|
+
name = @monitor.name_for(keyword, object)
|
58
|
+
@nr_ev = NewRelic::Agent::Tracer.start_transaction_or_segment(partial_name: name, category: :web)
|
59
|
+
end
|
69
60
|
|
70
|
-
|
71
|
-
|
61
|
+
def finish
|
62
|
+
@nr_ev.finish
|
63
|
+
end
|
64
|
+
end
|
72
65
|
end
|
73
66
|
end
|
74
67
|
end
|
@@ -1,45 +1,193 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require "graphql/tracing/platform_trace"
|
4
|
-
|
5
3
|
module GraphQL
|
6
4
|
module Tracing
|
7
|
-
# This implementation forwards events to a notification handler
|
8
|
-
# ActiveSupport::Notifications or Dry::Monitor::Notifications)
|
9
|
-
#
|
5
|
+
# This implementation forwards events to a notification handler
|
6
|
+
# (i.e. ActiveSupport::Notifications or Dry::Monitor::Notifications) with a `graphql` suffix.
|
7
|
+
#
|
8
|
+
# @see ActiveSupportNotificationsTrace ActiveSupport::Notifications integration
|
10
9
|
module NotificationsTrace
|
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
|
-
|
10
|
+
# @api private
|
11
|
+
class Adapter
|
12
|
+
def instrument(keyword, payload, &block)
|
13
|
+
raise "Implement #{self.class}#instrument to measure the block"
|
14
|
+
end
|
15
|
+
|
16
|
+
def start_event(keyword, payload)
|
17
|
+
ev = self.class::Event.new(keyword, payload)
|
18
|
+
ev.start
|
19
|
+
ev
|
20
|
+
end
|
21
|
+
|
22
|
+
class Event
|
23
|
+
def initialize(name, payload)
|
24
|
+
@name = name
|
25
|
+
@payload = payload
|
26
|
+
end
|
27
|
+
|
28
|
+
attr_reader :name, :payload
|
29
|
+
|
30
|
+
def start
|
31
|
+
raise "Implement #{self.class}#start to begin a new event (#{inspect})"
|
32
|
+
end
|
33
|
+
|
34
|
+
def finish
|
35
|
+
raise "Implement #{self.class}#finish to end this event (#{inspect})"
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
# @api private
|
41
|
+
class DryMonitorAdapter < Adapter
|
42
|
+
def instrument(...)
|
43
|
+
Dry::Monitor.instrument(...)
|
44
|
+
end
|
45
|
+
|
46
|
+
class Event < Adapter::Event
|
47
|
+
def start
|
48
|
+
Dry::Monitor.start(@name, @payload)
|
49
|
+
end
|
50
|
+
|
51
|
+
def finish
|
52
|
+
Dry::Monitor.stop(@name, @payload)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
# @api private
|
58
|
+
class ActiveSupportNotificationsAdapter < Adapter
|
59
|
+
def instrument(...)
|
60
|
+
ActiveSupport::Notifications.instrument(...)
|
61
|
+
end
|
62
|
+
|
63
|
+
class Event < Adapter::Event
|
64
|
+
def start
|
65
|
+
@asn_event = ActiveSupport::Notifications.instrumenter.new_event(@name, @payload)
|
66
|
+
@asn_event.start!
|
38
67
|
end
|
39
|
-
|
68
|
+
|
69
|
+
def finish
|
70
|
+
@asn_event.finish!
|
71
|
+
ActiveSupport::Notifications.publish_event(@asn_event)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
# @param engine [Class] The notifications engine to use, eg `Dry::Monitor` or `ActiveSupport::Notifications`
|
77
|
+
def initialize(engine:, **rest)
|
78
|
+
adapter = if defined?(Dry::Monitor) && engine == Dry::Monitor
|
79
|
+
DryMonitoringAdapter
|
80
|
+
elsif defined?(ActiveSupport::Notifications) && engine == ActiveSupport::Notifications
|
81
|
+
ActiveSupportNotificationsAdapter
|
82
|
+
else
|
83
|
+
engine
|
84
|
+
end
|
85
|
+
@notifications = adapter.new
|
86
|
+
super
|
87
|
+
end
|
88
|
+
|
89
|
+
def parse(**payload)
|
90
|
+
@notifications.instrument("parse.graphql", payload) do
|
91
|
+
super
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
def lex(**payload)
|
96
|
+
@notifications.instrument("lex.graphql", payload) do
|
97
|
+
super
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
def validate(**payload)
|
102
|
+
@notifications.instrument("validate.graphql", payload) do
|
103
|
+
super
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
def begin_analyze_multiplex(multiplex, analyzers)
|
108
|
+
begin_notifications_event("analyze.graphql", {multiplex: multiplex, analyzers: analyzers})
|
109
|
+
super
|
110
|
+
end
|
111
|
+
|
112
|
+
def end_analyze_multiplex(_multiplex, _analyzers)
|
113
|
+
finish_notifications_event
|
114
|
+
super
|
40
115
|
end
|
41
116
|
|
42
|
-
|
117
|
+
def execute_multiplex(**payload)
|
118
|
+
@notifications.instrument("execute.graphql", payload) do
|
119
|
+
super
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
def begin_execute_field(field, object, arguments, query)
|
124
|
+
begin_notifications_event("execute_field.graphql", {field: field, object: object, arguments: arguments, query: query})
|
125
|
+
super
|
126
|
+
end
|
127
|
+
|
128
|
+
def end_execute_field(_field, _object, _arguments, _query, _result)
|
129
|
+
finish_notifications_event
|
130
|
+
super
|
131
|
+
end
|
132
|
+
|
133
|
+
def dataloader_fiber_yield(source)
|
134
|
+
Fiber[PREVIOUS_EV_KEY] = finish_notifications_event
|
135
|
+
super
|
136
|
+
end
|
137
|
+
|
138
|
+
def dataloader_fiber_resume(source)
|
139
|
+
prev_ev = Fiber[PREVIOUS_EV_KEY]
|
140
|
+
begin_notifications_event(prev_ev.name, prev_ev.payload)
|
141
|
+
super
|
142
|
+
end
|
143
|
+
|
144
|
+
def begin_authorized(type, object, context)
|
145
|
+
begin_notifications_event("authorized.graphql", {type: type, object: object, context: context})
|
146
|
+
super
|
147
|
+
end
|
148
|
+
|
149
|
+
def end_authorized(type, object, context, result)
|
150
|
+
finish_notifications_event
|
151
|
+
super
|
152
|
+
end
|
153
|
+
|
154
|
+
def begin_resolve_type(type, object, context)
|
155
|
+
begin_notifications_event("resolve_type.graphql", {type: type, object: object, context: context})
|
156
|
+
super
|
157
|
+
end
|
158
|
+
|
159
|
+
def end_resolve_type(type, object, context, resolved_type)
|
160
|
+
finish_notifications_event
|
161
|
+
super
|
162
|
+
end
|
163
|
+
|
164
|
+
def begin_dataloader_source(source)
|
165
|
+
begin_notifications_event("dataloader_source.graphql", { source: source })
|
166
|
+
super
|
167
|
+
end
|
168
|
+
|
169
|
+
def end_dataloader_source(source)
|
170
|
+
finish_notifications_event
|
171
|
+
super
|
172
|
+
end
|
173
|
+
|
174
|
+
CURRENT_EV_KEY = :__notifications_graphql_trace_event
|
175
|
+
PREVIOUS_EV_KEY = :__notifications_graphql_trace_previous_event
|
176
|
+
|
177
|
+
private
|
178
|
+
|
179
|
+
def begin_notifications_event(name, payload)
|
180
|
+
Fiber[CURRENT_EV_KEY] = @notifications.start_event(name, payload)
|
181
|
+
end
|
182
|
+
|
183
|
+
def finish_notifications_event
|
184
|
+
if ev = Fiber[CURRENT_EV_KEY]
|
185
|
+
ev.finish
|
186
|
+
# Use `false` to prevent grabbing an event from a parent fiber
|
187
|
+
Fiber[CURRENT_EV_KEY] = false
|
188
|
+
ev
|
189
|
+
end
|
190
|
+
end
|
43
191
|
end
|
44
192
|
end
|
45
193
|
end
|