graphql 2.4.3 → 2.5.2

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.
Files changed (166) hide show
  1. checksums.yaml +4 -4
  2. data/lib/graphql/analysis/analyzer.rb +2 -1
  3. data/lib/graphql/analysis/visitor.rb +38 -41
  4. data/lib/graphql/analysis.rb +15 -12
  5. data/lib/graphql/autoload.rb +38 -0
  6. data/lib/graphql/backtrace/table.rb +118 -55
  7. data/lib/graphql/backtrace.rb +1 -19
  8. data/lib/graphql/current.rb +6 -1
  9. data/lib/graphql/dashboard/detailed_traces.rb +47 -0
  10. data/lib/graphql/dashboard/installable.rb +22 -0
  11. data/lib/graphql/dashboard/limiters.rb +93 -0
  12. data/lib/graphql/dashboard/operation_store.rb +199 -0
  13. data/lib/graphql/dashboard/statics/bootstrap-5.3.3.min.css +6 -0
  14. data/lib/graphql/dashboard/statics/bootstrap-5.3.3.min.js +7 -0
  15. data/lib/graphql/dashboard/statics/charts.min.css +1 -0
  16. data/lib/graphql/dashboard/statics/dashboard.css +30 -0
  17. data/lib/graphql/dashboard/statics/dashboard.js +143 -0
  18. data/lib/graphql/dashboard/statics/header-icon.png +0 -0
  19. data/lib/graphql/dashboard/statics/icon.png +0 -0
  20. data/lib/graphql/dashboard/subscriptions.rb +96 -0
  21. data/lib/graphql/dashboard/views/graphql/dashboard/detailed_traces/traces/index.html.erb +45 -0
  22. data/lib/graphql/dashboard/views/graphql/dashboard/landings/show.html.erb +18 -0
  23. data/lib/graphql/dashboard/views/graphql/dashboard/limiters/limiters/show.html.erb +62 -0
  24. data/lib/graphql/dashboard/views/graphql/dashboard/not_installed.html.erb +18 -0
  25. data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/clients/_form.html.erb +23 -0
  26. data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/clients/edit.html.erb +21 -0
  27. data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/clients/index.html.erb +69 -0
  28. data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/clients/new.html.erb +7 -0
  29. data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/index_entries/index.html.erb +39 -0
  30. data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/index_entries/show.html.erb +32 -0
  31. data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/operations/index.html.erb +81 -0
  32. data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/operations/show.html.erb +71 -0
  33. data/lib/graphql/dashboard/views/graphql/dashboard/subscriptions/subscriptions/show.html.erb +41 -0
  34. data/lib/graphql/dashboard/views/graphql/dashboard/subscriptions/topics/index.html.erb +55 -0
  35. data/lib/graphql/dashboard/views/graphql/dashboard/subscriptions/topics/show.html.erb +40 -0
  36. data/lib/graphql/dashboard/views/layouts/graphql/dashboard/application.html.erb +108 -0
  37. data/lib/graphql/dashboard.rb +158 -0
  38. data/lib/graphql/dataloader/active_record_association_source.rb +64 -0
  39. data/lib/graphql/dataloader/active_record_source.rb +26 -0
  40. data/lib/graphql/dataloader/async_dataloader.rb +21 -9
  41. data/lib/graphql/dataloader/null_dataloader.rb +1 -1
  42. data/lib/graphql/dataloader/source.rb +3 -3
  43. data/lib/graphql/dataloader.rb +43 -14
  44. data/lib/graphql/execution/interpreter/resolve.rb +3 -3
  45. data/lib/graphql/execution/interpreter/runtime/graphql_result.rb +34 -4
  46. data/lib/graphql/execution/interpreter/runtime.rb +94 -51
  47. data/lib/graphql/execution/interpreter.rb +16 -7
  48. data/lib/graphql/execution/multiplex.rb +1 -5
  49. data/lib/graphql/introspection/directive_location_enum.rb +1 -1
  50. data/lib/graphql/invalid_name_error.rb +1 -1
  51. data/lib/graphql/invalid_null_error.rb +5 -15
  52. data/lib/graphql/language/cache.rb +13 -0
  53. data/lib/graphql/language/document_from_schema_definition.rb +8 -7
  54. data/lib/graphql/language/lexer.rb +11 -4
  55. data/lib/graphql/language/nodes.rb +3 -0
  56. data/lib/graphql/language/parser.rb +15 -8
  57. data/lib/graphql/language/printer.rb +8 -8
  58. data/lib/graphql/language/static_visitor.rb +37 -33
  59. data/lib/graphql/language/visitor.rb +59 -55
  60. data/lib/graphql/pagination/connection.rb +1 -1
  61. data/lib/graphql/query/context/scoped_context.rb +1 -1
  62. data/lib/graphql/query/context.rb +6 -5
  63. data/lib/graphql/query/variable_validation_error.rb +1 -1
  64. data/lib/graphql/query.rb +19 -23
  65. data/lib/graphql/railtie.rb +7 -0
  66. data/lib/graphql/schema/addition.rb +1 -1
  67. data/lib/graphql/schema/argument.rb +7 -8
  68. data/lib/graphql/schema/build_from_definition.rb +99 -53
  69. data/lib/graphql/schema/directive/flagged.rb +3 -1
  70. data/lib/graphql/schema/directive.rb +2 -2
  71. data/lib/graphql/schema/enum.rb +36 -1
  72. data/lib/graphql/schema/enum_value.rb +1 -1
  73. data/lib/graphql/schema/field/scope_extension.rb +1 -1
  74. data/lib/graphql/schema/field.rb +27 -13
  75. data/lib/graphql/schema/field_extension.rb +1 -1
  76. data/lib/graphql/schema/has_single_input_argument.rb +3 -1
  77. data/lib/graphql/schema/input_object.rb +77 -40
  78. data/lib/graphql/schema/interface.rb +3 -2
  79. data/lib/graphql/schema/loader.rb +1 -1
  80. data/lib/graphql/schema/member/has_arguments.rb +25 -17
  81. data/lib/graphql/schema/member/has_dataloader.rb +60 -0
  82. data/lib/graphql/schema/member/has_deprecation_reason.rb +15 -0
  83. data/lib/graphql/schema/member/has_directives.rb +4 -4
  84. data/lib/graphql/schema/member/has_fields.rb +19 -1
  85. data/lib/graphql/schema/member/has_interfaces.rb +5 -5
  86. data/lib/graphql/schema/member/has_validators.rb +1 -1
  87. data/lib/graphql/schema/member/scoped.rb +1 -1
  88. data/lib/graphql/schema/member/type_system_helpers.rb +1 -1
  89. data/lib/graphql/schema/member.rb +1 -0
  90. data/lib/graphql/schema/object.rb +25 -8
  91. data/lib/graphql/schema/relay_classic_mutation.rb +0 -1
  92. data/lib/graphql/schema/resolver.rb +12 -10
  93. data/lib/graphql/schema/subscription.rb +52 -6
  94. data/lib/graphql/schema/union.rb +1 -1
  95. data/lib/graphql/schema/validator/required_validator.rb +23 -6
  96. data/lib/graphql/schema/validator.rb +1 -1
  97. data/lib/graphql/schema/visibility/migration.rb +1 -0
  98. data/lib/graphql/schema/visibility/profile.rb +95 -243
  99. data/lib/graphql/schema/visibility/visit.rb +190 -0
  100. data/lib/graphql/schema/visibility.rb +169 -28
  101. data/lib/graphql/schema/warden.rb +18 -5
  102. data/lib/graphql/schema.rb +93 -44
  103. data/lib/graphql/static_validation/all_rules.rb +1 -1
  104. data/lib/graphql/static_validation/rules/argument_names_are_unique.rb +1 -1
  105. data/lib/graphql/static_validation/rules/fields_have_appropriate_selections.rb +1 -1
  106. data/lib/graphql/static_validation/rules/fields_will_merge.rb +1 -1
  107. data/lib/graphql/static_validation/rules/no_definitions_are_present.rb +1 -1
  108. data/lib/graphql/static_validation/rules/not_single_subscription_error.rb +25 -0
  109. data/lib/graphql/static_validation/rules/required_arguments_are_present.rb +1 -1
  110. data/lib/graphql/static_validation/rules/subscription_root_exists_and_single_subscription_selection.rb +26 -0
  111. data/lib/graphql/static_validation/rules/unique_directives_per_location.rb +1 -1
  112. data/lib/graphql/static_validation/rules/variable_names_are_unique.rb +1 -1
  113. data/lib/graphql/static_validation/rules/variable_usages_are_allowed.rb +1 -1
  114. data/lib/graphql/static_validation/validation_context.rb +1 -0
  115. data/lib/graphql/static_validation/validator.rb +6 -1
  116. data/lib/graphql/subscriptions/action_cable_subscriptions.rb +1 -1
  117. data/lib/graphql/subscriptions/default_subscription_resolve_extension.rb +12 -10
  118. data/lib/graphql/subscriptions/event.rb +12 -1
  119. data/lib/graphql/subscriptions/serialize.rb +1 -1
  120. data/lib/graphql/subscriptions.rb +1 -1
  121. data/lib/graphql/testing/helpers.rb +7 -4
  122. data/lib/graphql/tracing/active_support_notifications_trace.rb +14 -3
  123. data/lib/graphql/tracing/active_support_notifications_tracing.rb +1 -1
  124. data/lib/graphql/tracing/appoptics_trace.rb +9 -1
  125. data/lib/graphql/tracing/appoptics_tracing.rb +7 -0
  126. data/lib/graphql/tracing/appsignal_trace.rb +32 -55
  127. data/lib/graphql/tracing/appsignal_tracing.rb +2 -0
  128. data/lib/graphql/tracing/call_legacy_tracers.rb +66 -0
  129. data/lib/graphql/tracing/data_dog_trace.rb +46 -158
  130. data/lib/graphql/tracing/data_dog_tracing.rb +2 -0
  131. data/lib/graphql/tracing/detailed_trace/memory_backend.rb +60 -0
  132. data/lib/graphql/tracing/detailed_trace/redis_backend.rb +72 -0
  133. data/lib/graphql/tracing/detailed_trace.rb +93 -0
  134. data/lib/graphql/tracing/legacy_hooks_trace.rb +1 -0
  135. data/lib/graphql/tracing/legacy_trace.rb +4 -61
  136. data/lib/graphql/tracing/monitor_trace.rb +283 -0
  137. data/lib/graphql/tracing/new_relic_trace.rb +47 -54
  138. data/lib/graphql/tracing/new_relic_tracing.rb +2 -0
  139. data/lib/graphql/tracing/notifications_trace.rb +182 -34
  140. data/lib/graphql/tracing/notifications_tracing.rb +2 -0
  141. data/lib/graphql/tracing/null_trace.rb +9 -0
  142. data/lib/graphql/tracing/perfetto_trace/trace.proto +141 -0
  143. data/lib/graphql/tracing/perfetto_trace/trace_pb.rb +33 -0
  144. data/lib/graphql/tracing/perfetto_trace.rb +734 -0
  145. data/lib/graphql/tracing/platform_trace.rb +5 -0
  146. data/lib/graphql/tracing/prometheus_trace/graphql_collector.rb +2 -0
  147. data/lib/graphql/tracing/prometheus_trace.rb +72 -68
  148. data/lib/graphql/tracing/prometheus_tracing.rb +2 -0
  149. data/lib/graphql/tracing/scout_trace.rb +32 -55
  150. data/lib/graphql/tracing/scout_tracing.rb +2 -0
  151. data/lib/graphql/tracing/sentry_trace.rb +62 -94
  152. data/lib/graphql/tracing/statsd_trace.rb +33 -41
  153. data/lib/graphql/tracing/statsd_tracing.rb +2 -0
  154. data/lib/graphql/tracing/trace.rb +111 -1
  155. data/lib/graphql/tracing.rb +31 -30
  156. data/lib/graphql/types/relay/connection_behaviors.rb +3 -3
  157. data/lib/graphql/types/relay/edge_behaviors.rb +2 -2
  158. data/lib/graphql/types.rb +18 -11
  159. data/lib/graphql/version.rb +1 -1
  160. data/lib/graphql.rb +55 -47
  161. metadata +146 -11
  162. data/lib/graphql/backtrace/inspect_result.rb +0 -38
  163. data/lib/graphql/backtrace/trace.rb +0 -93
  164. data/lib/graphql/backtrace/tracer.rb +0 -80
  165. data/lib/graphql/schema/null_mask.rb +0 -11
  166. data/lib/graphql/static_validation/rules/subscription_root_exists.rb +0 -17
