graphql-metrics 5.0.8 → 6.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/dependabot.yml +20 -0
- data/.github/workflows/dependabot_auto_merge.yml +23 -0
- data/.github/workflows/ruby.yml +7 -3
- data/.gitignore +0 -1
- data/.rubocop.yml +0 -3
- data/.ruby-version +1 -1
- data/CHANGELOG.md +39 -0
- data/Gemfile.lock +70 -0
- data/README.md +33 -50
- data/gemfiles/{graphql_1.13.gemfile → graphql_2.3.gemfile} +1 -1
- data/graphql_metrics.gemspec +5 -4
- data/lib/graphql/metrics/analyzer.rb +1 -16
- data/lib/graphql/metrics/instrumentation.rb +28 -28
- data/lib/graphql/metrics/trace.rb +30 -106
- data/lib/graphql/metrics/version.rb +1 -1
- data/lib/graphql/metrics.rb +20 -39
- metadata +17 -30
- data/gemfiles/graphql_2.0.gemfile +0 -5
- data/lib/graphql/metrics/tracer.rb +0 -70
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a07023fbdc4beff81a392ca7dc3ae2e5c413807be91121b49ad05bb63e9f9a20
|
4
|
+
data.tar.gz: c8c533731167efe52c0da74f1d5ee147bd69efb9e3c2eedd0a7a7302d956a187
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e6f0d75f489119e17d55fc83d8bfe0aa0601ccb54dc5fa5e97fa435746b130ffe5dd1f95114be8c40c44f6350b2357381986c3602e317c7c76b10bc98b233c7e
|
7
|
+
data.tar.gz: 35613ab78775485ede19e0ba178f7151e2239b091e4b7832e9fcba06c7c97cba7cecddea05042cad131f426c877e96156a54cc91afee2d4c5e33973673f7bb3a
|
@@ -0,0 +1,20 @@
|
|
1
|
+
version: 2
|
2
|
+
registries:
|
3
|
+
ruby-shopify:
|
4
|
+
type: rubygems-server
|
5
|
+
url: https://pkgs.shopify.io/basic/gems/ruby
|
6
|
+
username: ${{secrets.RUBYGEMS_SERVER_PKGS_SHOPIFY_IO_USERNAME}}
|
7
|
+
password: ${{secrets.RUBYGEMS_SERVER_PKGS_SHOPIFY_IO_PASSWORD}}
|
8
|
+
github-com:
|
9
|
+
type: git
|
10
|
+
url: https://github.com
|
11
|
+
username: ${{secrets.DEPENDENCIES_GITHUB_USER}}
|
12
|
+
password: ${{secrets.DEPENDENCIES_GITHUB_TOKEN}}
|
13
|
+
updates:
|
14
|
+
- package-ecosystem: bundler
|
15
|
+
directory: "/"
|
16
|
+
schedule:
|
17
|
+
interval: weekly
|
18
|
+
open-pull-requests-limit: 100
|
19
|
+
insecure-external-code-execution: allow
|
20
|
+
registries: "*"
|
@@ -0,0 +1,23 @@
|
|
1
|
+
name: Dependabot auto-merge
|
2
|
+
on: pull_request_target
|
3
|
+
|
4
|
+
permissions:
|
5
|
+
pull-requests: write
|
6
|
+
contents: write
|
7
|
+
|
8
|
+
jobs:
|
9
|
+
dependabot:
|
10
|
+
runs-on: ubuntu-latest
|
11
|
+
if: ${{ github.event.pull_request.user.login == 'dependabot[bot]' }}
|
12
|
+
steps:
|
13
|
+
- name: Dependabot metadata
|
14
|
+
id: metadata
|
15
|
+
uses: dependabot/fetch-metadata@v2
|
16
|
+
with:
|
17
|
+
github-token: "${{ secrets.GITHUB_TOKEN }}"
|
18
|
+
- name: Enable auto-merge for Dependabot PRs
|
19
|
+
if: ${{ steps.metadata.outputs.update-type == 'version-update:semver-patch' }}
|
20
|
+
run: gh pr merge --auto --merge "$PR_URL"
|
21
|
+
env:
|
22
|
+
PR_URL: ${{ github.event.pull_request.html_url }}
|
23
|
+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
data/.github/workflows/ruby.yml
CHANGED
@@ -1,13 +1,17 @@
|
|
1
1
|
name: Tests
|
2
|
-
on:
|
2
|
+
on:
|
3
|
+
push:
|
4
|
+
pull_request:
|
5
|
+
schedule:
|
6
|
+
- cron: '0 8 * * 1'
|
3
7
|
jobs:
|
4
8
|
test:
|
5
9
|
strategy:
|
6
10
|
fail-fast: false
|
7
11
|
matrix:
|
8
12
|
os: [ubuntu-latest]
|
9
|
-
ruby: ['2.7', '3.0', '3.1', '3.2']
|
10
|
-
gemfile: ['
|
13
|
+
ruby: ['2.7', '3.0', '3.1', '3.2', '3.3']
|
14
|
+
gemfile: ['graphql_2.3', 'graphql_head']
|
11
15
|
runs-on: ${{ matrix.os }}
|
12
16
|
env:
|
13
17
|
BUNDLE_GEMFILE: ${{ github.workspace }}/gemfiles/${{ matrix.gemfile }}.gemfile
|
data/.gitignore
CHANGED
data/.rubocop.yml
CHANGED
data/.ruby-version
CHANGED
@@ -1 +1 @@
|
|
1
|
-
3.
|
1
|
+
3.3.1
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,42 @@
|
|
1
|
+
6.0.0
|
2
|
+
-----
|
3
|
+
|
4
|
+
**Usage change**
|
5
|
+
Due to Instrumentation plugins being deprecated in the `graphql` gem, `graphql-metrics` has been refactored to use the [new `Tracing` API](https://graphql-ruby.org/queries/tracing.html).
|
6
|
+
This changes how `graphql-metrics` is used in your application.
|
7
|
+
|
8
|
+
Before:
|
9
|
+
```ruby
|
10
|
+
class MySchema < GraphQL::Schema
|
11
|
+
instrument :query, GraphQLMetrics::Instrumentation.new
|
12
|
+
query_analyzer MyMetricsAnalyzer
|
13
|
+
tracer GraphQL::Metrics::Tracer.new
|
14
|
+
end
|
15
|
+
```
|
16
|
+
|
17
|
+
After:
|
18
|
+
```ruby
|
19
|
+
class MySchema < GraphQL::Schema
|
20
|
+
use GraphQL::Metrics, analyzer: MyMetricsAnalyzer
|
21
|
+
end
|
22
|
+
```
|
23
|
+
|
24
|
+
Other breaking changes:
|
25
|
+
* `graphql` >= 2.3 is now required.
|
26
|
+
* Apollo tracing spec support has been removed (the spec has been deprecated for years). All `start_time_offset` metrics have been removed.
|
27
|
+
* Support for the old tracing API has been removed.
|
28
|
+
* The `multiplex_start_time_monotonic` operation mettric has been removed.
|
29
|
+
|
30
|
+
|
31
|
+
Changes:
|
32
|
+
- [87](https://github.com/Shopify/graphql-metrics/pull/87) Migrate Instrumentation to tracing
|
33
|
+
- [88](https://github.com/Shopify/graphql-metrics/pull/88) Update `analyzer` option
|
34
|
+
- [86](https://github.com/Shopify/graphql-metrics/pull/86) Refactor static metric mode
|
35
|
+
- [85](https://github.com/Shopify/graphql-metrics/pull/85) Remove old tracing API support
|
36
|
+
- [84](https://github.com/Shopify/graphql-metrics/pull/84) Remove Apollo tracing spec support
|
37
|
+
- [80](https://github.com/Shopify/graphql-metrics/pull/80) Support new parser
|
38
|
+
- [79](https://github.com/Shopify/graphql-metrics/pull/79) Centralize Ruby Version to `.ruby-version`
|
39
|
+
|
1
40
|
5.0.8
|
2
41
|
-----
|
3
42
|
- [76](https://github.com/Shopify/graphql-metrics/pull/76) Reduce object allocations
|
data/Gemfile.lock
ADDED
@@ -0,0 +1,70 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
graphql-metrics (6.0.0)
|
5
|
+
graphql (>= 2.3)
|
6
|
+
|
7
|
+
GEM
|
8
|
+
remote: https://rubygems.org/
|
9
|
+
specs:
|
10
|
+
activesupport (6.1.7.5)
|
11
|
+
concurrent-ruby (~> 1.0, >= 1.0.2)
|
12
|
+
i18n (>= 1.6, < 2)
|
13
|
+
minitest (>= 5.1)
|
14
|
+
tzinfo (~> 2.0)
|
15
|
+
zeitwerk (~> 2.3)
|
16
|
+
base64 (0.2.0)
|
17
|
+
byebug (11.1.3)
|
18
|
+
coderay (1.1.3)
|
19
|
+
concurrent-ruby (1.2.3)
|
20
|
+
diffy (3.4.2)
|
21
|
+
fakeredis (0.9.2)
|
22
|
+
redis (~> 4.8)
|
23
|
+
graphql (2.3.0)
|
24
|
+
base64
|
25
|
+
graphql-batch (0.6.0)
|
26
|
+
graphql (>= 1.12.18, < 3)
|
27
|
+
promise.rb (~> 0.7.2)
|
28
|
+
hashdiff (1.1.0)
|
29
|
+
i18n (1.14.4)
|
30
|
+
concurrent-ruby (~> 1.0)
|
31
|
+
method_source (1.0.0)
|
32
|
+
minitest (5.22.3)
|
33
|
+
minitest-focus (1.4.0)
|
34
|
+
minitest (>= 4, < 6)
|
35
|
+
mocha (2.1.0)
|
36
|
+
ruby2_keywords (>= 0.0.5)
|
37
|
+
promise.rb (0.7.4)
|
38
|
+
pry (0.14.2)
|
39
|
+
coderay (~> 1.1)
|
40
|
+
method_source (~> 1.0)
|
41
|
+
pry-byebug (3.10.1)
|
42
|
+
byebug (~> 11.0)
|
43
|
+
pry (>= 0.13, < 0.15)
|
44
|
+
rake (13.1.0)
|
45
|
+
redis (4.8.1)
|
46
|
+
ruby2_keywords (0.0.5)
|
47
|
+
tzinfo (2.0.6)
|
48
|
+
concurrent-ruby (~> 1.0)
|
49
|
+
zeitwerk (2.6.13)
|
50
|
+
|
51
|
+
PLATFORMS
|
52
|
+
arm64-darwin-23
|
53
|
+
x86_64-linux
|
54
|
+
|
55
|
+
DEPENDENCIES
|
56
|
+
activesupport (~> 6.1.7)
|
57
|
+
diffy
|
58
|
+
fakeredis
|
59
|
+
graphql-batch
|
60
|
+
graphql-metrics!
|
61
|
+
hashdiff
|
62
|
+
minitest (~> 5.0)
|
63
|
+
minitest-focus
|
64
|
+
mocha
|
65
|
+
pry
|
66
|
+
pry-byebug
|
67
|
+
rake
|
68
|
+
|
69
|
+
BUNDLED WITH
|
70
|
+
2.4.1
|
data/README.md
CHANGED
@@ -7,9 +7,6 @@ Compatible with the [`graphql-batch` gem](https://github.com/Shopify/graphql-bat
|
|
7
7
|
|
8
8
|
Be sure to read the [CHANGELOG](CHANGELOG.md) to stay updated on feature additions, breaking changes made to this gem.
|
9
9
|
|
10
|
-
**NOTE**: Not tested with graphql-ruby's multiplexing feature. Metrics may not
|
11
|
-
be accurate if you execute multiple operations at once.
|
12
|
-
|
13
10
|
## Installation
|
14
11
|
|
15
12
|
Add this line to your application's Gemfile:
|
@@ -42,15 +39,15 @@ Get started by defining your own Analyzer, inheriting from `GraphQL::Metrics::An
|
|
42
39
|
|
43
40
|
The following analyzer demonstrates a simple way to capture commonly used metrics sourced from key parts of your schema
|
44
41
|
definition, the query document being served, as well as runtime query and resolver timings. In this toy example, all of
|
45
|
-
this data is simply stored on the GraphQL::Query context, under a namespace to avoid collisions with other analyzers
|
42
|
+
this data is simply stored on the `GraphQL::Query` context, under a namespace to avoid collisions with other analyzers
|
46
43
|
etc.
|
47
44
|
|
48
45
|
What you do with these captured metrics is up to you!
|
49
46
|
|
50
47
|
**NOTE**: If any non-`graphql-ruby` gem-related exceptions occur in your application during query document
|
51
|
-
parsing and validation, **runtime metrics** for queries (like `query_duration`,
|
48
|
+
parsing and validation, **runtime metrics** for queries (like `query_duration`, etc.) as well as field
|
52
49
|
resolver timings (like `resolver_timings`, `lazy_resolver_timings`) **may not be present** in the extracted `metrics` hash,
|
53
|
-
even if you opt to collect them
|
50
|
+
even if you opt to collect them.
|
54
51
|
|
55
52
|
### Define your own analyzer subclass
|
56
53
|
|
@@ -75,15 +72,10 @@ even if you opt to collect them by using `GraphQL::Metrics::Analyzer` and `Graph
|
|
75
72
|
# operation_name: "PostDetails",
|
76
73
|
# query_start_time: 1573833076.027327,
|
77
74
|
# query_duration: 2.0207119999686256,
|
78
|
-
# lexing_start_time_offset: 0.0010339999571442604,
|
79
75
|
# lexing_duration: 0.0008190000080503523,
|
80
|
-
# parsing_start_time_offset: 0.0010339999571442604,
|
81
76
|
# parsing_duration: 0.0008190000080503523,
|
82
|
-
# validation_start_time_offset: 0.0030819999519735575,
|
83
77
|
# validation_duration: 0.01704599999357015,
|
84
|
-
# analysis_start_time_offset: 0.0010339999571442604,
|
85
78
|
# analysis_duration: 0.0008190000080503523,
|
86
|
-
# multiplex_start_time: 0.0008190000080503523,
|
87
79
|
# }
|
88
80
|
#
|
89
81
|
# You can use these metrics to track high-level query performance, along with any other details you wish to
|
@@ -129,12 +121,10 @@ even if you opt to collect them by using `GraphQL::Metrics::Analyzer` and `Graph
|
|
129
121
|
# deprecated: false,
|
130
122
|
# path: ["post", "id"],
|
131
123
|
# resolver_timings: [
|
132
|
-
#
|
133
|
-
# duration: 5.999987479299307e-06
|
124
|
+
# 5.999987479299307e-06,
|
134
125
|
# ],
|
135
126
|
# lazy_resolver_timings: [
|
136
|
-
#
|
137
|
-
# duration: 5.999987479299307e-06
|
127
|
+
# 5.999987479299307e-06,
|
138
128
|
# ],
|
139
129
|
# }
|
140
130
|
def field_extracted(metrics)
|
@@ -178,12 +168,12 @@ even if you opt to collect them by using `GraphQL::Metrics::Analyzer` and `Graph
|
|
178
168
|
end
|
179
169
|
```
|
180
170
|
|
181
|
-
Once defined, you can opt into capturing all metrics seen above by simply including GraphQL::Metrics as a plugin on your
|
171
|
+
Once defined, you can opt into capturing all metrics seen above by simply including `GraphQL::Metrics` as a plugin on your
|
182
172
|
schema.
|
183
|
-
#### Metrics that are captured for arguments for fields and directives
|
184
173
|
|
185
|
-
|
174
|
+
#### Metrics that are captured for arguments for fields and directives
|
186
175
|
|
176
|
+
Example query:
|
187
177
|
```graphql
|
188
178
|
query PostDetails($postId: ID!, $commentsTags: [String!] = null, $val: Int!) @customDirective(val: $val) {
|
189
179
|
post(id: $postId) {
|
@@ -242,46 +232,48 @@ These are some of the arguments that are extracted
|
|
242
232
|
|
243
233
|
### Make use of your analyzer
|
244
234
|
|
245
|
-
|
246
|
-
engine, and then simply add the below `GraphQL::Metrics` plugins.
|
247
|
-
|
248
|
-
This opts you in to capturing all static and runtime metrics seen above.
|
235
|
+
Add the `GraphQL::Metrics` plugin to your schema. This opts you in to capturing all static and runtime metrics seen above.
|
249
236
|
|
250
237
|
```ruby
|
251
238
|
class Schema < GraphQL::Schema
|
252
239
|
query QueryRoot
|
253
240
|
mutation MutationRoot
|
254
241
|
|
255
|
-
|
256
|
-
|
257
|
-
instrument :query, GraphQL::Metrics::Instrumentation.new # Both of these are required if either is used.
|
258
|
-
tracer GraphQL::Metrics::Tracer.new # <-- Note!
|
259
|
-
|
260
|
-
use GraphQL::Batch # Optional, but highly recommended. See https://github.com/Shopify/graphql-batch/.
|
242
|
+
use GraphQL::Metrics, analyzer: SimpleAnalyzer
|
261
243
|
end
|
262
244
|
```
|
263
245
|
|
264
246
|
### Optionally, only gather static metrics
|
265
247
|
|
266
|
-
If you don't care to capture
|
267
|
-
analyzer without `GraphQL::Metrics::Instrumentation` and `tracer GraphQL::Metrics::Tracer`, like so:
|
248
|
+
If you don't care to capture field timings metrics, they can be disabled with the `capture_field_timings` option:
|
268
249
|
|
269
250
|
```ruby
|
270
251
|
class Schema < GraphQL::Schema
|
271
252
|
query QueryRoot
|
272
253
|
mutation MutationRoot
|
273
254
|
|
274
|
-
|
255
|
+
use GraphQL::Metrics, analyzer: SimpleAnalyzer, capture_field_timings: false
|
275
256
|
end
|
276
257
|
```
|
277
258
|
|
278
259
|
Your analyzer will still be called with `query_extracted`, `field_extracted`, but with timings metrics omitted.
|
279
|
-
`argument_extracted` will work exactly the same
|
260
|
+
`argument_extracted` will work exactly the same.
|
261
|
+
|
262
|
+
And if you want to disable tracing metrics entirely, use the `capture_timings` option:
|
263
|
+
|
264
|
+
```ruby
|
265
|
+
class Schema < GraphQL::Schema
|
266
|
+
query QueryRoot
|
267
|
+
mutation MutationRoot
|
268
|
+
|
269
|
+
use GraphQL::Metrics, analyzer: SimpleAnalyzer, capture_timings: false
|
270
|
+
end
|
271
|
+
```
|
280
272
|
|
281
273
|
## Order of execution
|
282
274
|
|
283
275
|
Because of the structure of graphql-ruby's plugin architecture, it may be difficult to build an intuition around the
|
284
|
-
order in which methods defined on `GraphQL::Metrics::Instrumentation`, `GraphQL::Metrics::
|
276
|
+
order in which methods defined on `GraphQL::Metrics::Instrumentation`, `GraphQL::Metrics::Trace` and subclasses of
|
285
277
|
`GraphQL::Metrics::Analyzer` run.
|
286
278
|
|
287
279
|
Although you ideally will not need to care about these details if you are simply using this gem to gather metrics in
|
@@ -289,30 +281,21 @@ your application as intended, here's a breakdown of the order of execution of th
|
|
289
281
|
|
290
282
|
When used as instrumentation, an analyzer and tracing, the order of execution is usually:
|
291
283
|
|
292
|
-
*
|
293
|
-
*
|
294
|
-
*
|
295
|
-
*
|
296
|
-
*
|
297
|
-
* Tracer.capture_analysis_time
|
284
|
+
* Instrumentation.execute_multiplex (context setup)
|
285
|
+
* Trace.capture_lexing_time
|
286
|
+
* Trace.capture_parsing_time
|
287
|
+
* Trace.capture_validation_time
|
288
|
+
* Trace.capture_analysis_time
|
298
289
|
* Analyzer#initialize (bit more context setup, instance vars setup)
|
299
290
|
* Analyzer#result
|
300
|
-
*
|
301
|
-
*
|
302
|
-
* Instrumentation.
|
303
|
-
gathered)
|
291
|
+
* Trace.capture_query_start_time
|
292
|
+
* Trace.trace_field (n times)
|
293
|
+
* Instrumentation.execute_multiplex (call query and field callbacks, now that we have all static and runtime metrics gathered)
|
304
294
|
* Analyzer#extract_query
|
305
295
|
* Analyzer#query_extracted
|
306
296
|
* Analyzer#extract_fields_with_runtime_metrics
|
307
297
|
* calls Analyzer#field_extracted n times
|
308
298
|
|
309
|
-
When used as a simple analyzer, which doesn't gather or emit any runtime metrics (timings, arg values):
|
310
|
-
* Analyzer#initialize
|
311
|
-
* Analyzer#field_extracted n times
|
312
|
-
* Analyzer#result
|
313
|
-
* Analyzer#extract_query
|
314
|
-
* Analyzer#query_extracted
|
315
|
-
|
316
299
|
## Development
|
317
300
|
|
318
301
|
After checking out the repo, run `bin/setup` to install dependencies. Then, run `bundle exec rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
data/graphql_metrics.gemspec
CHANGED
@@ -14,6 +14,8 @@ Gem::Specification.new do |spec|
|
|
14
14
|
spec.homepage = 'https://github.com/Shopify/graphql-metrics'
|
15
15
|
spec.license = "MIT"
|
16
16
|
|
17
|
+
spec.required_ruby_version = ">= 2.7"
|
18
|
+
|
17
19
|
spec.metadata["allowed_push_host"] = "https://rubygems.org"
|
18
20
|
|
19
21
|
spec.files = `git ls-files -z`.split("\x0").reject do |f|
|
@@ -23,12 +25,11 @@ Gem::Specification.new do |spec|
|
|
23
25
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
24
26
|
spec.require_paths = ["lib"]
|
25
27
|
|
26
|
-
spec.add_runtime_dependency "graphql", ">=
|
27
|
-
spec.
|
28
|
-
spec.add_development_dependency "rake", "~> 10.0"
|
28
|
+
spec.add_runtime_dependency "graphql", ">= 2.3"
|
29
|
+
spec.add_development_dependency "rake", "~> 13.2"
|
29
30
|
spec.add_development_dependency "minitest", "~> 5.0"
|
30
31
|
spec.add_development_dependency 'graphql-batch'
|
31
|
-
spec.add_development_dependency "activesupport", "~>
|
32
|
+
spec.add_development_dependency "activesupport", "~> 6.1.7"
|
32
33
|
spec.add_development_dependency "pry"
|
33
34
|
spec.add_development_dependency "pry-byebug"
|
34
35
|
spec.add_development_dependency "mocha"
|
@@ -51,20 +51,13 @@ module GraphQL
|
|
51
51
|
path: visitor.response_path,
|
52
52
|
}
|
53
53
|
|
54
|
-
|
55
|
-
@static_field_metrics << static_metrics
|
56
|
-
else
|
57
|
-
field_extracted(static_metrics)
|
58
|
-
end
|
54
|
+
@static_field_metrics << static_metrics
|
59
55
|
end
|
60
56
|
|
61
57
|
def extract_fields(with_runtime_metrics: true)
|
62
|
-
return if query.context[SKIP_FIELD_AND_ARGUMENT_METRICS]
|
63
|
-
|
64
58
|
ns = query.context.namespace(CONTEXT_NAMESPACE)
|
65
59
|
|
66
60
|
@static_field_metrics.each do |static_metrics|
|
67
|
-
|
68
61
|
if with_runtime_metrics
|
69
62
|
resolver_timings = ns[GraphQL::Metrics::INLINE_FIELD_TIMINGS][static_metrics[:path]]
|
70
63
|
lazy_resolver_timings = ns[GraphQL::Metrics::LAZY_FIELD_TIMINGS][static_metrics[:path]]
|
@@ -89,14 +82,6 @@ module GraphQL
|
|
89
82
|
end
|
90
83
|
|
91
84
|
def result
|
92
|
-
return if GraphQL::Metrics.timings_capture_enabled?(query.context)
|
93
|
-
return if query.context[GraphQL::Metrics::SKIP_GRAPHQL_METRICS_ANALYSIS]
|
94
|
-
|
95
|
-
# NOTE: If we're running as a static analyzer (i.e. not with instrumentation and tracing), we still need to
|
96
|
-
# flush static query metrics somewhere other than `after_query`.
|
97
|
-
ns = query.context.namespace(CONTEXT_NAMESPACE)
|
98
|
-
analyzer = ns[GraphQL::Metrics::ANALYZER_INSTANCE_KEY]
|
99
|
-
analyzer.extract_query
|
100
85
|
end
|
101
86
|
|
102
87
|
private
|
@@ -2,28 +2,39 @@
|
|
2
2
|
|
3
3
|
module GraphQL
|
4
4
|
module Metrics
|
5
|
-
|
6
|
-
def
|
7
|
-
|
8
|
-
|
9
|
-
|
5
|
+
module Instrumentation
|
6
|
+
def initialize(capture_field_timings: true, **_options)
|
7
|
+
@capture_field_timings = capture_field_timings
|
8
|
+
|
9
|
+
super
|
10
|
+
end
|
11
|
+
|
12
|
+
def execute_multiplex(multiplex:)
|
13
|
+
return super if multiplex.context[GraphQL::Metrics::SKIP_GRAPHQL_METRICS_ANALYSIS]
|
14
|
+
|
15
|
+
result = nil
|
16
|
+
|
17
|
+
multiplex.queries.each do |query|
|
18
|
+
ns = query.context.namespace(CONTEXT_NAMESPACE)
|
19
|
+
ns[GraphQL::Metrics::TIMINGS_CAPTURE_ENABLED] = @capture_field_timings
|
20
|
+
ns[GraphQL::Metrics::INLINE_FIELD_TIMINGS] = Hash.new { |h, k| h[k] = [] }
|
21
|
+
ns[GraphQL::Metrics::LAZY_FIELD_TIMINGS] = Hash.new { |h, k| h[k] = [] }
|
10
22
|
end
|
11
23
|
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
24
|
+
begin
|
25
|
+
result = super
|
26
|
+
ensure
|
27
|
+
multiplex.queries.each do |query|
|
28
|
+
handle_query(query) if query.valid?
|
29
|
+
end
|
30
|
+
end
|
17
31
|
|
18
|
-
|
19
|
-
ns[GraphQL::Metrics::TIMINGS_CAPTURE_ENABLED] = true
|
20
|
-
ns[GraphQL::Metrics::INLINE_FIELD_TIMINGS] = {}
|
21
|
-
ns[GraphQL::Metrics::LAZY_FIELD_TIMINGS] = {}
|
32
|
+
result
|
22
33
|
end
|
23
34
|
|
24
|
-
|
25
|
-
return if query.context[GraphQL::Metrics::SKIP_GRAPHQL_METRICS_ANALYSIS]
|
35
|
+
private
|
26
36
|
|
37
|
+
def handle_query(query)
|
27
38
|
ns = query.context.namespace(CONTEXT_NAMESPACE)
|
28
39
|
analyzer = ns[GraphQL::Metrics::ANALYZER_INSTANCE_KEY]
|
29
40
|
|
@@ -41,30 +52,19 @@ module GraphQL
|
|
41
52
|
runtime_query_metrics = {
|
42
53
|
query_start_time: ns[GraphQL::Metrics::QUERY_START_TIME],
|
43
54
|
query_duration: query_duration,
|
44
|
-
parsing_start_time_offset: ns[GraphQL::Metrics::PARSING_START_TIME_OFFSET],
|
45
55
|
parsing_duration: ns[GraphQL::Metrics::PARSING_DURATION],
|
46
|
-
validation_start_time_offset: ns[GraphQL::Metrics::VALIDATION_START_TIME_OFFSET],
|
47
56
|
validation_duration: ns[GraphQL::Metrics::VALIDATION_DURATION],
|
48
|
-
lexing_start_time_offset: ns[GraphQL::Metrics::LEXING_START_TIME_OFFSET],
|
49
57
|
lexing_duration: ns[GraphQL::Metrics::LEXING_DURATION],
|
50
|
-
analysis_start_time_offset: ns[GraphQL::Metrics::ANALYSIS_START_TIME_OFFSET],
|
51
58
|
analysis_duration: ns[GraphQL::Metrics::ANALYSIS_DURATION],
|
52
|
-
multiplex_start_time: ns[GraphQL::Metrics::MULTIPLEX_START_TIME],
|
53
59
|
}
|
54
60
|
|
55
|
-
analyzer.extract_fields
|
61
|
+
analyzer.extract_fields(with_runtime_metrics: @capture_field_timings)
|
56
62
|
analyzer.extract_query(runtime_query_metrics: runtime_query_metrics)
|
57
63
|
end
|
58
64
|
end
|
59
65
|
|
60
66
|
private
|
61
67
|
|
62
|
-
def query_present_and_valid?(query)
|
63
|
-
# Check for selected_operation as well for graphql 1.9 compatibility
|
64
|
-
# which did not reject "empty" documents in its parser.
|
65
|
-
query.valid? && !query.selected_operation.nil?
|
66
|
-
end
|
67
|
-
|
68
68
|
def runtime_metrics_interrupted?(context_namespace)
|
69
69
|
# NOTE: The start time stored at `ns[GraphQL::Metrics::QUERY_START_TIME_MONOTONIC]` is captured during query
|
70
70
|
# parsing, which occurs before `Instrumentation#before_query`.
|
@@ -8,6 +8,8 @@ module GraphQL
|
|
8
8
|
|
9
9
|
query_or_multiplex = @query || @multiplex
|
10
10
|
@skip_tracing = query_or_multiplex.context&.fetch(SKIP_GRAPHQL_METRICS_ANALYSIS, false) if query_or_multiplex
|
11
|
+
@parsing_duration = 0.0
|
12
|
+
@lexing_duration = 0.0
|
11
13
|
end
|
12
14
|
|
13
15
|
# NOTE: These methods come from the graphql ruby gem and are in "chronological" order based on the phases
|
@@ -15,12 +17,6 @@ module GraphQL
|
|
15
17
|
# https://github.com/rmosolgo/graphql-ruby/issues/3393). Most of them can be run multiple times when
|
16
18
|
# multiplexing multiple queries.
|
17
19
|
|
18
|
-
# wraps everything below this line; only run once
|
19
|
-
def execute_multiplex(multiplex:)
|
20
|
-
return super if skip_tracing?(multiplex)
|
21
|
-
capture_multiplex_start_time { super }
|
22
|
-
end
|
23
|
-
|
24
20
|
# may not trigger if the query is passed in pre-parsed
|
25
21
|
def lex(query_string:)
|
26
22
|
return super if @skip_tracing
|
@@ -34,139 +30,74 @@ module GraphQL
|
|
34
30
|
end
|
35
31
|
|
36
32
|
def validate(query:, validate:)
|
37
|
-
return super if skip_tracing
|
33
|
+
return super if @skip_tracing
|
38
34
|
capture_validation_time(query.context) { super }
|
39
35
|
end
|
40
36
|
|
41
|
-
# wraps all `analyze_query`s; only run once
|
42
|
-
def analyze_multiplex(multiplex:)
|
43
|
-
return super if skip_tracing?(multiplex)
|
44
|
-
# Ensures that we reset potentially long-lived PreContext objects between multiplexs. We reset at this point
|
45
|
-
# since all parsing and validation will be done by this point, and a GraphQL::Query::Context will exist.
|
46
|
-
pre_context.reset
|
47
|
-
super
|
48
|
-
end
|
49
|
-
|
50
37
|
def analyze_query(query:)
|
51
|
-
return super if skip_tracing
|
38
|
+
return super if @skip_tracing
|
52
39
|
capture_analysis_time(query.context) { super }
|
53
40
|
end
|
54
41
|
|
55
42
|
def execute_query(query:)
|
56
|
-
return super if skip_tracing
|
43
|
+
return super if @skip_tracing
|
57
44
|
capture_query_start_time(query.context) { super }
|
58
45
|
end
|
59
46
|
|
60
47
|
def execute_field(field:, query:, ast_node:, arguments:, object:)
|
61
|
-
return super if skip_tracing
|
62
|
-
return super unless
|
48
|
+
return super if @skip_tracing || query.context[SKIP_FIELD_AND_ARGUMENT_METRICS]
|
49
|
+
return super unless capture_field_timings?(query.context)
|
63
50
|
trace_field(GraphQL::Metrics::INLINE_FIELD_TIMINGS, query) { super }
|
64
51
|
end
|
65
52
|
|
66
53
|
def execute_field_lazy(field:, query:, ast_node:, arguments:, object:)
|
67
|
-
return super if skip_tracing
|
68
|
-
return super unless
|
54
|
+
return super if @skip_tracing || query.context[SKIP_FIELD_AND_ARGUMENT_METRICS]
|
55
|
+
return super unless capture_field_timings?(query.context)
|
69
56
|
trace_field(GraphQL::Metrics::LAZY_FIELD_TIMINGS, query) { super }
|
70
57
|
end
|
71
58
|
|
72
59
|
private
|
73
60
|
|
74
|
-
def
|
75
|
-
if !defined?(@
|
76
|
-
@
|
61
|
+
def capture_field_timings?(context)
|
62
|
+
if !defined?(@capture_field_timings)
|
63
|
+
@capture_field_timings = !!context.namespace(CONTEXT_NAMESPACE)[TIMINGS_CAPTURE_ENABLED]
|
77
64
|
end
|
78
65
|
|
79
|
-
@
|
80
|
-
end
|
81
|
-
|
82
|
-
PreContext = Struct.new(
|
83
|
-
:multiplex_start_time,
|
84
|
-
:multiplex_start_time_monotonic,
|
85
|
-
:parsing_start_time_offset,
|
86
|
-
:parsing_duration,
|
87
|
-
:lexing_start_time_offset,
|
88
|
-
:lexing_duration
|
89
|
-
) do
|
90
|
-
def reset
|
91
|
-
self[:multiplex_start_time] = nil
|
92
|
-
self[:multiplex_start_time_monotonic] = nil
|
93
|
-
self[:parsing_start_time_offset] = nil
|
94
|
-
self[:parsing_duration] = nil
|
95
|
-
self[:lexing_start_time_offset] = nil
|
96
|
-
self[:lexing_duration] = nil
|
97
|
-
end
|
98
|
-
end
|
99
|
-
|
100
|
-
def pre_context
|
101
|
-
# NOTE: This is used to store timings from lexing, parsing, validation, before we have a context to store
|
102
|
-
# values in. Uses thread-safe Concurrent::ThreadLocalVar to store a set of values per thread.
|
103
|
-
@pre_context ||= Concurrent::ThreadLocalVar.new(PreContext.new)
|
104
|
-
@pre_context.value
|
105
|
-
end
|
106
|
-
|
107
|
-
def capture_multiplex_start_time
|
108
|
-
pre_context.multiplex_start_time = GraphQL::Metrics.current_time
|
109
|
-
pre_context.multiplex_start_time_monotonic = GraphQL::Metrics.current_time_monotonic
|
110
|
-
|
111
|
-
yield
|
66
|
+
@capture_field_timings
|
112
67
|
end
|
113
68
|
|
114
69
|
def capture_lexing_time
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
pre_context.lexing_duration = timed_result.duration
|
119
|
-
|
120
|
-
timed_result.result
|
70
|
+
result, duration = GraphQL::Metrics.time { yield }
|
71
|
+
@lexing_duration = duration
|
72
|
+
result
|
121
73
|
end
|
122
74
|
|
123
75
|
def capture_parsing_time
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
pre_context.parsing_duration = timed_result.duration
|
128
|
-
|
129
|
-
timed_result.result
|
76
|
+
result, duration = GraphQL::Metrics.time { yield }
|
77
|
+
@parsing_duration = duration
|
78
|
+
result
|
130
79
|
end
|
131
80
|
|
132
|
-
# Also consolidates parsing timings (if any)
|
81
|
+
# Also consolidates parsing timings (if any)
|
133
82
|
def capture_validation_time(context)
|
134
|
-
|
135
|
-
# If we don't have those values, use some sane defaults.
|
136
|
-
if pre_context.lexing_duration.nil?
|
137
|
-
pre_context.lexing_start_time_offset = pre_context.multiplex_start_time
|
138
|
-
pre_context.lexing_duration = 0.0
|
139
|
-
end
|
140
|
-
if pre_context.parsing_duration.nil?
|
141
|
-
pre_context.parsing_start_time_offset = pre_context.multiplex_start_time
|
142
|
-
pre_context.parsing_duration = 0.0
|
143
|
-
end
|
144
|
-
|
145
|
-
timed_result = GraphQL::Metrics.time(pre_context.multiplex_start_time_monotonic) { yield }
|
83
|
+
result, duration = GraphQL::Metrics.time { yield }
|
146
84
|
|
147
85
|
ns = context.namespace(CONTEXT_NAMESPACE)
|
86
|
+
ns[LEXING_DURATION] = @lexing_duration
|
87
|
+
ns[PARSING_DURATION] = @parsing_duration
|
88
|
+
ns[VALIDATION_DURATION] = duration
|
148
89
|
|
149
|
-
|
150
|
-
ns[MULTIPLEX_START_TIME_MONOTONIC] = pre_context.multiplex_start_time_monotonic
|
151
|
-
ns[LEXING_START_TIME_OFFSET] = pre_context.lexing_start_time_offset
|
152
|
-
ns[LEXING_DURATION] = pre_context.lexing_duration
|
153
|
-
ns[PARSING_START_TIME_OFFSET] = pre_context.parsing_start_time_offset
|
154
|
-
ns[PARSING_DURATION] = pre_context.parsing_duration
|
155
|
-
ns[VALIDATION_START_TIME_OFFSET] = timed_result.time_since_offset
|
156
|
-
ns[VALIDATION_DURATION] = timed_result.duration
|
157
|
-
|
158
|
-
timed_result.result
|
90
|
+
result
|
159
91
|
end
|
160
92
|
|
161
93
|
def capture_analysis_time(context)
|
162
94
|
ns = context.namespace(CONTEXT_NAMESPACE)
|
163
95
|
|
164
|
-
|
96
|
+
result, duration = GraphQL::Metrics.time { yield }
|
165
97
|
|
166
|
-
ns[
|
167
|
-
ns[ANALYSIS_DURATION] = timed_result.duration
|
98
|
+
ns[ANALYSIS_DURATION] = duration
|
168
99
|
|
169
|
-
|
100
|
+
result
|
170
101
|
end
|
171
102
|
|
172
103
|
def capture_query_start_time(context)
|
@@ -179,20 +110,13 @@ module GraphQL
|
|
179
110
|
|
180
111
|
def trace_field(context_key, query)
|
181
112
|
ns = query.context.namespace(CONTEXT_NAMESPACE)
|
182
|
-
offset_time = ns[GraphQL::Metrics::QUERY_START_TIME_MONOTONIC]
|
183
|
-
start_time = GraphQL::Metrics.current_time_monotonic
|
184
113
|
path = query.context[:current_path]
|
185
114
|
|
186
|
-
result = yield
|
187
|
-
|
188
|
-
duration = GraphQL::Metrics.current_time_monotonic - start_time
|
189
|
-
time_since_offset = start_time - offset_time if offset_time
|
115
|
+
result, duration = GraphQL::Metrics.time { yield }
|
190
116
|
|
191
117
|
path_excluding_numeric_indicies = path.select { |p| p.is_a?(String) }
|
192
118
|
ns[context_key][path_excluding_numeric_indicies] ||= []
|
193
|
-
ns[context_key][path_excluding_numeric_indicies] <<
|
194
|
-
start_time_offset: time_since_offset, duration: duration
|
195
|
-
}
|
119
|
+
ns[context_key][path_excluding_numeric_indicies] << duration
|
196
120
|
|
197
121
|
result
|
198
122
|
end
|
data/lib/graphql/metrics.rb
CHANGED
@@ -1,10 +1,8 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require "concurrent"
|
4
3
|
require "graphql/metrics/version"
|
5
4
|
require "graphql/metrics/instrumentation"
|
6
5
|
require "graphql/metrics/trace"
|
7
|
-
require "graphql/metrics/tracer"
|
8
6
|
require "graphql/metrics/analyzer"
|
9
7
|
|
10
8
|
module GraphQL
|
@@ -27,22 +25,13 @@ module GraphQL
|
|
27
25
|
MULTIPLEX_START_TIME_MONOTONIC = :multiplex_start_time_monotonic
|
28
26
|
QUERY_START_TIME = :query_start_time
|
29
27
|
QUERY_START_TIME_MONOTONIC = :query_start_time_monotonic
|
30
|
-
LEXING_START_TIME_OFFSET = :lexing_start_time_offset
|
31
28
|
LEXING_DURATION = :lexing_duration
|
32
|
-
PARSING_START_TIME_OFFSET = :parsing_start_time_offset
|
33
29
|
PARSING_DURATION = :parsing_duration
|
34
|
-
VALIDATION_START_TIME_OFFSET = :validation_start_time_offset
|
35
30
|
VALIDATION_DURATION = :validation_duration
|
36
|
-
ANALYSIS_START_TIME_OFFSET = :analysis_start_time_offset
|
37
31
|
ANALYSIS_DURATION = :analysis_duration
|
38
32
|
INLINE_FIELD_TIMINGS = :inline_field_timings
|
39
33
|
LAZY_FIELD_TIMINGS = :lazy_field_timings
|
40
34
|
|
41
|
-
def self.timings_capture_enabled?(context)
|
42
|
-
return false unless context
|
43
|
-
!!context.namespace(CONTEXT_NAMESPACE)[TIMINGS_CAPTURE_ENABLED]
|
44
|
-
end
|
45
|
-
|
46
35
|
def self.current_time
|
47
36
|
Process.clock_gettime(Process::CLOCK_REALTIME)
|
48
37
|
end
|
@@ -51,40 +40,32 @@ module GraphQL
|
|
51
40
|
Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
52
41
|
end
|
53
42
|
|
54
|
-
def self.time
|
43
|
+
def self.time
|
55
44
|
start_time = current_time_monotonic
|
56
45
|
result = yield
|
57
46
|
duration = current_time_monotonic - start_time
|
58
|
-
|
59
|
-
TimedResult.new(start_time, duration, time_since_offset, result)
|
47
|
+
[result, duration]
|
60
48
|
end
|
61
49
|
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
# < start offset >
|
73
|
-
# |------------------|----------|--------->
|
74
|
-
# OS (t=0) FS (t=1) FE (t=2)
|
75
|
-
#
|
76
|
-
# OS = Operation start time
|
77
|
-
# FS = Field resolver start time
|
78
|
-
# FE = Field resolver end time
|
79
|
-
#
|
80
|
-
attr_reader :result, :start_time, :duration, :time_since_offset
|
81
|
-
|
82
|
-
def initialize(start_time, duration, time_since_offset, result)
|
83
|
-
@start_time = start_time
|
84
|
-
@duration = duration
|
85
|
-
@time_since_offset = time_since_offset
|
86
|
-
@result = result
|
50
|
+
def self.use(
|
51
|
+
schema_defn,
|
52
|
+
analyzer:,
|
53
|
+
tracer: GraphQL::Metrics::Trace,
|
54
|
+
capture_timings: nil,
|
55
|
+
capture_field_timings: nil,
|
56
|
+
trace_mode: :default
|
57
|
+
)
|
58
|
+
if capture_field_timings && !capture_timings
|
59
|
+
raise ArgumentError, "Cannot capture field timings without capturing query timings"
|
87
60
|
end
|
61
|
+
|
62
|
+
capture_timings = true if capture_timings.nil?
|
63
|
+
capture_field_timings = true if capture_field_timings.nil?
|
64
|
+
capture_field_timings = false if !capture_timings
|
65
|
+
|
66
|
+
schema_defn.trace_with(GraphQL::Metrics::Instrumentation, capture_field_timings: capture_field_timings)
|
67
|
+
schema_defn.trace_with(tracer, mode: trace_mode) if capture_timings
|
68
|
+
schema_defn.query_analyzer(analyzer)
|
88
69
|
end
|
89
70
|
end
|
90
71
|
end
|
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: 6.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Christopher Butcher
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2024-09-12 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: graphql
|
@@ -16,42 +16,28 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - ">="
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version:
|
19
|
+
version: '2.3'
|
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:
|
27
|
-
- !ruby/object:Gem::Dependency
|
28
|
-
name: concurrent-ruby
|
29
|
-
requirement: !ruby/object:Gem::Requirement
|
30
|
-
requirements:
|
31
|
-
- - ">="
|
32
|
-
- !ruby/object:Gem::Version
|
33
|
-
version: 1.1.0
|
34
|
-
type: :runtime
|
35
|
-
prerelease: false
|
36
|
-
version_requirements: !ruby/object:Gem::Requirement
|
37
|
-
requirements:
|
38
|
-
- - ">="
|
39
|
-
- !ruby/object:Gem::Version
|
40
|
-
version: 1.1.0
|
26
|
+
version: '2.3'
|
41
27
|
- !ruby/object:Gem::Dependency
|
42
28
|
name: rake
|
43
29
|
requirement: !ruby/object:Gem::Requirement
|
44
30
|
requirements:
|
45
31
|
- - "~>"
|
46
32
|
- !ruby/object:Gem::Version
|
47
|
-
version: '
|
33
|
+
version: '13.2'
|
48
34
|
type: :development
|
49
35
|
prerelease: false
|
50
36
|
version_requirements: !ruby/object:Gem::Requirement
|
51
37
|
requirements:
|
52
38
|
- - "~>"
|
53
39
|
- !ruby/object:Gem::Version
|
54
|
-
version: '
|
40
|
+
version: '13.2'
|
55
41
|
- !ruby/object:Gem::Dependency
|
56
42
|
name: minitest
|
57
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -86,14 +72,14 @@ dependencies:
|
|
86
72
|
requirements:
|
87
73
|
- - "~>"
|
88
74
|
- !ruby/object:Gem::Version
|
89
|
-
version:
|
75
|
+
version: 6.1.7
|
90
76
|
type: :development
|
91
77
|
prerelease: false
|
92
78
|
version_requirements: !ruby/object:Gem::Requirement
|
93
79
|
requirements:
|
94
80
|
- - "~>"
|
95
81
|
- !ruby/object:Gem::Version
|
96
|
-
version:
|
82
|
+
version: 6.1.7
|
97
83
|
- !ruby/object:Gem::Dependency
|
98
84
|
name: pry
|
99
85
|
requirement: !ruby/object:Gem::Requirement
|
@@ -201,7 +187,9 @@ executables: []
|
|
201
187
|
extensions: []
|
202
188
|
extra_rdoc_files: []
|
203
189
|
files:
|
190
|
+
- ".github/dependabot.yml"
|
204
191
|
- ".github/workflows/cla.yml"
|
192
|
+
- ".github/workflows/dependabot_auto_merge.yml"
|
205
193
|
- ".github/workflows/ruby.yml"
|
206
194
|
- ".gitignore"
|
207
195
|
- ".rubocop-http---shopify-github-io-ruby-style-guide-rubocop-yml"
|
@@ -210,28 +198,27 @@ files:
|
|
210
198
|
- CHANGELOG.md
|
211
199
|
- CODE_OF_CONDUCT.md
|
212
200
|
- Gemfile
|
201
|
+
- Gemfile.lock
|
213
202
|
- LICENSE.txt
|
214
203
|
- README.md
|
215
204
|
- RELEASING
|
216
205
|
- Rakefile
|
217
206
|
- bin/console
|
218
207
|
- bin/setup
|
219
|
-
- gemfiles/
|
220
|
-
- gemfiles/graphql_2.0.gemfile
|
208
|
+
- gemfiles/graphql_2.3.gemfile
|
221
209
|
- gemfiles/graphql_head.gemfile
|
222
210
|
- graphql_metrics.gemspec
|
223
211
|
- lib/graphql/metrics.rb
|
224
212
|
- lib/graphql/metrics/analyzer.rb
|
225
213
|
- lib/graphql/metrics/instrumentation.rb
|
226
214
|
- lib/graphql/metrics/trace.rb
|
227
|
-
- lib/graphql/metrics/tracer.rb
|
228
215
|
- lib/graphql/metrics/version.rb
|
229
216
|
homepage: https://github.com/Shopify/graphql-metrics
|
230
217
|
licenses:
|
231
218
|
- MIT
|
232
219
|
metadata:
|
233
220
|
allowed_push_host: https://rubygems.org
|
234
|
-
post_install_message:
|
221
|
+
post_install_message:
|
235
222
|
rdoc_options: []
|
236
223
|
require_paths:
|
237
224
|
- lib
|
@@ -239,15 +226,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
239
226
|
requirements:
|
240
227
|
- - ">="
|
241
228
|
- !ruby/object:Gem::Version
|
242
|
-
version: '
|
229
|
+
version: '2.7'
|
243
230
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
244
231
|
requirements:
|
245
232
|
- - ">="
|
246
233
|
- !ruby/object:Gem::Version
|
247
234
|
version: '0'
|
248
235
|
requirements: []
|
249
|
-
rubygems_version: 3.
|
250
|
-
signing_key:
|
236
|
+
rubygems_version: 3.5.18
|
237
|
+
signing_key:
|
251
238
|
specification_version: 4
|
252
239
|
summary: GraphQL Metrics Extractor
|
253
240
|
test_files: []
|
@@ -1,70 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module GraphQL
|
4
|
-
module Metrics
|
5
|
-
class Tracer
|
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, 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
|
11
|
-
GRAPHQL_GEM_LEXING_KEY = 'lex' # may not trigger if the query is passed in pre-parsed
|
12
|
-
GRAPHQL_GEM_PARSING_KEY = 'parse' # may not trigger if the query is passed in pre-parsed
|
13
|
-
GRAPHQL_GEM_VALIDATION_KEY = 'validate'
|
14
|
-
GRAPHQL_GEM_ANALYZE_MULTIPLEX_KEY = 'analyze_multiplex' # wraps all `analyze_query`s; only run once
|
15
|
-
GRAPHQL_GEM_ANALYZE_QUERY_KEY = 'analyze_query'
|
16
|
-
GRAPHQL_GEM_EXECUTE_QUERY_KEY = 'execute_query'
|
17
|
-
GRAPHQL_GEM_TRACING_FIELD_KEYS = [
|
18
|
-
GRAPHQL_GEM_TRACING_FIELD_KEY = 'execute_field',
|
19
|
-
GRAPHQL_GEM_TRACING_LAZY_FIELD_KEY = 'execute_field_lazy'
|
20
|
-
]
|
21
|
-
|
22
|
-
include GraphQL::Metrics::Trace
|
23
|
-
|
24
|
-
def initialize
|
25
|
-
# no-op, but don't want the behavior from GraphQL::Metrics::Trace
|
26
|
-
end
|
27
|
-
|
28
|
-
def trace(key, data, &block)
|
29
|
-
# NOTE: Context doesn't exist yet during lexing, parsing.
|
30
|
-
context = data[:query]&.context
|
31
|
-
skip_tracing = context&.fetch(GraphQL::Metrics::SKIP_GRAPHQL_METRICS_ANALYSIS, false)
|
32
|
-
return yield if skip_tracing
|
33
|
-
|
34
|
-
case key
|
35
|
-
when GRAPHQL_GEM_EXECUTE_MULTIPLEX_KEY
|
36
|
-
return capture_multiplex_start_time(&block)
|
37
|
-
when GRAPHQL_GEM_LEXING_KEY
|
38
|
-
return capture_lexing_time(&block)
|
39
|
-
when GRAPHQL_GEM_PARSING_KEY
|
40
|
-
return capture_parsing_time(&block)
|
41
|
-
when GRAPHQL_GEM_VALIDATION_KEY
|
42
|
-
return capture_validation_time(context, &block)
|
43
|
-
when GRAPHQL_GEM_ANALYZE_MULTIPLEX_KEY
|
44
|
-
# Ensures that we reset potentially long-lived PreContext objects between multiplexs. We reset at this point
|
45
|
-
# since all parsing and validation will be done by this point, and a GraphQL::Query::Context will exist.
|
46
|
-
pre_context.reset
|
47
|
-
return yield
|
48
|
-
when GRAPHQL_GEM_ANALYZE_QUERY_KEY
|
49
|
-
return capture_analysis_time(context, &block)
|
50
|
-
when GRAPHQL_GEM_EXECUTE_QUERY_KEY
|
51
|
-
capture_query_start_time(context, &block)
|
52
|
-
when *GRAPHQL_GEM_TRACING_FIELD_KEYS
|
53
|
-
return yield if context[SKIP_FIELD_AND_ARGUMENT_METRICS]
|
54
|
-
return yield unless GraphQL::Metrics.timings_capture_enabled?(data[:query].context)
|
55
|
-
|
56
|
-
context_key = case key
|
57
|
-
when GRAPHQL_GEM_TRACING_FIELD_KEY
|
58
|
-
GraphQL::Metrics::INLINE_FIELD_TIMINGS
|
59
|
-
when GRAPHQL_GEM_TRACING_LAZY_FIELD_KEY
|
60
|
-
GraphQL::Metrics::LAZY_FIELD_TIMINGS
|
61
|
-
end
|
62
|
-
|
63
|
-
trace_field(context_key, data[:query], &block)
|
64
|
-
else
|
65
|
-
return yield
|
66
|
-
end
|
67
|
-
end
|
68
|
-
end
|
69
|
-
end
|
70
|
-
end
|