graphql-metrics 5.0.8 → 6.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/.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
|