@@ -39,6 +39,9 @@ module GraphQL
39
39
  include(BaseKeyCache)
40
40
  }
41
41
  child_class.const_set(:KeyCache, key_methods_class)
42
+
43
+ # rubocop:disable Development/NoEvalCop This eval takes static inputs at load-time
44
+
42
45
  [:execute_field, :execute_field_lazy].each do |field_trace_method|
43
46
  if !child_class.method_defined?(field_trace_method)
44
47
  child_class.module_eval <<-RUBY, __FILE__, __LINE__
@@ -91,6 +94,8 @@ module GraphQL
91
94
  end
92
95
  RUBY
93
96
  end
97
+
98
+ # rubocop:enable Development/NoEvalCop
94
99
  end
95
100
  end
96
101
 
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "graphql/tracing"
4
+
3
5
  module GraphQL
4
6
  module Tracing
5
7
  module PrometheusTrace
@@ -1,87 +1,91 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "graphql/tracing/monitor_trace"
4
+
3
5
  module GraphQL
4
6
  module Tracing
7
+ # A tracer for reporting GraphQL-Ruby times to Prometheus.
8
+ #
9
+ # The PrometheusExporter server must be run with a custom type collector that extends `GraphQL::Tracing::PrometheusTracing::GraphQLCollector`.
10
+ #
11
+ # @example Adding this trace to your schema
12
+ # require 'prometheus_exporter/client'
13
+ #
14
+ # class MySchema < GraphQL::Schema
15
+ # trace_with GraphQL::Tracing::PrometheusTrace
16
+ # end
17
+ #
18
+ # @example Running a custom type collector
19
+ # # lib/graphql_collector.rb
20
+ # if defined?(PrometheusExporter::Server)
21
+ # require 'graphql/tracing'
22
+ #
23
+ # class GraphQLCollector < GraphQL::Tracing::PrometheusTrace::GraphQLCollector
24
+ # end
25
+ # end
26
+ #
27
+ # # Then run:
28
+ # # bundle exec prometheus_exporter -a lib/graphql_collector.rb
29
+ PrometheusTrace = MonitorTrace.create_module("prometheus")
5
30
  module PrometheusTrace
