graphql-metrics 3.0.1 → 4.0.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: cf09941ca4f9ed80c7d1080c6c121fa1cb1ac66ed55735dcb74cec0bbbe3a8a2
4
- data.tar.gz: 18a8b3feafc4bb5be6c065cf05b91da34fee5c826e77d67ad14ea48a23630459
3
+ metadata.gz: 2af911e9031c446b604b9c296caef2c89f00cb968ecf15350a6a196d6cab08d7
4
+ data.tar.gz: 94e65ae882fd6222bf3b4ed9395f32dfc395ec00c85e061a458cb5fe54b9dc67
5
5
  SHA512:
6
- metadata.gz: 4e11ef76ccd1d038fab97151f40418e5386b5d2d80b45ca1238f5c0ebf60b6a593980bde4efe4fc60c63b3c590bc7da803e8fcb04ce1c9906fa0cf6edecb0e74
7
- data.tar.gz: 32684bc8c1e3fac2e73b4efad7969bb84c38fc34a89510e450e0b423b5ec66104a8cd0fea016b91a47e26299a01df0b09da82597a4fd5dd763d64de4677c04f7
6
+ metadata.gz: aa3ffe4208e67594c3e65786a0a0477cdcee71506dea37988db388433eb155175d87f92e30082931e5e8c493937876e9a5a4043327b74e9e17bc474d58bd4ed9
7
+ data.tar.gz: e876e9f5907cff0863a6ead3e319f34ab238b1904d0f72c8874420fdf50b5cb2941cb0f77fcfbefac35968c2bb86ec6364c5e338842dcee591eb165ee2ed14d6
@@ -76,13 +76,6 @@ Style/BlockDelimiters:
76
76
  - proc
77
77
  - it
78
78
 
79
- Style/BracesAroundHashParameters:
80
- EnforcedStyle: no_braces
81
- SupportedStyles:
82
- - braces
83
- - no_braces
84
- - context_dependent
85
-
86
79
  Layout/CaseIndentation:
87
80
  EnforcedStyle: end
88
81
  SupportedStyles:
@@ -509,7 +502,7 @@ Style/WhileUntilModifier:
509
502
  Metrics/BlockNesting:
510
503
  Max: 3
511
504
 
512
- Metrics/LineLength:
505
+ Layout/LineLength:
513
506
  Max: 120
514
507
  AllowHeredoc: true
515
508
  AllowURI: true
