graphql-metrics 4.1.0 → 5.0.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/CHANGELOG.md +3 -0
- data/README.md +73 -5
- data/graphql_metrics.gemspec +1 -1
- data/lib/graphql/metrics/analyzer.rb +83 -21
- data/lib/graphql/metrics/tracer.rb +6 -4
- data/lib/graphql/metrics/version.rb +1 -1
- metadata +5 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8e2e02bf92da34c67af89cf112efe2525ca4a785f4f9169dd35320c687f5f9f4
|
4
|
+
data.tar.gz: 4ec1a5b37180e5e7be02934992347053d3d4a4d2f03866975600577704e141a1
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 213906efdd58b32a7cab558028fc96de33c9624f9e85ffbb572954486bb31bebc7e718f0aee4e6df4f7324d5c99c6cc1849d13a54e43bef1963a3b55517796e1
|
7
|
+
data.tar.gz: 4f6309cc4cdbe2244be1d744173f795df04e955af9cef6d52111f1181c074cece01dcf226ebe20a545f3f2d41306b5eb5226e4d90ec1a7ece70656707982934a
|
data/CHANGELOG.md
CHANGED
data/README.md
CHANGED
@@ -141,13 +141,22 @@ even if you opt to collect them by using `GraphQL::Metrics::Analyzer` and `Graph
|
|
141
141
|
store_metrics(:fields, metrics)
|
142
142
|
end
|
143
143
|
|
144
|
+
# @param metrics [Hash] Directive metrics
|
145
|
+
# {
|
146
|
+
# directive_name: "customDirective",
|
147
|
+
# }
|
148
|
+
def directive_extracted(metrics)
|
149
|
+
store_metrics(:directives, metrics)
|
150
|
+
end
|
151
|
+
|
144
152
|
# @param metrics [Hash] Argument usage metrics, including a few details about the query document itself, as well
|
145
153
|
# as resolver timings metrics, also ahering to the Apollo Tracing spec referred to above.
|
146
154
|
# {
|
147
|
-
# argument_name: "
|
155
|
+
# argument_name: "ids",
|
148
156
|
# argument_type_name: "ID",
|
149
|
-
#
|
150
|
-
#
|
157
|
+
# parent_name: "comments",
|
158
|
+
# grandparent_type_name: "Post",
|
159
|
+
# grandparent_node_name: "post",
|
151
160
|
# default_used: false,
|
152
161
|
# value_is_null: false,
|
153
162
|
# value: <GraphQL::Query::Arguments::ArgumentValue>,
|
@@ -171,6 +180,65 @@ even if you opt to collect them by using `GraphQL::Metrics::Analyzer` and `Graph
|
|
171
180
|
|
172
181
|
Once defined, you can opt into capturing all metrics seen above by simply including GraphQL::Metrics as a plugin on your
|
173
182
|
schema.
|
183
|
+
#### Metrics that are captured for arguments for fields and directives
|
184
|
+
|
185
|
+
Let's have a query example
|
186
|
+
|
187
|
+
```graphql
|
188
|
+
query PostDetails($postId: ID!, $commentsTags: [String!] = null, $val: Int!) @customDirective(val: $val) {
|
189
|
+
post(id: $postId) {
|
190
|
+
title @skip(if: true)
|
191
|
+
comments(ids: [1, 2], tags: $commentsTags) {
|
192
|
+
id
|
193
|
+
body
|
194
|
+
}
|
195
|
+
}
|
196
|
+
}
|
197
|
+
```
|
198
|
+
These are some of the arguments that are extracted
|
199
|
+
|
200
|
+
```ruby
|
201
|
+
{
|
202
|
+
argument_name: "if", # argument name
|
203
|
+
argument_type_name: "Boolean", # argument type
|
204
|
+
parent_name: "skip", # argument belongs to `skip` directive
|
205
|
+
grandparent_type_name: "__Directive", # argument was applied to directive
|
206
|
+
grandparent_node_name: "title", # directive was applied to field title
|
207
|
+
default_used: false, # check if default value was used
|
208
|
+
value_is_null: false, # check if value was null
|
209
|
+
value: <GraphQL::Execution::Interpreter::ArgumentValue>
|
210
|
+
}, {
|
211
|
+
argument_name: "id",
|
212
|
+
argument_name: "ids",
|
213
|
+
argument_type_name: "ID",
|
214
|
+
parent_name: "comments", # name of the node that argument was applied to
|
215
|
+
grandparent_type_name: "Post", # grandparent node to uniquely identify which node the argument was applied to
|
216
|
+
grandparent_node_name: "post", # name of grandparend node
|
217
|
+
default_used: false,
|
218
|
+
value_is_null: false,
|
219
|
+
value: <GraphQL::Execution::Interpreter::ArgumentValue>
|
220
|
+
}, {
|
221
|
+
argument_name: "id",
|
222
|
+
argument_type_name: "ID",
|
223
|
+
parent_name: "post", # argument applied to post field
|
224
|
+
grandparent_type_name: "QueryRoot", # post is a QueryRoot
|
225
|
+
grandparent_node_name: "query", # post field is already in the query root
|
226
|
+
parent_input_object_type: nil,
|
227
|
+
default_used: false,
|
228
|
+
value_is_null: false,
|
229
|
+
value: <GraphQL::Execution::Interpreter::ArgumentValue>
|
230
|
+
}, {
|
231
|
+
argument_name: "val",
|
232
|
+
argument_type_name: "Int",
|
233
|
+
parent_name: "customDirective", # argument belongs to `customDirective` directive
|
234
|
+
grandparent_type_name: "__Directive", # argument was applied to directive
|
235
|
+
grandparent_node_name: "query", # directive was applied to query
|
236
|
+
parent_input_object_type: nil,
|
237
|
+
default_used: false,
|
238
|
+
value_is_null: false,
|
239
|
+
value: <GraphQL::Execution::Interpreter::ArgumentValue>
|
240
|
+
}
|
241
|
+
```
|
174
242
|
|
175
243
|
### Make use of your analyzer
|
176
244
|
|
@@ -219,11 +287,11 @@ order in which methods defined on `GraphQL::Metrics::Instrumentation`, `GraphQL:
|
|
219
287
|
Although you ideally will not need to care about these details if you are simply using this gem to gather metrics in
|
220
288
|
your application as intended, here's a breakdown of the order of execution of the methods involved:
|
221
289
|
|
222
|
-
When used as instrumentation, an analyzer and tracing, the order of execution is:
|
290
|
+
When used as instrumentation, an analyzer and tracing, the order of execution is usually:
|
223
291
|
|
292
|
+
* Tracer.capture_multiplex_start_time
|
224
293
|
* Tracer.capture_lexing_time
|
225
294
|
* Tracer.capture_parsing_time
|
226
|
-
* Tracer.capture_multiplex_start_time
|
227
295
|
* Instrumentation.before_query (context setup)
|
228
296
|
* Tracer.capture_validation_time
|
229
297
|
* Tracer.capture_analysis_time
|
data/graphql_metrics.gemspec
CHANGED
@@ -30,7 +30,7 @@ Gem::Specification.new do |spec|
|
|
30
30
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
31
31
|
spec.require_paths = ["lib"]
|
32
32
|
|
33
|
-
spec.add_runtime_dependency "graphql", ">= 1.10
|
33
|
+
spec.add_runtime_dependency "graphql", ">= 1.12.10"
|
34
34
|
spec.add_runtime_dependency "concurrent-ruby", "~> 1.1.0"
|
35
35
|
spec.add_development_dependency "rake", "~> 10.0"
|
36
36
|
spec.add_development_dependency "minitest", "~> 5.0"
|
@@ -4,6 +4,7 @@ module GraphQL
|
|
4
4
|
module Metrics
|
5
5
|
class Analyzer < GraphQL::Analysis::AST::Analyzer
|
6
6
|
attr_reader :query
|
7
|
+
DIRECTIVE_TYPE = "__Directive"
|
7
8
|
|
8
9
|
def initialize(query_or_multiplex)
|
9
10
|
super
|
@@ -31,20 +32,16 @@ module GraphQL
|
|
31
32
|
}
|
32
33
|
end
|
33
34
|
|
34
|
-
def on_leave_field(node,
|
35
|
+
def on_leave_field(node, parent, visitor)
|
35
36
|
return if visitor.field_definition.introspection?
|
36
37
|
return if query.context[SKIP_FIELD_AND_ARGUMENT_METRICS]
|
37
38
|
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
nil
|
45
|
-
end
|
46
|
-
|
47
|
-
extract_arguments(argument_values, visitor.field_definition) if argument_values
|
39
|
+
argument_values = arguments_for(node, visitor.field_definition)
|
40
|
+
extract_arguments(
|
41
|
+
argument: argument_values,
|
42
|
+
definition: visitor.field_definition,
|
43
|
+
parent: parent
|
44
|
+
) if argument_values
|
48
45
|
|
49
46
|
static_metrics = {
|
50
47
|
field_name: node.name,
|
@@ -80,6 +77,17 @@ module GraphQL
|
|
80
77
|
end
|
81
78
|
end
|
82
79
|
|
80
|
+
def on_enter_directive(node, parent, visitor)
|
81
|
+
argument_values = arguments_for(node, visitor.directive_definition)
|
82
|
+
extract_arguments(
|
83
|
+
argument: argument_values,
|
84
|
+
definition: visitor.directive_definition,
|
85
|
+
parent: parent
|
86
|
+
) if argument_values
|
87
|
+
|
88
|
+
directive_extracted({ directive_name: node.name })
|
89
|
+
end
|
90
|
+
|
83
91
|
def result
|
84
92
|
return if GraphQL::Metrics.timings_capture_enabled?(query.context)
|
85
93
|
return if query.context[GraphQL::Metrics::SKIP_GRAPHQL_METRICS_ANALYSIS]
|
@@ -93,37 +101,91 @@ module GraphQL
|
|
93
101
|
|
94
102
|
private
|
95
103
|
|
96
|
-
def
|
104
|
+
def arguments_for(node, definition)
|
105
|
+
# Arguments can raise execution errors within their `prepare` methods
|
106
|
+
# which aren't properly handled during analysis so we have to handle
|
107
|
+
# them ourselves safely and return `nil`.
|
108
|
+
query.arguments_for(node, definition)
|
109
|
+
rescue ::GraphQL::ExecutionError
|
110
|
+
nil
|
111
|
+
end
|
112
|
+
|
113
|
+
def extract_arguments(argument:, definition:, parent:, parent_input_object: nil)
|
97
114
|
case argument
|
98
115
|
when Array
|
99
116
|
argument.each do |a|
|
100
|
-
extract_arguments(
|
117
|
+
extract_arguments(
|
118
|
+
argument: a,
|
119
|
+
definition: definition,
|
120
|
+
parent_input_object: parent_input_object,
|
121
|
+
parent: parent
|
122
|
+
)
|
101
123
|
end
|
102
124
|
when Hash
|
103
125
|
argument.each_value do |a|
|
104
|
-
extract_arguments(
|
126
|
+
extract_arguments(
|
127
|
+
argument: a,
|
128
|
+
definition: definition,
|
129
|
+
parent_input_object: parent_input_object,
|
130
|
+
parent: parent)
|
105
131
|
end
|
106
132
|
when ::GraphQL::Execution::Interpreter::Arguments
|
107
133
|
argument.each_value do |arg_val|
|
108
|
-
extract_arguments(
|
134
|
+
extract_arguments(
|
135
|
+
argument: arg_val,
|
136
|
+
definition: definition,
|
137
|
+
parent_input_object: parent_input_object,
|
138
|
+
parent: parent
|
139
|
+
)
|
109
140
|
end
|
110
141
|
when ::GraphQL::Execution::Interpreter::ArgumentValue
|
111
|
-
extract_argument(
|
112
|
-
|
142
|
+
extract_argument(
|
143
|
+
value: argument,
|
144
|
+
definition: definition,
|
145
|
+
parent_input_object: parent_input_object,
|
146
|
+
parent: parent
|
147
|
+
)
|
148
|
+
extract_arguments(
|
149
|
+
argument:argument.value,
|
150
|
+
definition: definition,
|
151
|
+
parent_input_object: parent_input_object,
|
152
|
+
parent: parent
|
153
|
+
)
|
113
154
|
when ::GraphQL::Schema::InputObject
|
114
155
|
input_object_argument_values = argument.arguments.argument_values.values
|
115
156
|
parent_input_object = input_object_argument_values.first&.definition&.owner
|
116
157
|
|
117
|
-
extract_arguments(
|
158
|
+
extract_arguments(
|
159
|
+
argument: input_object_argument_values,
|
160
|
+
definition: definition,
|
161
|
+
parent_input_object: parent_input_object,
|
162
|
+
parent: parent
|
163
|
+
)
|
118
164
|
end
|
119
165
|
end
|
120
166
|
|
121
|
-
def extract_argument(value
|
167
|
+
def extract_argument(value:, definition:, parent_input_object:, parent:)
|
168
|
+
parent_type_name = if definition.is_a?(GraphQL::Schema::Field)
|
169
|
+
definition.owner.graphql_name
|
170
|
+
else
|
171
|
+
DIRECTIVE_TYPE
|
172
|
+
end
|
173
|
+
|
174
|
+
grand_parent_name = case parent
|
175
|
+
when GraphQL::Language::Nodes::OperationDefinition
|
176
|
+
parent.operation_type
|
177
|
+
when GraphQL::Language::Nodes::InlineFragment
|
178
|
+
parent.type.name
|
179
|
+
else
|
180
|
+
parent.name
|
181
|
+
end
|
182
|
+
|
122
183
|
static_metrics = {
|
123
184
|
argument_name: value.definition.graphql_name,
|
124
185
|
argument_type_name: value.definition.type.unwrap.graphql_name,
|
125
|
-
|
126
|
-
|
186
|
+
parent_name: definition.graphql_name,
|
187
|
+
grandparent_type_name: parent_type_name,
|
188
|
+
grandparent_node_name: grand_parent_name,
|
127
189
|
parent_input_object_type: parent_input_object&.graphql_name,
|
128
190
|
default_used: value.default_used?,
|
129
191
|
value_is_null: value.value.nil?,
|
@@ -4,10 +4,12 @@ module GraphQL
|
|
4
4
|
module Metrics
|
5
5
|
class Tracer
|
6
6
|
# NOTE: These constants come from the graphql ruby gem and are in "chronological" order based on the phases
|
7
|
-
# of execution of the graphql-ruby gem
|
7
|
+
# of execution of the graphql-ruby gem, though old versions of the gem aren't always consistent about this (see
|
8
|
+
# https://github.com/rmosolgo/graphql-ruby/issues/3393). Most of them can be run multiple times when
|
9
|
+
# multiplexing multiple queries.
|
10
|
+
GRAPHQL_GEM_EXECUTE_MULTIPLEX_KEY = 'execute_multiplex' # wraps everything below this line; only run once
|
8
11
|
GRAPHQL_GEM_LEXING_KEY = 'lex' # may not trigger if the query is passed in pre-parsed
|
9
12
|
GRAPHQL_GEM_PARSING_KEY = 'parse' # may not trigger if the query is passed in pre-parsed
|
10
|
-
GRAPHQL_GEM_EXECUTE_MULTIPLEX_KEY = 'execute_multiplex' # wraps everything below this line; only run once
|
11
13
|
GRAPHQL_GEM_VALIDATION_KEY = 'validate'
|
12
14
|
GRAPHQL_GEM_ANALYZE_MULTIPLEX_KEY = 'analyze_multiplex' # wraps all `analyze_query`s; only run once
|
13
15
|
GRAPHQL_GEM_ANALYZE_QUERY_KEY = 'analyze_query'
|
@@ -24,12 +26,12 @@ module GraphQL
|
|
24
26
|
return yield if skip_tracing
|
25
27
|
|
26
28
|
case key
|
29
|
+
when GRAPHQL_GEM_EXECUTE_MULTIPLEX_KEY
|
30
|
+
return capture_multiplex_start_time(&block)
|
27
31
|
when GRAPHQL_GEM_LEXING_KEY
|
28
32
|
return capture_lexing_time(&block)
|
29
33
|
when GRAPHQL_GEM_PARSING_KEY
|
30
34
|
return capture_parsing_time(&block)
|
31
|
-
when GRAPHQL_GEM_EXECUTE_MULTIPLEX_KEY
|
32
|
-
return capture_multiplex_start_time(&block)
|
33
35
|
when GRAPHQL_GEM_VALIDATION_KEY
|
34
36
|
return capture_validation_time(context, &block)
|
35
37
|
when GRAPHQL_GEM_ANALYZE_MULTIPLEX_KEY
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: graphql-metrics
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 5.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Christopher Butcher
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-
|
11
|
+
date: 2021-06-24 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: graphql
|
@@ -16,14 +16,14 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - ">="
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: 1.10
|
19
|
+
version: 1.12.10
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - ">="
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: 1.10
|
26
|
+
version: 1.12.10
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: concurrent-ruby
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -241,7 +241,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
241
241
|
- !ruby/object:Gem::Version
|
242
242
|
version: '0'
|
243
243
|
requirements: []
|
244
|
-
rubygems_version: 3.
|
244
|
+
rubygems_version: 3.0.3
|
245
245
|
signing_key:
|
246
246
|
specification_version: 4
|
247
247
|
summary: GraphQL Metrics Extractor
|