6
- include PlatformTrace
7
-
8
- def initialize(client: PrometheusExporter::Client.default, keys_whitelist: ["execute_field", "execute_field_lazy"], collector_type: "graphql", **rest)
9
- @client = client
10
- @keys_whitelist = keys_whitelist
11
- @collector_type = collector_type
12
-
13
- super(**rest)
14
- end
15
-
16
- {
17
- 'lex' => "graphql.lex",
18
- 'parse' => "graphql.parse",
19
- 'validate' => "graphql.validate",
20
- 'analyze_query' => "graphql.analyze",
21
- 'analyze_multiplex' => "graphql.analyze",
22
- 'execute_multiplex' => "graphql.execute",
23
- 'execute_query' => "graphql.execute",
24
- 'execute_query_lazy' => "graphql.execute",
25
- }.each do |trace_method, platform_key|
26
- module_eval <<-RUBY, __FILE__, __LINE__
27
- def #{trace_method}(**data)
28
- instrument_prometheus_execution("#{platform_key}", "#{trace_method}") { super }
29
- end
30
- RUBY
31
- end
32
-
33
- def platform_execute_field(platform_key, &block)
34
- instrument_prometheus_execution(platform_key, "execute_field", &block)
35
- end
36
-
37
- def platform_execute_field_lazy(platform_key, &block)
38
- instrument_prometheus_execution(platform_key, "execute_field_lazy", &block)
31
+ if defined?(PrometheusExporter::Server)
32
+ autoload :GraphQLCollector, "graphql/tracing/prometheus_trace/graphql_collector"
39
33
  end