@@ -1,3 +1,26 @@
1
+ 4.0.2
2
+ -----
3
+ - [25](https://github.com/Shopify/graphql-metrics/pull/25) Safely handle interrupted runtime metrics.
4
+
5
+ 4.0.1
6
+ -----
7
+ - [24](https://github.com/Shopify/graphql-metrics/pull/24) Safely call `arguments_for` to handle arguments which may
8
+ raise `ExecutionError`s in their `prepare` methods.
9
+
10
+ 4.0.0
11
+ -----
12
+ - [23](https://github.com/Shopify/graphql-metrics/pull/23) graphql-ruby 1.10.8+ compatibility
13
+
14
+ 3.0.3
15
+ -----
16
+
17
+ - [#22](https://github.com/Shopify/graphql-metrics/pull/22) Optimization: use hash assignment over merge
18
+
19
+ 3.0.2
20
+ -----
21
+
22
+ - [#21](https://github.com/Shopify/graphql-metrics/pull/21) Optimize empty document check
23
+
1
24
  3.0.1
2
25
  -----
3
26
 
@@ -1,9 +1,9 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- graphql-metrics (3.0.1)
4
+ graphql-metrics (4.0.1)
5
5
  concurrent-ruby (~> 1.1.0)
6
- graphql (>= 1.9.5, < 1.10.0)
6
+ graphql (>= 1.10.8)
7
7
 
8
8
  GEM
9
9
  remote: https://rubygems.org/
@@ -19,8 +19,8 @@ GEM
19
19
  diffy (3.3.0)
20
20
  fakeredis (0.7.0)
21
21
  redis (>= 3.2, < 5.0)
22
- graphql (1.9.19)
23
- graphql-batch (0.4.1)
22
+ graphql (1.10.8)
23
+ graphql-batch (0.4.2)
24
24
  graphql (>= 1.3, < 2)
25
25
  promise.rb (~> 0.7.2)
26
26
  hashdiff (1.0.0)
data/README.md CHANGED
@@ -5,6 +5,8 @@
5
5
  Extract as much much detail as you want from GraphQL queries, served up from your Ruby app and the [`graphql` gem](https://github.com/rmosolgo/graphql-ruby).
6
6
  Compatible with the [`graphql-batch` gem](https://github.com/Shopify/graphql-batch), to extract batch-loaded fields resolution timings.
7
7
 
8
+ Be sure to read the [CHANGELOG](CHANGELOG.md) to stay updated on feature additions, breaking changes made to this gem.
9
+
8
10
  ## Installation
9
11
 
10
12
  Add this line to your application's Gemfile:
@@ -42,6 +44,13 @@ etc.
42
44
 
43
45
  What you do with these captured metrics is up to you!
44
46
 
47
+ **NOTE**: Runtime metrics on for queries (like `query_duration`, `parsing_start_time_offset` etc.) as well as field
48
+ resolver timings (like `resolver_timings`, `lazy_resolver_timings`) may not be present in the extracted `metrics` hash,
49
+ even if you opt to collect them by using `GraphQL::Metrics::Analyzer` and `GraphQL::Metrics::Tracer`.
50
+
51
+ More specifically, if any non-`graphql-ruby` gem-related exceptions occur in your application during query document
52
+ parsing and validation runtime metrics will not be added to the `metrics` hash.
53
+
45
54
  ### Define your own analyzer subclass
46
55
 
47
56
  ```ruby
@@ -172,9 +181,10 @@ class Schema < GraphQL::Schema
172
181
  use GraphQL::Execution::Interpreter # Required.
173
182
  use GraphQL::Analysis::AST # Required.
174
183
 
175
- instrument :query, GraphQL::Metrics::Instrumentation.new
176
184
  query_analyzer SimpleAnalyzer
177
- tracer GraphQL::Metrics::Tracer.new
185
+
186
+ instrument :query, GraphQL::Metrics::Instrumentation.new # Both of these are required if either is used.
187
+ tracer GraphQL::Metrics::Tracer.new # <-- Note!
178
188
 
179
189
  use GraphQL::Batch # Optional, but highly recommended. See https://github.com/Shopify/graphql-batch/.
180
190
  end
@@ -30,9 +30,8 @@ Gem::Specification.new do |spec|
30
30
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
31
31
  spec.require_paths = ["lib"]
32
32
 
33
+ spec.add_runtime_dependency "graphql", ">= 1.10.8"
33
34
  spec.add_runtime_dependency "concurrent-ruby", "~> 1.1.0"
34
- spec.add_runtime_dependency "graphql", ">= 1.9.5", "< 1.10.0"
35
-
36
35
  spec.add_development_dependency "rake", "~> 10.0"
37
36
  spec.add_development_dependency "minitest", "~> 5.0"
38
37
  spec.add_development_dependency 'graphql-batch'
@@ -35,14 +35,21 @@ module GraphQL
35
35
  return if visitor.field_definition.introspection?
36
36
  return if query.context[SKIP_FIELD_AND_ARGUMENT_METRICS]
37
37
 
38
- # NOTE: @rmosolgo "I think it could be reduced to `arguments = visitor.arguments_for(ast_node)`"
39
- arguments = visitor.arguments_for(node, visitor.field_definition)
40
- extract_arguments(arguments.argument_values.values, visitor.field_definition)
38
+ # Arguments can raise execution errors within their `prepare` methods
39
+ # which aren't properly handled during analysis so we have to handle
40
+ # them ourselves safely and return `nil`.
41
+ argument_values = begin
42
+ query.arguments_for(node, visitor.field_definition)
43
+ rescue ::GraphQL::ExecutionError
44
+ nil
45
+ end
46
+
47
+ extract_arguments(argument_values, visitor.field_definition) if argument_values
41
48
 
42
49
  static_metrics = {
43
50
  field_name: node.name,
44
- return_type_name: visitor.type_definition.name,
45
- parent_type_name: visitor.parent_type_definition.name,
51
+ return_type_name: visitor.type_definition.graphql_name,
52
+ parent_type_name: visitor.parent_type_definition.graphql_name,
46
53
  deprecated: visitor.field_definition.deprecation_reason.present?,
47
54
  path: visitor.response_path,
48
55
  }
@@ -54,21 +61,22 @@ module GraphQL
54
61
  end
55
62
  end
56
63
 
57
- def extract_fields_with_runtime_metrics
64
+ def extract_fields(with_runtime_metrics: true)
58
65
  return if query.context[SKIP_FIELD_AND_ARGUMENT_METRICS]
59
66
 
60
67
  ns = query.context.namespace(CONTEXT_NAMESPACE)
61
68
 
62
69
  @static_field_metrics.each do |static_metrics|
63
- resolver_timings = ns[GraphQL::Metrics::INLINE_FIELD_TIMINGS][static_metrics[:path]]
64
- lazy_field_timings = ns[GraphQL::Metrics::LAZY_FIELD_TIMINGS][static_metrics[:path]]
65
70
 
66
- metrics = static_metrics.merge(
67
- resolver_timings: resolver_timings || [],
68
- lazy_resolver_timings: lazy_field_timings || [],
69
- )
71
+ if with_runtime_metrics
72
+ resolver_timings = ns[GraphQL::Metrics::INLINE_FIELD_TIMINGS][static_metrics[:path]]
73
+ lazy_resolver_timings = ns[GraphQL::Metrics::LAZY_FIELD_TIMINGS][static_metrics[:path]]
70
74
 
71
- field_extracted(metrics)
75
+ static_metrics[:resolver_timings] = resolver_timings || []
76
+ static_metrics[:lazy_resolver_timings] = lazy_resolver_timings || []
77
+ end
78
+
79
+ field_extracted(static_metrics)
72
80
  end
73
81
  end
74
82
 
@@ -95,16 +103,16 @@ module GraphQL
95
103
  argument.each_value do |a|
96
104
  extract_arguments(a, field_defn, parent_input_object)
97
105
  end
98
- when ::GraphQL::Query::Arguments
106
+ when ::GraphQL::Execution::Interpreter::Arguments
99
107
  argument.each_value do |arg_val|
100
108
  extract_arguments(arg_val, field_defn, parent_input_object)
101
109
  end
102
- when ::GraphQL::Query::Arguments::ArgumentValue
110
+ when ::GraphQL::Execution::Interpreter::ArgumentValue
103
111
  extract_argument(argument, field_defn, parent_input_object)
104
112
  extract_arguments(argument.value, field_defn, parent_input_object)
105
113
  when ::GraphQL::Schema::InputObject
106
114
  input_object_argument_values = argument.arguments.argument_values.values
107
- parent_input_object = input_object_argument_values.first&.definition&.metadata&.fetch(:type_class, nil)&.owner
115
+ parent_input_object = input_object_argument_values.first&.definition&.owner
108
116
 
109
117
  extract_arguments(input_object_argument_values, field_defn, parent_input_object)
110
118
  end
@@ -112,10 +120,10 @@ module GraphQL
112
120
 
113
121
  def extract_argument(value, field_defn, parent_input_object = nil)
114
122
  static_metrics = {
115
- argument_name: value.definition.expose_as,
116
- argument_type_name: value.definition.type.unwrap.to_s,
117
- parent_field_name: field_defn.name,
118
- parent_field_type_name: field_defn.metadata[:type_class].owner.graphql_name,
123
+ argument_name: value.definition.graphql_name,
124
+ argument_type_name: value.definition.type.unwrap.graphql_name,
125
+ parent_field_name: field_defn.graphql_name,
126
+ parent_field_type_name: field_defn.owner.graphql_name,
119
127
  parent_input_object_type: parent_input_object&.graphql_name,
120
128
  default_used: value.default_used?,
121
129
  value_is_null: value.value.nil?,
@@ -25,29 +25,45 @@ module GraphQL
25
25
  return if query.context[GraphQL::Metrics::SKIP_GRAPHQL_METRICS_ANALYSIS]
26
26
 
27
27
  ns = query.context.namespace(CONTEXT_NAMESPACE)
28
+ analyzer = ns[GraphQL::Metrics::ANALYZER_INSTANCE_KEY]
28
29
 
29
- # NOTE: The start time stored at `ns[GraphQL::Metrics::QUERY_START_TIME_MONOTONIC]` is captured during query
30
- # parsing, which occurs before `Instrumentation#before_query`.
31
- query_duration = GraphQL::Metrics.current_time_monotonic - ns[GraphQL::Metrics::QUERY_START_TIME_MONOTONIC]
30
+ if runtime_metrics_interrupted?(ns)
31
+ # If runtime metrics were interrupted, then it's most likely that the application raised an exception and that
32
+ # query parsing (which is instrumented by GraphQL::Metrics::Tracer) was abruptly stopped.
33
+ #
34
+ # In this scenario, we still attempt to log whatever static query metrics we've collected, with runtime
35
+ # metrics (like query, field resolver timings) excluded.
36
+ analyzer.extract_fields(with_runtime_metrics: false)
37
+ analyzer.extract_query
38
+ else
39
+ query_duration = GraphQL::Metrics.current_time_monotonic - ns[GraphQL::Metrics::QUERY_START_TIME_MONOTONIC]
32
40
 
33
- runtime_query_metrics = {
34
- query_start_time: ns[GraphQL::Metrics::QUERY_START_TIME],
35
- query_duration: query_duration,
36
- parsing_start_time_offset: ns[GraphQL::Metrics::PARSING_START_TIME_OFFSET],
37
- parsing_duration: ns[GraphQL::Metrics::PARSING_DURATION],
38
- validation_start_time_offset: ns[GraphQL::Metrics::VALIDATION_START_TIME_OFFSET],
39
- validation_duration: ns[GraphQL::Metrics::VALIDATION_DURATION],
40
- }
41
+ runtime_query_metrics = {
42
+ query_start_time: ns[GraphQL::Metrics::QUERY_START_TIME],
43
+ query_duration: query_duration,
44
+ parsing_start_time_offset: ns[GraphQL::Metrics::PARSING_START_TIME_OFFSET],
45
+ parsing_duration: ns[GraphQL::Metrics::PARSING_DURATION],
46
+ validation_start_time_offset: ns[GraphQL::Metrics::VALIDATION_START_TIME_OFFSET],
47
+ validation_duration: ns[GraphQL::Metrics::VALIDATION_DURATION],
48
+ }
41
49
 
42
- analyzer = ns[GraphQL::Metrics::ANALYZER_INSTANCE_KEY]
43
- analyzer.extract_fields_with_runtime_metrics
44
- analyzer.extract_query(runtime_query_metrics: runtime_query_metrics)
50
+ analyzer.extract_fields
51
+ analyzer.extract_query(runtime_query_metrics: runtime_query_metrics)
52
+ end
45
53
  end
46
54
 
47
55
  private
48
56
 
49
57
  def query_present_and_valid?(query)
50
- query.valid? && query.document.to_query_string.present?
58
+ # Check for selected_operation as well for graphql 1.9 compatibility
59
+ # which did not reject "empty" documents in its parser.
60
+ query.valid? && !query.selected_operation.nil?
61
+ end
62
+
63
+ def runtime_metrics_interrupted?(context_namespace)
64
+ # NOTE: The start time stored at `ns[GraphQL::Metrics::QUERY_START_TIME_MONOTONIC]` is captured during query
65
+ # parsing, which occurs before `Instrumentation#before_query`.
66
+ context_namespace.key?(GraphQL::Metrics::QUERY_START_TIME_MONOTONIC) == false
51
67
  end
52
68
  end
53
69
  end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module GraphQL
4
4
  module Metrics
5
- VERSION = "3.0.1"
5
+ VERSION = "4.0.2"
6
6
  end
7
7
  end
metadata CHANGED
@@ -1,49 +1,43 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: graphql-metrics
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.0.1
4
+ version: 4.0.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Christopher Butcher
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2020-03-16 00:00:00.000000000 Z
11
+ date: 2020-06-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
- name: concurrent-ruby
14
+ name: graphql
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - "~>"
17
+ - - ">="
18
18
  - !ruby/object:Gem::Version
19
- version: 1.1.0
19
+ version: 1.10.8
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
- - - "~>"
24
+ - - ">="
25
25
  - !ruby/object:Gem::Version
26
- version: 1.1.0
26
+ version: 1.10.8
27
27
  - !ruby/object:Gem::Dependency
28
- name: graphql
28
+ name: concurrent-ruby
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
- - - ">="
32
- - !ruby/object:Gem::Version
33
- version: 1.9.5
34
- - - "<"
31
+ - - "~>"
35
32
  - !ruby/object:Gem::Version
36
- version: 1.10.0
33
+ version: 1.1.0
37
34
  type: :runtime
38
35
  prerelease: false
39
36
  version_requirements: !ruby/object:Gem::Requirement
40
37
  requirements:
41
- - - ">="
42
- - !ruby/object:Gem::Version
43
- version: 1.9.5
44
- - - "<"
38
+ - - "~>"
45
39
  - !ruby/object:Gem::Version
46
- version: 1.10.0
40
+ version: 1.1.0
47
41
  - !ruby/object:Gem::Dependency
48
42
  name: rake
49
43
  requirement: !ruby/object:Gem::Requirement