graphql-metrics 5.0.3 → 5.0.5

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: abf17ff04ff0eb71cd30861aeec84ea3fd53fe561affccdf375343fc7b8897b3
4
- data.tar.gz: 4297bb879eff66d756dded5a8e588e26bd65b4cf23d041208a465f979dc9a9f0
3
+ metadata.gz: 9f93d51a57f59de189c9b97ad3f0f26eee049c3ec1af8dc77951cb3e892558f8
4
+ data.tar.gz: 62531e77262446959b6309c57a34e29313d8c006af3acbd2a9b74b02888d78d7
5
5
  SHA512:
6
- metadata.gz: d6cff213c42b23297591abc236fbfe411c82f3e48456b7d9fa94063dbe422a06c95ab2145611d8ef5750e4a280024d80e61eb1bfb0203617168fee916d8c3eec
7
- data.tar.gz: 119101eac9cd8210b1713917bb45ad371ac1390b21249af677b8b4347c4e585668b4f5b49b029227130a2abc4a88985c6ec592e2e0c214f22866ec7204700f75
6
+ metadata.gz: 3b8782d2b3ffe8891f2f54e8554b8c3f1e121090fd74525f64ae38730a1bc4d277f0fa8a4bf7d7ae5e33258edae3e6f31351a1bf897507a92de828fcde5854a7
7
+ data.tar.gz: 16aa581d6b9073dbd6ca0db8ebada56c6d4af05e74e4f78ac0d20577768068f5f39b620860bfc4cb2470da1825dee9a0f25f8a26ec4e1dddaf8c22e1867ef8c4
@@ -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
@@ -1,5 +1,14 @@
1
+ 5.0.5
2
+ -----
3
+ - [73](https://github.com/Shopify/graphql-metrics/pull/73) Fix calls to other traces.
4
+
5
+ 5.0.4
6
+ -----
7
+ - [66](https://github.com/Shopify/graphql-metrics/pull/66) Support graphql-ruby's new tracing API (backwards compatible)
8
+ - [71](https://github.com/Shopify/graphql-metrics/pull/71) Fix handling of inline fragment without a parent type.
9
+
1
10
  5.0.3
2
- =====
11
+ -----
3
12
  - [69](https://github.com/Shopify/graphql-metrics/pull/69) Loosen concurrent-ruby dependency
4
13
 
5
14
  5.0.2
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ eval_gemfile("../Gemfile")
4
+
5
+ gem("graphql", github: "rmosolgo/graphql-ruby", branch: "master")
@@ -175,7 +175,7 @@ module GraphQL
175
175
  when GraphQL::Language::Nodes::OperationDefinition
176
176
  parent.operation_type
177
177
  when GraphQL::Language::Nodes::InlineFragment
178
- parent.type.name
178
+ parent&.type&.name
179
179
  else
180
180
  parent.name
181
181
  end
@@ -0,0 +1,191 @@
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:)
19
+ return super if @skip_tracing
20
+ capture_multiplex_start_time { super }
21
+ end
22
+
23
+ # may not trigger if the query is passed in pre-parsed
24
+ def lex(query_string:)
25
+ return super if @skip_tracing
26
+ capture_lexing_time { super }
27
+ end
28
+
29
+ # may not trigger if the query is passed in pre-parsed
30
+ def parse(query_string:)
31
+ return super if @skip_tracing
32
+ capture_parsing_time { super }
33
+ end
34
+
35
+ def validate(query:, validate:)
36
+ return super if @skip_tracing
37
+ capture_validation_time(query.context) { super }
38
+ end
39
+
40
+ # wraps all `analyze_query`s; only run once
41
+ def analyze_multiplex(multiplex:)
42
+ return super if @skip_tracing
43
+ # Ensures that we reset potentially long-lived PreContext objects between multiplexs. We reset at this point
44
+ # since all parsing and validation will be done by this point, and a GraphQL::Query::Context will exist.
45
+ pre_context.reset
46
+ super
47
+ end
48
+
49
+ def analyze_query(query:)
50
+ return super if @skip_tracing
51
+ capture_analysis_time(query.context) { super }
52
+ end
53
+
54
+ def execute_query(query:)
55
+ return super if @skip_tracing
56
+ capture_query_start_time(query.context) { super }
57
+ end
58
+
59
+ def execute_field(field:, query:, ast_node:, arguments:, object:)
60
+ return super if @skip_tracing || query.context[SKIP_FIELD_AND_ARGUMENT_METRICS]
61
+ return super unless GraphQL::Metrics.timings_capture_enabled?(query.context)
62
+ trace_field(GraphQL::Metrics::INLINE_FIELD_TIMINGS, query) { super }
63
+ end
64
+
65
+ def execute_field_lazy(field:, query:, ast_node:, arguments:, object:)
66
+ return super if @skip_tracing || query.context[SKIP_FIELD_AND_ARGUMENT_METRICS]
67
+ return super unless GraphQL::Metrics.timings_capture_enabled?(query.context)
68
+ trace_field(GraphQL::Metrics::LAZY_FIELD_TIMINGS, query) { super }
69
+ end
70
+
71
+ private
72
+
73
+ PreContext = Struct.new(
74
+ :multiplex_start_time,
75
+ :multiplex_start_time_monotonic,
76
+ :parsing_start_time_offset,
77
+ :parsing_duration,
78
+ :lexing_start_time_offset,
79
+ :lexing_duration
80
+ ) do
81
+ def reset
82
+ self[:multiplex_start_time] = nil
83
+ self[:multiplex_start_time_monotonic] = nil
84
+ self[:parsing_start_time_offset] = nil
85
+ self[:parsing_duration] = nil
86
+ self[:lexing_start_time_offset] = nil
87
+ self[:lexing_duration] = nil
88
+ end
89
+ end
90
+
91
+ def pre_context
92
+ # NOTE: This is used to store timings from lexing, parsing, validation, before we have a context to store
93
+ # values in. Uses thread-safe Concurrent::ThreadLocalVar to store a set of values per thread.
94
+ @pre_context ||= Concurrent::ThreadLocalVar.new(PreContext.new)
95
+ @pre_context.value
96
+ end
97
+
98
+ def capture_multiplex_start_time
99
+ pre_context.multiplex_start_time = GraphQL::Metrics.current_time
100
+ pre_context.multiplex_start_time_monotonic = GraphQL::Metrics.current_time_monotonic
101
+
102
+ yield
103
+ end
104
+
105
+ def capture_lexing_time
106
+ timed_result = GraphQL::Metrics.time { yield }
107
+
108
+ pre_context.lexing_start_time_offset = timed_result.start_time
109
+ pre_context.lexing_duration = timed_result.duration
110
+
111
+ timed_result.result
112
+ end
113
+
114
+ def capture_parsing_time
115
+ timed_result = GraphQL::Metrics.time { yield }
116
+
117
+ pre_context.parsing_start_time_offset = timed_result.start_time
118
+ pre_context.parsing_duration = timed_result.duration
119
+
120
+ timed_result.result
121
+ end
122
+
123
+ # Also consolidates parsing timings (if any) from the `pre_context`
124
+ def capture_validation_time(context)
125
+ # Queries may already be lexed and parsed before execution (whether a single query or multiplex).
126
+ # If we don't have those values, use some sane defaults.
127
+ if pre_context.lexing_duration.nil?
128
+ pre_context.lexing_start_time_offset = pre_context.multiplex_start_time
129
+ pre_context.lexing_duration = 0.0
130
+ end
131
+ if pre_context.parsing_duration.nil?
132
+ pre_context.parsing_start_time_offset = pre_context.multiplex_start_time
133
+ pre_context.parsing_duration = 0.0
134
+ end
135
+
136
+ timed_result = GraphQL::Metrics.time(pre_context.multiplex_start_time_monotonic) { yield }
137
+
138
+ ns = context.namespace(CONTEXT_NAMESPACE)
139
+
140
+ ns[MULTIPLEX_START_TIME] = pre_context.multiplex_start_time
141
+ ns[MULTIPLEX_START_TIME_MONOTONIC] = pre_context.multiplex_start_time_monotonic
142
+ ns[LEXING_START_TIME_OFFSET] = pre_context.lexing_start_time_offset
143
+ ns[LEXING_DURATION] = pre_context.lexing_duration
144
+ ns[PARSING_START_TIME_OFFSET] = pre_context.parsing_start_time_offset
145
+ ns[PARSING_DURATION] = pre_context.parsing_duration
146
+ ns[VALIDATION_START_TIME_OFFSET] = timed_result.time_since_offset
147
+ ns[VALIDATION_DURATION] = timed_result.duration
148
+
149
+ timed_result.result
150
+ end
151
+
152
+ def capture_analysis_time(context)
153
+ ns = context.namespace(CONTEXT_NAMESPACE)
154
+
155
+ timed_result = GraphQL::Metrics.time(ns[MULTIPLEX_START_TIME_MONOTONIC]) { yield }
156
+
157
+ ns[ANALYSIS_START_TIME_OFFSET] = timed_result.time_since_offset
158
+ ns[ANALYSIS_DURATION] = timed_result.duration
159
+
160
+ timed_result.result
161
+ end
162
+
163
+ def capture_query_start_time(context)
164
+ ns = context.namespace(CONTEXT_NAMESPACE)
165
+ ns[QUERY_START_TIME] = GraphQL::Metrics.current_time
166
+ ns[QUERY_START_TIME_MONOTONIC] = GraphQL::Metrics.current_time_monotonic
167
+
168
+ yield
169
+ end
170
+
171
+ def trace_field(context_key, query)
172
+ ns = query.context.namespace(CONTEXT_NAMESPACE)
173
+ offset_time = ns[GraphQL::Metrics::QUERY_START_TIME_MONOTONIC]
174
+ start_time = GraphQL::Metrics.current_time_monotonic
175
+
176
+ result = yield
177
+
178
+ duration = GraphQL::Metrics.current_time_monotonic - start_time
179
+ time_since_offset = start_time - offset_time if offset_time
180
+
181
+ path_excluding_numeric_indicies = query.context[:current_path].select { |p| p.is_a?(String) }
182
+ ns[context_key][path_excluding_numeric_indicies] ||= []
183
+ ns[context_key][path_excluding_numeric_indicies] << {
184
+ start_time_offset: time_since_offset, duration: duration
185
+ }
186
+
187
+ result
188
+ end
189
+ end
190
+ end
191
+ 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
@@ -2,6 +2,6 @@
2
2
 
3
3
  module GraphQL
4
4
  module Metrics
5
- VERSION = "5.0.3"
5
+ VERSION = "5.0.5"
6
6
  end
7
7
  end
@@ -3,6 +3,7 @@
3
3
  require "concurrent"
4
4
  require "graphql/metrics/version"
5
5
  require "graphql/metrics/instrumentation"
6
+ require "graphql/metrics/trace"
6
7
  require "graphql/metrics/tracer"
7
8
  require "graphql/metrics/analyzer"
8
9
 
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.3
4
+ version: 5.0.5
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: 2023-03-06 00:00:00.000000000 Z
11
+ date: 2023-04-10 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: graphql
@@ -218,17 +218,19 @@ 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
228
230
  licenses:
229
231
  - MIT
230
232
  metadata: {}
231
- post_install_message:
233
+ post_install_message:
232
234
  rdoc_options: []
233
235
  require_paths:
234
236
  - lib
@@ -243,8 +245,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
243
245
  - !ruby/object:Gem::Version
244
246
  version: '0'
245
247
  requirements: []
246
- rubygems_version: 3.3.3
247
- signing_key:
248
+ rubygems_version: 3.0.3.1
249
+ signing_key:
248
250
  specification_version: 4
249
251
  summary: GraphQL Metrics Extractor
250
252
  test_files: []