40
34
 
41
- def platform_authorized(platform_key, &block)
42
- instrument_prometheus_execution(platform_key, "authorized", &block)
35
+ def initialize(client: PrometheusExporter::Client.default, keys_whitelist: [:execute_field], collector_type: "graphql", **rest)
36
+ @prometheus_client = client
37
+ @prometheus_keys_whitelist = keys_whitelist.map(&:to_sym) # handle previous string keys
38
+ @prometheus_collector_type = collector_type
39
+ setup_prometheus_monitor(**rest)
40
+ super
43
41
  end
44
42
 
45
- def platform_authorized_lazy(platform_key, &block)
46
- instrument_prometheus_execution(platform_key, "authorized_lazy", &block)
47
- end
43
+ attr_reader :prometheus_collector_type, :prometheus_client, :prometheus_keys_whitelist
48
44
 
49
- def platform_resolve_type(platform_key, &block)
50
- instrument_prometheus_execution(platform_key, "resolve_type", &block)
51
- end
45
+ class PrometheusMonitor < MonitorTrace::Monitor
46
+ def instrument(keyword, object)
47
+ if active?(keyword)
48
+ start = gettime
49
+ result = yield
50
+ duration = gettime - start
51
+ send_json(duration, keyword, object)
52
+ result
53
+ else
54
+ yield
55
+ end
56
+ end
52
57
 
