graphql-metrics 5.0.3 → 5.0.4
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/workflows/ruby.yml +1 -1
- data/CHANGELOG.md +5 -1
- data/gemfiles/graphql_head.gemfile +5 -0
- data/lib/graphql/metrics/analyzer.rb +1 -1
- data/lib/graphql/metrics/trace.rb +192 -0
- data/lib/graphql/metrics/tracer.rb +7 -120
- data/lib/graphql/metrics/version.rb +1 -1
- data/lib/graphql/metrics.rb +1 -0
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7422838e228e2f0dd82382ee2b165b7ab7dfee2b5430044d5b8e2e3f83da173a
|
4
|
+
data.tar.gz: 934f63e77e19731fa2f675185d92d81c30b9087a747cfddc7798986327b85a8c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 16f4f5ed3f00779216232748c5ad45e248b90b8827568d6f323e3a9477e61e273f67e66893bb439494d3e88526b8aec84a7fe1d35691b283235687cecacdbf58
|
7
|
+
data.tar.gz: c69155471bc5c372903af17d8311b0a6f95c55df7f8e490436217ffbfb0c4472401567375ed097bc11b93aa4cd1276f3d64a7d09e6d3e41254efc0fbed0a1156
|
data/.github/workflows/ruby.yml
CHANGED
@@ -7,7 +7,7 @@ jobs:
|
|
7
7
|
matrix:
|
8
8
|
os: [ubuntu-latest]
|
9
9
|
ruby: ['2.7', '3.0', '3.1', '3.2']
|
10
|
-
gemfile: ['graphql_1.13', 'graphql_2.0']
|
10
|
+
gemfile: ['graphql_1.13', 'graphql_2.0', 'graphql_head']
|
11
11
|
runs-on: ${{ matrix.os }}
|
12
12
|
env:
|
13
13
|
BUNDLE_GEMFILE: ${{ github.workspace }}/gemfiles/${{ matrix.gemfile }}.gemfile
|
data/CHANGELOG.md
CHANGED
@@ -0,0 +1,192 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module GraphQL
|
4
|
+
module Metrics
|
5
|
+
module Trace
|
6
|
+
def initialize(**_rest)
|
7
|
+
super
|
8
|
+
context = (@multiplex || @query).context
|
9
|
+
@skip_tracing = context&.fetch(GraphQL::Metrics::SKIP_GRAPHQL_METRICS_ANALYSIS, false)
|
10
|
+
end
|
11
|
+
|
12
|
+
# NOTE: These methods come from the graphql ruby gem and are in "chronological" order based on the phases
|
13
|
+
# of execution of the graphql-ruby gem, though old versions of the gem aren't always consistent about this (see
|
14
|
+
# https://github.com/rmosolgo/graphql-ruby/issues/3393). Most of them can be run multiple times when
|
15
|
+
# multiplexing multiple queries.
|
16
|
+
|
17
|
+
# wraps everything below this line; only run once
|
18
|
+
def execute_multiplex(multiplex:, &block)
|
19
|
+
return yield if @skip_tracing
|
20
|
+
capture_multiplex_start_time(&block)
|
21
|
+
|
22
|
+
end
|
23
|
+
|
24
|
+
# may not trigger if the query is passed in pre-parsed
|
25
|
+
def lex(query_string:, &block)
|
26
|
+
return yield if @skip_tracing
|
27
|
+
capture_lexing_time(&block)
|
28
|
+
end
|
29
|
+
|
30
|
+
# may not trigger if the query is passed in pre-parsed
|
31
|
+
def parse(query_string:, &block)
|
32
|
+
return yield if @skip_tracing
|
33
|
+
capture_parsing_time(&block)
|
34
|
+
end
|
35
|
+
|
36
|
+
def validate(query:, validate:, &block)
|
37
|
+
return yield if @skip_tracing
|
38
|
+
capture_validation_time(query.context, &block)
|
39
|
+
end
|
40
|
+
|
41
|
+
# wraps all `analyze_query`s; only run once
|
42
|
+
def analyze_multiplex(multiplex:, &block)
|
43
|
+
return yield if @skip_tracing
|
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
|
+
yield
|
48
|
+
end
|
49
|
+
|
50
|
+
def analyze_query(query:, &block)
|
51
|
+
return yield if @skip_tracing
|
52
|
+
capture_analysis_time(query.context, &block)
|
53
|
+
end
|
54
|
+
|
55
|
+
def execute_query(query:, &block)
|
56
|
+
return yield if @skip_tracing
|
57
|
+
capture_query_start_time(query.context, &block)
|
58
|
+
end
|
59
|
+
|
60
|
+
def execute_field(field:, query:, ast_node:, arguments:, object:, &block)
|
61
|
+
return yield if @skip_tracing || query.context[SKIP_FIELD_AND_ARGUMENT_METRICS]
|
62
|
+
return yield unless GraphQL::Metrics.timings_capture_enabled?(query.context)
|
63
|
+
trace_field(GraphQL::Metrics::INLINE_FIELD_TIMINGS, query, &block)
|
64
|
+
end
|
65
|
+
|
66
|
+
def execute_field_lazy(field:, query:, ast_node:, arguments:, object:, &block)
|
67
|
+
return yield if @skip_tracing || query.context[SKIP_FIELD_AND_ARGUMENT_METRICS]
|
68
|
+
return yield unless GraphQL::Metrics.timings_capture_enabled?(query.context)
|
69
|
+
trace_field(GraphQL::Metrics::LAZY_FIELD_TIMINGS, query, &block)
|
70
|
+
end
|
71
|
+
|
72
|
+
private
|
73
|
+
|
74
|
+
PreContext = Struct.new(
|
75
|
+
:multiplex_start_time,
|
76
|
+
:multiplex_start_time_monotonic,
|
77
|
+
:parsing_start_time_offset,
|
78
|
+
:parsing_duration,
|
79
|
+
:lexing_start_time_offset,
|
80
|
+
:lexing_duration
|
81
|
+
) do
|
82
|
+
def reset
|
83
|
+
self[:multiplex_start_time] = nil
|
84
|
+
self[:multiplex_start_time_monotonic] = nil
|
85
|
+
self[:parsing_start_time_offset] = nil
|
86
|
+
self[:parsing_duration] = nil
|
87
|
+
self[:lexing_start_time_offset] = nil
|
88
|
+
self[:lexing_duration] = nil
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
def pre_context
|
93
|
+
# NOTE: This is used to store timings from lexing, parsing, validation, before we have a context to store
|
94
|
+
# values in. Uses thread-safe Concurrent::ThreadLocalVar to store a set of values per thread.
|
95
|
+
@pre_context ||= Concurrent::ThreadLocalVar.new(PreContext.new)
|
96
|
+
@pre_context.value
|
97
|
+
end
|
98
|
+
|
99
|
+
def capture_multiplex_start_time
|
100
|
+
pre_context.multiplex_start_time = GraphQL::Metrics.current_time
|
101
|
+
pre_context.multiplex_start_time_monotonic = GraphQL::Metrics.current_time_monotonic
|
102
|
+
|
103
|
+
yield
|
104
|
+
end
|
105
|
+
|
106
|
+
def capture_lexing_time
|
107
|
+
timed_result = GraphQL::Metrics.time { yield }
|
108
|
+
|
109
|
+
pre_context.lexing_start_time_offset = timed_result.start_time
|
110
|
+
pre_context.lexing_duration = timed_result.duration
|
111
|
+
|
112
|
+
timed_result.result
|
113
|
+
end
|
114
|
+
|
115
|
+
def capture_parsing_time
|
116
|
+
timed_result = GraphQL::Metrics.time { yield }
|
117
|
+
|
118
|
+
pre_context.parsing_start_time_offset = timed_result.start_time
|
119
|
+
pre_context.parsing_duration = timed_result.duration
|
120
|
+
|
121
|
+
timed_result.result
|
122
|
+
end
|
123
|
+
|
124
|
+
# Also consolidates parsing timings (if any) from the `pre_context`
|
125
|
+
def capture_validation_time(context)
|
126
|
+
# Queries may already be lexed and parsed before execution (whether a single query or multiplex).
|
127
|
+
# If we don't have those values, use some sane defaults.
|
128
|
+
if pre_context.lexing_duration.nil?
|
129
|
+
pre_context.lexing_start_time_offset = pre_context.multiplex_start_time
|
130
|
+
pre_context.lexing_duration = 0.0
|
131
|
+
end
|
132
|
+
if pre_context.parsing_duration.nil?
|
133
|
+
pre_context.parsing_start_time_offset = pre_context.multiplex_start_time
|
134
|
+
pre_context.parsing_duration = 0.0
|
135
|
+
end
|
136
|
+
|
137
|
+
timed_result = GraphQL::Metrics.time(pre_context.multiplex_start_time_monotonic) { yield }
|
138
|
+
|
139
|
+
ns = context.namespace(CONTEXT_NAMESPACE)
|
140
|
+
|
141
|
+
ns[MULTIPLEX_START_TIME] = pre_context.multiplex_start_time
|
142
|
+
ns[MULTIPLEX_START_TIME_MONOTONIC] = pre_context.multiplex_start_time_monotonic
|
143
|
+
ns[LEXING_START_TIME_OFFSET] = pre_context.lexing_start_time_offset
|
144
|
+
ns[LEXING_DURATION] = pre_context.lexing_duration
|
145
|
+
ns[PARSING_START_TIME_OFFSET] = pre_context.parsing_start_time_offset
|
146
|
+
ns[PARSING_DURATION] = pre_context.parsing_duration
|
147
|
+
ns[VALIDATION_START_TIME_OFFSET] = timed_result.time_since_offset
|
148
|
+
ns[VALIDATION_DURATION] = timed_result.duration
|
149
|
+
|
150
|
+
timed_result.result
|
151
|
+
end
|
152
|
+
|
153
|
+
def capture_analysis_time(context)
|
154
|
+
ns = context.namespace(CONTEXT_NAMESPACE)
|
155
|
+
|
156
|
+
timed_result = GraphQL::Metrics.time(ns[MULTIPLEX_START_TIME_MONOTONIC]) { yield }
|
157
|
+
|
158
|
+
ns[ANALYSIS_START_TIME_OFFSET] = timed_result.time_since_offset
|
159
|
+
ns[ANALYSIS_DURATION] = timed_result.duration
|
160
|
+
|
161
|
+
timed_result.result
|
162
|
+
end
|
163
|
+
|
164
|
+
def capture_query_start_time(context)
|
165
|
+
ns = context.namespace(CONTEXT_NAMESPACE)
|
166
|
+
ns[QUERY_START_TIME] = GraphQL::Metrics.current_time
|
167
|
+
ns[QUERY_START_TIME_MONOTONIC] = GraphQL::Metrics.current_time_monotonic
|
168
|
+
|
169
|
+
yield
|
170
|
+
end
|
171
|
+
|
172
|
+
def trace_field(context_key, query)
|
173
|
+
ns = query.context.namespace(CONTEXT_NAMESPACE)
|
174
|
+
offset_time = ns[GraphQL::Metrics::QUERY_START_TIME_MONOTONIC]
|
175
|
+
start_time = GraphQL::Metrics.current_time_monotonic
|
176
|
+
|
177
|
+
result = yield
|
178
|
+
|
179
|
+
duration = GraphQL::Metrics.current_time_monotonic - start_time
|
180
|
+
time_since_offset = start_time - offset_time if offset_time
|
181
|
+
|
182
|
+
path_excluding_numeric_indicies = query.context[:current_path].select { |p| p.is_a?(String) }
|
183
|
+
ns[context_key][path_excluding_numeric_indicies] ||= []
|
184
|
+
ns[context_key][path_excluding_numeric_indicies] << {
|
185
|
+
start_time_offset: time_since_offset, duration: duration
|
186
|
+
}
|
187
|
+
|
188
|
+
result
|
189
|
+
end
|
190
|
+
end
|
191
|
+
end
|
192
|
+
end
|
@@ -19,6 +19,12 @@ module GraphQL
|
|
19
19
|
GRAPHQL_GEM_TRACING_LAZY_FIELD_KEY = 'execute_field_lazy'
|
20
20
|
]
|
21
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
|
+
|
22
28
|
def trace(key, data, &block)
|
23
29
|
# NOTE: Context doesn't exist yet during lexing, parsing.
|
24
30
|
context = data[:query]&.context
|
@@ -54,130 +60,11 @@ module GraphQL
|
|
54
60
|
GraphQL::Metrics::LAZY_FIELD_TIMINGS
|
55
61
|
end
|
56
62
|
|
57
|
-
trace_field(context_key, data, &block)
|
63
|
+
trace_field(context_key, data[:query], &block)
|
58
64
|
else
|
59
65
|
return yield
|
60
66
|
end
|
61
67
|
end
|
62
|
-
|
63
|
-
private
|
64
|
-
|
65
|
-
PreContext = Struct.new(
|
66
|
-
:multiplex_start_time,
|
67
|
-
:multiplex_start_time_monotonic,
|
68
|
-
:parsing_start_time_offset,
|
69
|
-
:parsing_duration,
|
70
|
-
:lexing_start_time_offset,
|
71
|
-
:lexing_duration
|
72
|
-
) do
|
73
|
-
def reset
|
74
|
-
self[:multiplex_start_time] = nil
|
75
|
-
self[:multiplex_start_time_monotonic] = nil
|
76
|
-
self[:parsing_start_time_offset] = nil
|
77
|
-
self[:parsing_duration] = nil
|
78
|
-
self[:lexing_start_time_offset] = nil
|
79
|
-
self[:lexing_duration] = nil
|
80
|
-
end
|
81
|
-
end
|
82
|
-
|
83
|
-
def pre_context
|
84
|
-
# NOTE: This is used to store timings from lexing, parsing, validation, before we have a context to store
|
85
|
-
# values in. Uses thread-safe Concurrent::ThreadLocalVar to store a set of values per thread.
|
86
|
-
@pre_context ||= Concurrent::ThreadLocalVar.new(PreContext.new)
|
87
|
-
@pre_context.value
|
88
|
-
end
|
89
|
-
|
90
|
-
def capture_multiplex_start_time
|
91
|
-
pre_context.multiplex_start_time = GraphQL::Metrics.current_time
|
92
|
-
pre_context.multiplex_start_time_monotonic = GraphQL::Metrics.current_time_monotonic
|
93
|
-
|
94
|
-
yield
|
95
|
-
end
|
96
|
-
|
97
|
-
def capture_lexing_time
|
98
|
-
timed_result = GraphQL::Metrics.time { yield }
|
99
|
-
|
100
|
-
pre_context.lexing_start_time_offset = timed_result.start_time
|
101
|
-
pre_context.lexing_duration = timed_result.duration
|
102
|
-
|
103
|
-
timed_result.result
|
104
|
-
end
|
105
|
-
|
106
|
-
def capture_parsing_time
|
107
|
-
timed_result = GraphQL::Metrics.time { yield }
|
108
|
-
|
109
|
-
pre_context.parsing_start_time_offset = timed_result.start_time
|
110
|
-
pre_context.parsing_duration = timed_result.duration
|
111
|
-
|
112
|
-
timed_result.result
|
113
|
-
end
|
114
|
-
|
115
|
-
# Also consolidates parsing timings (if any) from the `pre_context`
|
116
|
-
def capture_validation_time(context)
|
117
|
-
# Queries may already be lexed and parsed before execution (whether a single query or multiplex).
|
118
|
-
# If we don't have those values, use some sane defaults.
|
119
|
-
if pre_context.lexing_duration.nil?
|
120
|
-
pre_context.lexing_start_time_offset = pre_context.multiplex_start_time
|
121
|
-
pre_context.lexing_duration = 0.0
|
122
|
-
end
|
123
|
-
if pre_context.parsing_duration.nil?
|
124
|
-
pre_context.parsing_start_time_offset = pre_context.multiplex_start_time
|
125
|
-
pre_context.parsing_duration = 0.0
|
126
|
-
end
|
127
|
-
|
128
|
-
timed_result = GraphQL::Metrics.time(pre_context.multiplex_start_time_monotonic) { yield }
|
129
|
-
|
130
|
-
ns = context.namespace(CONTEXT_NAMESPACE)
|
131
|
-
|
132
|
-
ns[MULTIPLEX_START_TIME] = pre_context.multiplex_start_time
|
133
|
-
ns[MULTIPLEX_START_TIME_MONOTONIC] = pre_context.multiplex_start_time_monotonic
|
134
|
-
ns[LEXING_START_TIME_OFFSET] = pre_context.lexing_start_time_offset
|
135
|
-
ns[LEXING_DURATION] = pre_context.lexing_duration
|
136
|
-
ns[PARSING_START_TIME_OFFSET] = pre_context.parsing_start_time_offset
|
137
|
-
ns[PARSING_DURATION] = pre_context.parsing_duration
|
138
|
-
ns[VALIDATION_START_TIME_OFFSET] = timed_result.time_since_offset
|
139
|
-
ns[VALIDATION_DURATION] = timed_result.duration
|
140
|
-
|
141
|
-
timed_result.result
|
142
|
-
end
|
143
|
-
|
144
|
-
def capture_analysis_time(context)
|
145
|
-
ns = context.namespace(CONTEXT_NAMESPACE)
|
146
|
-
|
147
|
-
timed_result = GraphQL::Metrics.time(ns[MULTIPLEX_START_TIME_MONOTONIC]) { yield }
|
148
|
-
|
149
|
-
ns[ANALYSIS_START_TIME_OFFSET] = timed_result.time_since_offset
|
150
|
-
ns[ANALYSIS_DURATION] = timed_result.duration
|
151
|
-
|
152
|
-
timed_result.result
|
153
|
-
end
|
154
|
-
|
155
|
-
def capture_query_start_time(context)
|
156
|
-
ns = context.namespace(CONTEXT_NAMESPACE)
|
157
|
-
ns[QUERY_START_TIME] = GraphQL::Metrics.current_time
|
158
|
-
ns[QUERY_START_TIME_MONOTONIC] = GraphQL::Metrics.current_time_monotonic
|
159
|
-
|
160
|
-
yield
|
161
|
-
end
|
162
|
-
|
163
|
-
def trace_field(context_key, data)
|
164
|
-
ns = data[:query].context.namespace(CONTEXT_NAMESPACE)
|
165
|
-
offset_time = ns[GraphQL::Metrics::QUERY_START_TIME_MONOTONIC]
|
166
|
-
start_time = GraphQL::Metrics.current_time_monotonic
|
167
|
-
|
168
|
-
result = yield
|
169
|
-
|
170
|
-
duration = GraphQL::Metrics.current_time_monotonic - start_time
|
171
|
-
time_since_offset = start_time - offset_time if offset_time
|
172
|
-
|
173
|
-
path_excluding_numeric_indicies = data[:path].select { |p| p.is_a?(String) }
|
174
|
-
ns[context_key][path_excluding_numeric_indicies] ||= []
|
175
|
-
ns[context_key][path_excluding_numeric_indicies] << {
|
176
|
-
start_time_offset: time_since_offset, duration: duration
|
177
|
-
}
|
178
|
-
|
179
|
-
result
|
180
|
-
end
|
181
68
|
end
|
182
69
|
end
|
183
70
|
end
|
data/lib/graphql/metrics.rb
CHANGED
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: 5.0.
|
4
|
+
version: 5.0.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Christopher Butcher
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-03-
|
11
|
+
date: 2023-03-27 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: graphql
|
@@ -218,10 +218,12 @@ files:
|
|
218
218
|
- bin/setup
|
219
219
|
- gemfiles/graphql_1.13.gemfile
|
220
220
|
- gemfiles/graphql_2.0.gemfile
|
221
|
+
- gemfiles/graphql_head.gemfile
|
221
222
|
- graphql_metrics.gemspec
|
222
223
|
- lib/graphql/metrics.rb
|
223
224
|
- lib/graphql/metrics/analyzer.rb
|
224
225
|
- lib/graphql/metrics/instrumentation.rb
|
226
|
+
- lib/graphql/metrics/trace.rb
|
225
227
|
- lib/graphql/metrics/tracer.rb
|
226
228
|
- lib/graphql/metrics/version.rb
|
227
229
|
homepage: https://github.com/Shopify/graphql-metrics
|