53
- def platform_resolve_type_lazy(platform_key, &block)
54
- instrument_prometheus_execution(platform_key, "resolve_type_lazy", &block)
55
- end
58
+ def active?(keyword)
59
+ @trace.prometheus_keys_whitelist.include?(keyword)
60
+ end
56
61
 
57
- def platform_field_key(field)
58
- field.path
59
- end
62
+ def gettime
63
+ ::Process.clock_gettime(::Process::CLOCK_MONOTONIC)
64
+ end
60
65
 
61
- def platform_authorized_key(type)
62
- "#{type.graphql_name}.authorized"
63
- end
66
+ def send_json(duration, keyword, object)
67
+ event_name = name_for(keyword, object)
68
+ @trace.prometheus_client.send_json(
69
+ type: @trace.prometheus_collector_type,
70
+ duration: duration,
71
+ platform_key: event_name,
72
+ key: keyword
73
+ )
74
+ end
64
75
 
65
- def platform_resolve_type_key(type)
66
- "#{type.graphql_name}.resolve_type"
67
- end
76
+ include MonitorTrace::Monitor::GraphQLPrefixNames
68
77
 
69
- private
78
+ class Event < MonitorTrace::Monitor::Event
79
+ def start
80
+ @start_time = @monitor.gettime
81
+ end
70
82
 
71
- def instrument_prometheus_execution(platform_key, key, &block)
72
- if @keys_whitelist.include?(key)
73
- start = ::Process.clock_gettime ::Process::CLOCK_MONOTONIC
74
- result = block.call
75
- duration = ::Process.clock_gettime(::Process::CLOCK_MONOTONIC) - start
76
- @client.send_json(
77
- type: @collector_type,
78
- duration: duration,
79
- platform_key: platform_key,
80
- key: key
81
- )
82
- result
83
- else
84
- yield
83
+ def finish
84
+ if @monitor.active?(keyword)
85
+ duration = @monitor.gettime - @start_time
86
+ @monitor.send_json(duration, keyword, object)
87
+ end
88
+ end
85
89
  end
86
90
  end
87
91
  end
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "graphql/tracing/platform_tracing"
4
+
3
5
  module GraphQL
4
6
  module Tracing
5
7
  class PrometheusTracing < PlatformTracing
@@ -1,71 +1,48 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "graphql/tracing/monitor_trace"
4
+
3
5
  module GraphQL
4
6
  module Tracing
7
+ # A tracer for sending GraphQL-Ruby times to Scout
8
+ #
9
+ # @example Adding this tracer to your schema
10
+ # class MySchema < GraphQL::Schema
11
+ # trace_with GraphQL::Tracing::ScoutTrace
12
+ # end
13
+ ScoutTrace = MonitorTrace.create_module("scout")
5
14
  module ScoutTrace
6
- include PlatformTrace
7
-
8
- INSTRUMENT_OPTS = { scope: true }
9
-
10
- # @param set_transaction_name [Boolean] If true, the GraphQL operation name will be used as the transaction name.
11
- # This is not advised if you run more than one query per HTTP request, for example, with `graphql-client` or multiplexing.
12
- # It can also be specified per-query with `context[:set_scout_transaction_name]`.
13
- def initialize(set_transaction_name: false, **_rest)
14
- self.class.include(ScoutApm::Tracer)
15
- @set_transaction_name = set_transaction_name
16
- super
17
- end
18
-
19
- {
20
- "lex" => "lex.graphql",
21
- "parse" => "parse.graphql",
22
- "validate" => "validate.graphql",
23
- "analyze_query" => "analyze.graphql",
24
- "analyze_multiplex" => "analyze.graphql",
25
- "execute_multiplex" => "execute.graphql",
26
- "execute_query" => "execute.graphql",
27
- "execute_query_lazy" => "execute.graphql",
28
- }.each do |trace_method, platform_key|
29
- module_eval <<-RUBY, __FILE__, __LINE__
30
- def #{trace_method}(**data)
31
- #{
32
- if trace_method == "execute_query"
33
- <<-RUBY
34
- set_this_txn_name = data[:query].context[:set_scout_transaction_name]
35
- if set_this_txn_name == true || (set_this_txn_name.nil? && @set_transaction_name)
36
- ScoutApm::Transaction.rename(transaction_name(data[:query]))
37
- end
38
- RUBY
15
+ class ScoutMonitor < MonitorTrace::Monitor
16
+ def instrument(keyword, object)
17
+ if keyword == :execute
18
+ query = object.queries.first
19
+ set_this_txn_name = query.context[:set_scout_transaction_name]
20
+ if set_this_txn_name == true || (set_this_txn_name.nil? && @set_transaction_name)
21
+ ScoutApm::Transaction.rename(transaction_name(query))
39
22
  end
40
- }
23
+ end
41
24
 
42
- self.class.instrument("GraphQL", "#{platform_key}", INSTRUMENT_OPTS) do
43
- super
25
+ ScoutApm::Tracer.instrument("GraphQL", name_for(keyword, object), INSTRUMENT_OPTS) do
26
+ yield
44
27
  end
45
28
  end
46
- RUBY
47
- end
48
-
49
- def platform_execute_field(platform_key, &block)
50
- self.class.instrument("GraphQL", platform_key, INSTRUMENT_OPTS, &block)
51
- end
52
-
53
- def platform_authorized(platform_key, &block)
54
- self.class.instrument("GraphQL", platform_key, INSTRUMENT_OPTS, &block)
55
- end
56
29
 
57
- alias :platform_resolve_type :platform_authorized
30
+ INSTRUMENT_OPTS = { scope: true }
58
31
 
59
- def platform_field_key(field)
60
- field.path
61
- end
32
+ include MonitorTrace::Monitor::GraphQLSuffixNames
62
33
 
63
- def platform_authorized_key(type)
64
- "#{type.graphql_name}.authorized"
65
- end
34
+ class Event < MonitorTrace::Monitor::Event
35
+ def start
36
+ layer = ScoutApm::Layer.new("GraphQL", @monitor.name_for(keyword, object))
37
+ layer.subscopable!
38
+ @scout_req = ScoutApm::RequestManager.lookup
39
+ @scout_req.start_layer(layer)
40
+ end
66
41
 
67
- def platform_resolve_type_key(type)
68
- "#{type.graphql_name}.resolve_type"
42
+ def finish
43
+ @scout_req.stop_layer
44
+ end
45
+ end
69
46
  end
70
47
  end
71
48
  end
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "graphql/tracing/platform_tracing"
4
+
3
5
  module GraphQL
4
6
  module Tracing
5
7
  class ScoutTracing < PlatformTracing
@@ -1,110 +1,78 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "graphql/tracing/monitor_trace"
4
+
3
5
  module GraphQL
4
6
  module Tracing
7
+ # A tracer for reporting GraphQL-Ruby times to Sentry.
8
+ #
9
+ # @example Installing the tracer
10
+ # class MySchema < GraphQL::Schema
11
+ # trace_with GraphQL::Tracing::SentryTrace
12
+ # end
13
+ # @see MonitorTrace Configuration Options in the parent module
14
+ SentryTrace = MonitorTrace.create_module("sentry")
5
15
  module SentryTrace
6
- include PlatformTrace
7
-
8
- # @param set_transaction_name [Boolean] If true, the GraphQL operation name will be used as the transaction name.
9
- # This is not advised if you run more than one query per HTTP request, for example, with `graphql-client` or multiplexing.
10
- # It can also be specified per-query with `context[:set_sentry_transaction_name]`.
11
- def initialize(set_transaction_name: false, **_rest)
12
- @set_transaction_name = set_transaction_name
13
- super
14
- end
15
-
16
- def execute_query(**data)
17
- set_this_txn_name = data[:query].context[:set_sentry_transaction_name]
18
- if set_this_txn_name == true || (set_this_txn_name.nil? && @set_transaction_name)
19
- Sentry.configure_scope do |scope|
20
- scope.set_transaction_name(transaction_name(data[:query]))
16
+ class SentryMonitor < MonitorTrace::Monitor
17
+ def instrument(keyword, object)
18
+ return yield unless Sentry.initialized?
19
+
20
+ platform_key = name_for(keyword, object)
21
+
22
+ Sentry.with_child_span(op: platform_key, start_timestamp: Sentry.utc_now.to_f) do |span|
23
+ result = yield
24
+ return result unless span
25
+
26
+ span.finish
27
+ if keyword == :execute
28
+ queries = object.queries
29
+ operation_names = queries.map{|q| operation_name(q) }
30
+ span.set_description(operation_names.join(", "))
31
+
32
+ if queries.size == 1
33
+ query = queries.first
34
+ set_this_txn_name = query.context[:set_sentry_transaction_name]
35
+ if set_this_txn_name == true || (set_this_txn_name.nil? && @set_transaction_name)
36
+ Sentry.configure_scope do |scope|
37
+ scope.set_transaction_name(transaction_name(query))
38
+ end
39
+ end
40
+ span.set_data('graphql.document', query.query_string)
41
+ if query.selected_operation_name
42
+ span.set_data('graphql.operation.name', query.selected_operation_name)
43
+ end
44
+ span.set_data('graphql.operation.type', query.selected_operation.operation_type)
45
+ end
46
+ end
47
+
48
+ result
21
49
  end
22
50
  end
23
- instrument_sentry_execution("graphql.execute", "execute_query", data) { super }
24
- end
25
-
26
- {
27
- "lex" => "graphql.lex",
28
- "parse" => "graphql.parse",
29
- "validate" => "graphql.validate",
30
- "analyze_query" => "graphql.analyze",
31
- "analyze_multiplex" => "graphql.analyze_multiplex",
32
- "execute_multiplex" => "graphql.execute_multiplex",
33
- "execute_query_lazy" => "graphql.execute"
34
- }.each do |trace_method, platform_key|
35
- module_eval <<-RUBY, __FILE__, __LINE__
36
- def #{trace_method}(**data)
37
- instrument_sentry_execution("#{platform_key}", "#{trace_method}", data) { super }
38
- end
39
- RUBY
40
- end
41
-
42
- def platform_execute_field(platform_key, &block)
43
- instrument_sentry_execution(platform_key, "execute_field", &block)
44
- end
45
-
46
- def platform_execute_field_lazy(platform_key, &block)
47
- instrument_sentry_execution(platform_key, "execute_field_lazy", &block)
48
- end
49
-
50
- def platform_authorized(platform_key, &block)
51
- instrument_sentry_execution(platform_key, "authorized", &block)
52
- end
53
-
54
- def platform_authorized_lazy(platform_key, &block)
55
- instrument_sentry_execution(platform_key, "authorized_lazy", &block)
56
- end
57
51
 
58
- def platform_resolve_type(platform_key, &block)
59
- instrument_sentry_execution(platform_key, "resolve_type", &block)
60
- end
61
-
62
- def platform_resolve_type_lazy(platform_key, &block)
63
- instrument_sentry_execution(platform_key, "resolve_type_lazy", &block)
64
- end
65
-
66
- def platform_field_key(field)
67
- "graphql.field.#{field.path}"
68
- end
69
-
70
- def platform_authorized_key(type)
71
- "graphql.authorized.#{type.graphql_name}"
72
- end
73
-
74
- def platform_resolve_type_key(type)
75
- "graphql.resolve_type.#{type.graphql_name}"
76
- end
77
-
78
- private
52
+ include MonitorTrace::Monitor::GraphQLPrefixNames
79
53
 
80
- def instrument_sentry_execution(platform_key, trace_method, data=nil, &block)
81
- return yield unless Sentry.initialized?
54
+ private
82
55
 
83
- Sentry.with_child_span(op: platform_key, start_timestamp: Sentry.utc_now.to_f) do |span|
84
- result = yield
85
- return result unless span
86
-
87
- span.finish
88
- if trace_method == "execute_multiplex" && data.key?(:multiplex)
89
- operation_names = data[:multiplex].queries.map{|q| operation_name(q) }
90
- span.set_description(operation_names.join(", "))
91
- elsif trace_method == "execute_query" && data.key?(:query)
92
- span.set_description(operation_name(data[:query]))
93
- span.set_data('graphql.document', data[:query].query_string)
94
- span.set_data('graphql.operation.name', data[:query].selected_operation_name) if data[:query].selected_operation_name
95
- span.set_data('graphql.operation.type', data[:query].selected_operation.operation_type)
56
+ def operation_name(query)
57
+ selected_op = query.selected_operation
58
+ if selected_op
59
+ [selected_op.operation_type, selected_op.name].compact.join(' ')
60
+ else
61
+ 'GraphQL Operation'
96
62
  end
97
-
98
- result
99
63
  end
100
- end
101
64
 
102
- def operation_name(query)
103
- selected_op = query.selected_operation
104
- if selected_op
105
- [selected_op.operation_type, selected_op.name].compact.join(' ')
106
- else
107
- 'GraphQL Operation'
65
+ class Event < MonitorTrace::Monitor::Event
66
+ def start
67
+ if Sentry.initialized? && (@span = Sentry.get_current_scope.get_span)
68
+ span_name = @monitor.name_for(@keyword, @object)
69
+ @span.start_child(op: span_name)
70
+ end
71
+ end
72
+
73
+ def finish
74
+ @span&.finish
75
+ end
108
76
  end
109
77
  end
110
78
  end
@@ -1,55 +1,47 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "graphql/tracing/monitor_trace"
4
+
3
5
  module GraphQL
4
6
  module Tracing
7
+ # A tracer for reporting GraphQL-Ruby times to Statsd.
8
+ # Passing any Statsd client that implements `.time(name) { ... }`
9
+ # and `.timing(name, ms)` will work.
10
+ #
11
+ # @example Installing this tracer
12
+ # # eg:
13
+ # # $statsd = Statsd.new 'localhost', 9125
14
+ # class MySchema < GraphQL::Schema
15
+ # use GraphQL::Tracing::StatsdTrace, statsd: $statsd
16
+ # end
17
+ StatsdTrace = MonitorTrace.create_module("statsd")
5
18
  module StatsdTrace
6
- include PlatformTrace
19
+ class StatsdMonitor < MonitorTrace::Monitor
20
+ def initialize(statsd:, **_rest)
21
+ @statsd = statsd
22
+ super
23
+ end
7
24
 
8
- # @param statsd [Object] A statsd client
9
- def initialize(statsd:, **rest)
10
- @statsd = statsd
11
- super(**rest)
12
- end
25
+ attr_reader :statsd
13
26
 
14
- {
15
- 'lex' => "graphql.lex",
16
- 'parse' => "graphql.parse",
17
- 'validate' => "graphql.validate",
18
- 'analyze_query' => "graphql.analyze_query",
19
- 'analyze_multiplex' => "graphql.analyze_multiplex",
20
- 'execute_multiplex' => "graphql.execute_multiplex",
21
- 'execute_query' => "graphql.execute_query",
22
- 'execute_query_lazy' => "graphql.execute_query_lazy",
23
- }.each do |trace_method, platform_key|
24
- module_eval <<-RUBY, __FILE__, __LINE__
25
- def #{trace_method}(**data)
26
- @statsd.time("#{platform_key}") do
27
- super
28
- end
27
+ def instrument(keyword, object)
28
+ @statsd.time(name_for(keyword, object)) do
29
+ yield
29
30
  end
30
- RUBY
31
- end
32
-
33
- def platform_execute_field(platform_key, &block)
34
- @statsd.time(platform_key, &block)
35
- end
36
-
37
- def platform_authorized(key, &block)
38
- @statsd.time(key, &block)
39
- end
40
-
41
- alias :platform_resolve_type :platform_authorized
31
+ end
42
32
 
43
- def platform_field_key(field)
44
- "graphql.#{field.path}"
45
- end
33
+ include MonitorTrace::Monitor::GraphQLPrefixNames
46
34
 
47
- def platform_authorized_key(type)
48
- "graphql.authorized.#{type.graphql_name}"
49
- end
35
+ class Event < MonitorTrace::Monitor::Event
36
+ def start
37
+ @start_time = Process.clock_gettime(Process::CLOCK_MONOTONIC)
38
+ end
50
39
 
51
- def platform_resolve_type_key(type)
52
- "graphql.resolve_type.#{type.graphql_name}"
40
+ def finish
41
+ elapsed = Process.clock_gettime(Process::CLOCK_MONOTONIC) - @start_time
42
+ @monitor.statsd.timing(@monitor.name_for(keyword, object), elapsed)
43
+ end
44
+ end
53
45
  end
54
46
  end
55
47
  end
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "graphql/tracing/platform_tracing"
4
+
3
5
  module GraphQL
4
6
  module Tracing
5
7
  class StatsdTracing < PlatformTracing