graphql 2.4.5 → 2.5.21
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/lib/generators/graphql/detailed_trace_generator.rb +77 -0
- data/lib/generators/graphql/templates/create_graphql_detailed_traces.erb +10 -0
- data/lib/graphql/analysis/analyzer.rb +2 -1
- data/lib/graphql/analysis/query_complexity.rb +87 -7
- data/lib/graphql/analysis/visitor.rb +37 -40
- data/lib/graphql/analysis.rb +12 -9
- data/lib/graphql/autoload.rb +1 -0
- data/lib/graphql/backtrace/table.rb +118 -55
- data/lib/graphql/backtrace.rb +1 -19
- data/lib/graphql/current.rb +6 -1
- data/lib/graphql/dashboard/application_controller.rb +41 -0
- data/lib/graphql/dashboard/detailed_traces.rb +47 -0
- data/lib/graphql/dashboard/installable.rb +22 -0
- data/lib/graphql/dashboard/landings_controller.rb +9 -0
- data/lib/graphql/dashboard/limiters.rb +93 -0
- data/lib/graphql/dashboard/operation_store.rb +199 -0
- data/lib/graphql/dashboard/statics/bootstrap-5.3.3.min.css +6 -0
- data/lib/graphql/dashboard/statics/bootstrap-5.3.3.min.js +7 -0
- data/lib/graphql/dashboard/statics/charts.min.css +1 -0
- data/lib/graphql/dashboard/statics/dashboard.css +30 -0
- data/lib/graphql/dashboard/statics/dashboard.js +143 -0
- data/lib/graphql/dashboard/statics/header-icon.png +0 -0
- data/lib/graphql/dashboard/statics/icon.png +0 -0
- data/lib/graphql/dashboard/statics_controller.rb +31 -0
- data/lib/graphql/dashboard/subscriptions.rb +97 -0
- data/lib/graphql/dashboard/views/graphql/dashboard/detailed_traces/traces/index.html.erb +45 -0
- data/lib/graphql/dashboard/views/graphql/dashboard/landings/show.html.erb +18 -0
- data/lib/graphql/dashboard/views/graphql/dashboard/limiters/limiters/show.html.erb +62 -0
- data/lib/graphql/dashboard/views/graphql/dashboard/not_installed.html.erb +18 -0
- data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/clients/_form.html.erb +24 -0
- data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/clients/edit.html.erb +21 -0
- data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/clients/index.html.erb +69 -0
- data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/clients/new.html.erb +7 -0
- data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/index_entries/index.html.erb +39 -0
- data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/index_entries/show.html.erb +32 -0
- data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/operations/index.html.erb +81 -0
- data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/operations/show.html.erb +71 -0
- data/lib/graphql/dashboard/views/graphql/dashboard/subscriptions/subscriptions/show.html.erb +41 -0
- data/lib/graphql/dashboard/views/graphql/dashboard/subscriptions/topics/index.html.erb +55 -0
- data/lib/graphql/dashboard/views/graphql/dashboard/subscriptions/topics/show.html.erb +40 -0
- data/lib/graphql/dashboard/views/layouts/graphql/dashboard/application.html.erb +108 -0
- data/lib/graphql/dashboard.rb +96 -0
- data/lib/graphql/dataloader/active_record_association_source.rb +84 -0
- data/lib/graphql/dataloader/active_record_source.rb +47 -0
- data/lib/graphql/dataloader/async_dataloader.rb +38 -15
- data/lib/graphql/dataloader/null_dataloader.rb +55 -10
- data/lib/graphql/dataloader/source.rb +18 -6
- data/lib/graphql/dataloader.rb +110 -26
- data/lib/graphql/date_encoding_error.rb +1 -1
- data/lib/graphql/dig.rb +2 -1
- data/lib/graphql/execution/interpreter/resolve.rb +10 -16
- data/lib/graphql/execution/interpreter/runtime/graphql_result.rb +58 -5
- data/lib/graphql/execution/interpreter/runtime.rb +229 -93
- data/lib/graphql/execution/interpreter.rb +15 -24
- data/lib/graphql/execution/multiplex.rb +7 -6
- data/lib/graphql/execution/next/field_resolve_step.rb +690 -0
- data/lib/graphql/execution/next/load_argument_step.rb +60 -0
- data/lib/graphql/execution/next/prepare_object_step.rb +129 -0
- data/lib/graphql/execution/next/runner.rb +389 -0
- data/lib/graphql/execution/next/selections_step.rb +37 -0
- data/lib/graphql/execution/next.rb +69 -0
- data/lib/graphql/execution.rb +1 -0
- data/lib/graphql/execution_error.rb +13 -10
- data/lib/graphql/introspection/directive_location_enum.rb +1 -1
- data/lib/graphql/introspection/directive_type.rb +7 -3
- data/lib/graphql/introspection/dynamic_fields.rb +5 -1
- data/lib/graphql/introspection/entry_points.rb +11 -3
- data/lib/graphql/introspection/enum_value_type.rb +5 -5
- data/lib/graphql/introspection/field_type.rb +13 -5
- data/lib/graphql/introspection/input_value_type.rb +21 -13
- data/lib/graphql/introspection/type_type.rb +64 -28
- data/lib/graphql/invalid_name_error.rb +1 -1
- data/lib/graphql/invalid_null_error.rb +25 -16
- data/lib/graphql/language/document_from_schema_definition.rb +2 -1
- data/lib/graphql/language/lexer.rb +16 -5
- data/lib/graphql/language/nodes.rb +8 -1
- data/lib/graphql/language/parser.rb +16 -8
- data/lib/graphql/language/static_visitor.rb +37 -33
- data/lib/graphql/language/visitor.rb +59 -55
- data/lib/graphql/language.rb +21 -12
- data/lib/graphql/pagination/connection.rb +2 -0
- data/lib/graphql/pagination/connections.rb +32 -0
- data/lib/graphql/query/context.rb +6 -10
- data/lib/graphql/query/null_context.rb +9 -3
- data/lib/graphql/query/partial.rb +179 -0
- data/lib/graphql/query.rb +64 -64
- data/lib/graphql/railtie.rb +1 -1
- data/lib/graphql/schema/addition.rb +3 -1
- data/lib/graphql/schema/always_visible.rb +1 -0
- data/lib/graphql/schema/argument.rb +24 -8
- data/lib/graphql/schema/build_from_definition.rb +113 -54
- data/lib/graphql/schema/directive/flagged.rb +2 -0
- data/lib/graphql/schema/directive.rb +52 -2
- data/lib/graphql/schema/enum.rb +36 -1
- data/lib/graphql/schema/enum_value.rb +1 -1
- data/lib/graphql/schema/field/connection_extension.rb +15 -35
- data/lib/graphql/schema/field/scope_extension.rb +22 -13
- data/lib/graphql/schema/field.rb +101 -51
- data/lib/graphql/schema/field_extension.rb +33 -0
- data/lib/graphql/schema/input_object.rb +45 -38
- data/lib/graphql/schema/interface.rb +2 -1
- data/lib/graphql/schema/list.rb +1 -1
- data/lib/graphql/schema/member/base_dsl_methods.rb +1 -1
- data/lib/graphql/schema/member/has_arguments.rb +56 -19
- data/lib/graphql/schema/member/has_authorization.rb +35 -0
- data/lib/graphql/schema/member/has_dataloader.rb +79 -0
- data/lib/graphql/schema/member/has_deprecation_reason.rb +15 -0
- data/lib/graphql/schema/member/has_directives.rb +1 -1
- data/lib/graphql/schema/member/has_fields.rb +81 -5
- data/lib/graphql/schema/member/has_interfaces.rb +3 -3
- data/lib/graphql/schema/member/scoped.rb +1 -1
- data/lib/graphql/schema/member/type_system_helpers.rb +17 -3
- data/lib/graphql/schema/member.rb +6 -0
- data/lib/graphql/schema/object.rb +18 -8
- data/lib/graphql/schema/ractor_shareable.rb +79 -0
- data/lib/graphql/schema/resolver.rb +52 -6
- data/lib/graphql/schema/scalar.rb +1 -6
- data/lib/graphql/schema/subscription.rb +50 -4
- data/lib/graphql/schema/timeout.rb +19 -2
- data/lib/graphql/schema/validator/required_validator.rb +71 -14
- data/lib/graphql/schema/visibility/migration.rb +3 -2
- data/lib/graphql/schema/visibility/profile.rb +115 -23
- data/lib/graphql/schema/visibility.rb +49 -32
- data/lib/graphql/schema/warden.rb +23 -2
- data/lib/graphql/schema.rb +333 -68
- data/lib/graphql/static_validation/all_rules.rb +2 -2
- data/lib/graphql/static_validation/rules/fields_have_appropriate_selections.rb +47 -13
- data/lib/graphql/static_validation/rules/fields_will_merge.rb +79 -17
- data/lib/graphql/static_validation/rules/fields_will_merge_error.rb +10 -2
- data/lib/graphql/static_validation/rules/not_single_subscription_error.rb +25 -0
- data/lib/graphql/static_validation/rules/subscription_root_exists_and_single_subscription_selection.rb +26 -0
- data/lib/graphql/static_validation/rules/unique_directives_per_location.rb +6 -2
- data/lib/graphql/static_validation/validator.rb +6 -1
- data/lib/graphql/subscriptions/action_cable_subscriptions.rb +1 -0
- data/lib/graphql/subscriptions/default_subscription_resolve_extension.rb +12 -10
- data/lib/graphql/subscriptions/event.rb +12 -1
- data/lib/graphql/subscriptions/serialize.rb +1 -1
- data/lib/graphql/subscriptions.rb +1 -1
- data/lib/graphql/testing/helpers.rb +17 -11
- data/lib/graphql/testing/mock_action_cable.rb +111 -0
- data/lib/graphql/testing.rb +1 -0
- data/lib/graphql/tracing/active_support_notifications_trace.rb +14 -3
- data/lib/graphql/tracing/active_support_notifications_tracing.rb +1 -1
- data/lib/graphql/tracing/appoptics_trace.rb +9 -1
- data/lib/graphql/tracing/appoptics_tracing.rb +7 -0
- data/lib/graphql/tracing/appsignal_trace.rb +32 -55
- data/lib/graphql/tracing/appsignal_tracing.rb +2 -0
- data/lib/graphql/tracing/call_legacy_tracers.rb +66 -0
- data/lib/graphql/tracing/data_dog_trace.rb +46 -158
- data/lib/graphql/tracing/data_dog_tracing.rb +2 -0
- data/lib/graphql/tracing/detailed_trace/active_record_backend.rb +74 -0
- data/lib/graphql/tracing/detailed_trace/memory_backend.rb +60 -0
- data/lib/graphql/tracing/detailed_trace/redis_backend.rb +72 -0
- data/lib/graphql/tracing/detailed_trace.rb +156 -0
- data/lib/graphql/tracing/legacy_hooks_trace.rb +1 -0
- data/lib/graphql/tracing/legacy_trace.rb +4 -61
- data/lib/graphql/tracing/monitor_trace.rb +283 -0
- data/lib/graphql/tracing/new_relic_trace.rb +47 -54
- data/lib/graphql/tracing/new_relic_tracing.rb +2 -0
- data/lib/graphql/tracing/notifications_trace.rb +184 -34
- data/lib/graphql/tracing/notifications_tracing.rb +2 -0
- data/lib/graphql/tracing/null_trace.rb +9 -0
- data/lib/graphql/tracing/perfetto_trace/trace.proto +141 -0
- data/lib/graphql/tracing/perfetto_trace/trace_pb.rb +33 -0
- data/lib/graphql/tracing/perfetto_trace.rb +864 -0
- data/lib/graphql/tracing/platform_trace.rb +5 -0
- data/lib/graphql/tracing/prometheus_trace/graphql_collector.rb +2 -0
- data/lib/graphql/tracing/prometheus_trace.rb +72 -68
- data/lib/graphql/tracing/prometheus_tracing.rb +2 -0
- data/lib/graphql/tracing/scout_trace.rb +32 -55
- data/lib/graphql/tracing/scout_tracing.rb +2 -0
- data/lib/graphql/tracing/sentry_trace.rb +64 -94
- data/lib/graphql/tracing/statsd_trace.rb +33 -41
- data/lib/graphql/tracing/statsd_tracing.rb +2 -0
- data/lib/graphql/tracing/trace.rb +111 -1
- data/lib/graphql/tracing.rb +31 -30
- data/lib/graphql/type_kinds.rb +1 -0
- data/lib/graphql/types/relay/connection_behaviors.rb +9 -7
- data/lib/graphql/types/relay/edge_behaviors.rb +5 -4
- data/lib/graphql/types/relay/has_node_field.rb +13 -8
- data/lib/graphql/types/relay/has_nodes_field.rb +13 -8
- data/lib/graphql/types/relay/node_behaviors.rb +13 -2
- data/lib/graphql/unauthorized_error.rb +5 -1
- data/lib/graphql/version.rb +1 -1
- data/lib/graphql.rb +12 -31
- metadata +174 -11
- data/lib/graphql/backtrace/inspect_result.rb +0 -38
- data/lib/graphql/backtrace/trace.rb +0 -93
- data/lib/graphql/backtrace/tracer.rb +0 -80
- data/lib/graphql/schema/null_mask.rb +0 -11
- data/lib/graphql/static_validation/rules/subscription_root_exists.rb +0 -17
|
@@ -1,11 +1,22 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require
|
|
3
|
+
require "graphql/tracing/notifications_trace"
|
|
4
4
|
|
|
5
5
|
module GraphQL
|
|
6
6
|
module Tracing
|
|
7
|
-
# This implementation forwards events to ActiveSupport::Notifications
|
|
8
|
-
#
|
|
7
|
+
# This implementation forwards events to ActiveSupport::Notifications with a `graphql` suffix.
|
|
8
|
+
#
|
|
9
|
+
# @example Sending execution events to ActiveSupport::Notifications
|
|
10
|
+
# class MySchema < GraphQL::Schema
|
|
11
|
+
# trace_with(GraphQL::Tracing::ActiveSupportNotificationsTrace)
|
|
12
|
+
# end
|
|
13
|
+
#
|
|
14
|
+
# @example Subscribing to GraphQL events with ActiveSupport::Notifications
|
|
15
|
+
# ActiveSupport::Notifications.subscribe(/graphql/) do |event|
|
|
16
|
+
# pp event.name
|
|
17
|
+
# pp event.payload
|
|
18
|
+
# end
|
|
19
|
+
#
|
|
9
20
|
module ActiveSupportNotificationsTrace
|
|
10
21
|
include NotificationsTrace
|
|
11
22
|
def initialize(engine: ActiveSupport::Notifications, **rest)
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require "graphql/tracing/platform_trace"
|
|
4
|
+
|
|
3
5
|
module GraphQL
|
|
4
6
|
module Tracing
|
|
5
7
|
|
|
@@ -20,14 +22,18 @@ module GraphQL
|
|
|
20
22
|
# These GraphQL events will show up as 'graphql.execute' spans
|
|
21
23
|
EXEC_KEYS = ['execute_multiplex', 'execute_query', 'execute_query_lazy'].freeze
|
|
22
24
|
|
|
25
|
+
|
|
23
26
|
# During auto-instrumentation this version of AppOpticsTracing is compared
|
|
24
27
|
# with the version provided in the appoptics_apm gem, so that the newer
|
|
25
28
|
# version of the class can be used
|
|
26
29
|
|
|
30
|
+
|
|
27
31
|
def self.version
|
|
28
32
|
Gem::Version.new('1.0.0')
|
|
29
33
|
end
|
|
30
34
|
|
|
35
|
+
# rubocop:disable Development/NoEvalCop This eval takes static inputs at load-time
|
|
36
|
+
|
|
31
37
|
[
|
|
32
38
|
'lex',
|
|
33
39
|
'parse',
|
|
@@ -55,6 +61,8 @@ module GraphQL
|
|
|
55
61
|
RUBY
|
|
56
62
|
end
|
|
57
63
|
|
|
64
|
+
# rubocop:enable Development/NoEvalCop
|
|
65
|
+
|
|
58
66
|
def execute_field(query:, field:, ast_node:, arguments:, object:)
|
|
59
67
|
return_type = field.type.unwrap
|
|
60
68
|
trace_field = if return_type.kind.scalar? || return_type.kind.enum?
|
|
@@ -81,7 +89,7 @@ module GraphQL
|
|
|
81
89
|
end
|
|
82
90
|
end
|
|
83
91
|
|
|
84
|
-
def execute_field_lazy(query:, field:, ast_node:, arguments:, object:)
|
|
92
|
+
def execute_field_lazy(query:, field:, ast_node:, arguments:, object:) # rubocop:disable Development/TraceCallsSuperCop
|
|
85
93
|
execute_field(query: query, field: field, ast_node: ast_node, arguments: arguments, object: object)
|
|
86
94
|
end
|
|
87
95
|
|
|
@@ -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
|
|
|
@@ -20,6 +22,11 @@ module GraphQL
|
|
|
20
22
|
# These GraphQL events will show up as 'graphql.execute' spans
|
|
21
23
|
EXEC_KEYS = ['execute_multiplex', 'execute_query', 'execute_query_lazy'].freeze
|
|
22
24
|
|
|
25
|
+
def initialize(...)
|
|
26
|
+
warn "GraphQL::Tracing::AppOptics tracing is deprecated; update to SolarWindsAPM instead, which uses OpenTelemetry."
|
|
27
|
+
super
|
|
28
|
+
end
|
|
29
|
+
|
|
23
30
|
# During auto-instrumentation this version of AppOpticsTracing is compared
|
|
24
31
|
# with the version provided in the appoptics_apm gem, so that the newer
|
|
25
32
|
# version of the class can be used
|
|
@@ -1,77 +1,54 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
|
+
require "graphql/tracing/monitor_trace"
|
|
2
3
|
|
|
3
4
|
module GraphQL
|
|
4
5
|
module Tracing
|
|
6
|
+
# Instrumentation for reporting GraphQL-Ruby times to Appsignal.
|
|
7
|
+
#
|
|
8
|
+
# @example Installing the tracer
|
|
9
|
+
# class MySchema < GraphQL::Schema
|
|
10
|
+
# trace_with GraphQL::Tracing::AppsignalTrace
|
|
11
|
+
# end
|
|
12
|
+
AppsignalTrace = MonitorTrace.create_module("appsignal")
|
|
5
13
|
module AppsignalTrace
|
|
6
|
-
include PlatformTrace
|
|
7
|
-
|
|
8
14
|
# @param set_action_name [Boolean] If true, the GraphQL operation name will be used as the transaction name.
|
|
9
15
|
# This is not advised if you run more than one query per HTTP request, for example, with `graphql-client` or multiplexing.
|
|
10
16
|
# It can also be specified per-query with `context[:set_appsignal_action_name]`.
|
|
11
17
|
def initialize(set_action_name: false, **rest)
|
|
12
|
-
|
|
18
|
+
rest[:set_transaction_name] ||= set_action_name
|
|
19
|
+
setup_appsignal_monitor(**rest)
|
|
13
20
|
super
|
|
14
21
|
end
|
|
15
22
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
"execute_query" => "execute.graphql",
|
|
24
|
-
"execute_query_lazy" => "execute.graphql",
|
|
25
|
-
}.each do |trace_method, platform_key|
|
|
26
|
-
module_eval <<-RUBY, __FILE__, __LINE__
|
|
27
|
-
def #{trace_method}(**data)
|
|
28
|
-
#{
|
|
29
|
-
if trace_method == "execute_query"
|
|
30
|
-
<<-RUBY
|
|
31
|
-
set_this_txn_name = data[:query].context[:set_appsignal_action_name]
|
|
32
|
-
if set_this_txn_name == true || (set_this_txn_name.nil? && @set_action_name)
|
|
33
|
-
Appsignal::Transaction.current.set_action(transaction_name(data[:query]))
|
|
34
|
-
end
|
|
35
|
-
RUBY
|
|
36
|
-
end
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
Appsignal.instrument("#{platform_key}") do
|
|
40
|
-
super
|
|
23
|
+
class AppsignalMonitor < MonitorTrace::Monitor
|
|
24
|
+
def instrument(keyword, object)
|
|
25
|
+
if keyword == :execute
|
|
26
|
+
query = object.queries.first
|
|
27
|
+
set_this_txn_name = query.context[:set_appsignal_action_name]
|
|
28
|
+
if set_this_txn_name == true || (set_this_txn_name.nil? && @set_transaction_name)
|
|
29
|
+
Appsignal::Transaction.current.set_action(transaction_name(query))
|
|
41
30
|
end
|
|
42
31
|
end
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
def platform_execute_field(platform_key)
|
|
47
|
-
Appsignal.instrument(platform_key) do
|
|
48
|
-
yield
|
|
32
|
+
Appsignal.instrument(name_for(keyword, object)) do
|
|
33
|
+
yield
|
|
34
|
+
end
|
|
49
35
|
end
|
|
50
|
-
end
|
|
51
36
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
37
|
+
include MonitorTrace::Monitor::GraphQLSuffixNames
|
|
38
|
+
class Event < GraphQL::Tracing::MonitorTrace::Monitor::Event
|
|
39
|
+
def start
|
|
40
|
+
Appsignal::Transaction.current.start_event
|
|
41
|
+
end
|
|
57
42
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
43
|
+
def finish
|
|
44
|
+
Appsignal::Transaction.current.finish_event(
|
|
45
|
+
@monitor.name_for(@keyword, @object),
|
|
46
|
+
"",
|
|
47
|
+
""
|
|
48
|
+
)
|
|
49
|
+
end
|
|
61
50
|
end
|
|
62
51
|
end
|
|
63
|
-
|
|
64
|
-
def platform_field_key(field)
|
|
65
|
-
"#{field.owner.graphql_name}.#{field.graphql_name}.graphql"
|
|
66
|
-
end
|
|
67
|
-
|
|
68
|
-
def platform_authorized_key(type)
|
|
69
|
-
"#{type.graphql_name}.authorized.graphql"
|
|
70
|
-
end
|
|
71
|
-
|
|
72
|
-
def platform_resolve_type_key(type)
|
|
73
|
-
"#{type.graphql_name}.resolve_type.graphql"
|
|
74
|
-
end
|
|
75
52
|
end
|
|
76
53
|
end
|
|
77
54
|
end
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module GraphQL
|
|
4
|
+
module Tracing
|
|
5
|
+
# This trace class calls legacy-style tracer with payload hashes.
|
|
6
|
+
# New-style `trace_with` modules significantly reduce the overhead of tracing,
|
|
7
|
+
# but that advantage is lost when legacy-style tracers are also used (since the payload hashes are still constructed).
|
|
8
|
+
module CallLegacyTracers
|
|
9
|
+
def lex(query_string:)
|
|
10
|
+
(@multiplex || @query).trace("lex", { query_string: query_string }) { super }
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def parse(query_string:)
|
|
14
|
+
(@multiplex || @query).trace("parse", { query_string: query_string }) { super }
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def validate(query:, validate:)
|
|
18
|
+
query.trace("validate", { validate: validate, query: query }) { super }
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def analyze_multiplex(multiplex:)
|
|
22
|
+
multiplex.trace("analyze_multiplex", { multiplex: multiplex }) { super }
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def analyze_query(query:)
|
|
26
|
+
query.trace("analyze_query", { query: query }) { super }
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def execute_multiplex(multiplex:)
|
|
30
|
+
multiplex.trace("execute_multiplex", { multiplex: multiplex }) { super }
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def execute_query(query:)
|
|
34
|
+
query.trace("execute_query", { query: query }) { super }
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def execute_query_lazy(query:, multiplex:)
|
|
38
|
+
multiplex.trace("execute_query_lazy", { multiplex: multiplex, query: query }) { super }
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def execute_field(field:, query:, ast_node:, arguments:, object:)
|
|
42
|
+
query.trace("execute_field", { field: field, query: query, ast_node: ast_node, arguments: arguments, object: object, owner: field.owner, path: query.context[:current_path] }) { super }
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def execute_field_lazy(field:, query:, ast_node:, arguments:, object:)
|
|
46
|
+
query.trace("execute_field_lazy", { field: field, query: query, ast_node: ast_node, arguments: arguments, object: object, owner: field.owner, path: query.context[:current_path] }) { super }
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def authorized(query:, type:, object:)
|
|
50
|
+
query.trace("authorized", { context: query.context, type: type, object: object, path: query.context[:current_path] }) { super }
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def authorized_lazy(query:, type:, object:)
|
|
54
|
+
query.trace("authorized_lazy", { context: query.context, type: type, object: object, path: query.context[:current_path] }) { super }
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def resolve_type(query:, type:, object:)
|
|
58
|
+
query.trace("resolve_type", { context: query.context, type: type, object: object, path: query.context[:current_path] }) { super }
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def resolve_type_lazy(query:, type:, object:)
|
|
62
|
+
query.trace("resolve_type_lazy", { context: query.context, type: type, object: object, path: query.context[:current_path] }) { super }
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
@@ -1,183 +1,71 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
|
+
require "graphql/tracing/monitor_trace"
|
|
2
3
|
|
|
3
4
|
module GraphQL
|
|
4
5
|
module Tracing
|
|
6
|
+
# A tracer for reporting to DataDog
|
|
7
|
+
# @example Adding this tracer to your schema
|
|
8
|
+
# class MySchema < GraphQL::Schema
|
|
9
|
+
# trace_with GraphQL::Tracing::DataDogTrace
|
|
10
|
+
# end
|
|
11
|
+
# @example Skipping `resolve_type` and `authorized` events
|
|
12
|
+
# trace_with GraphQL::Tracing::DataDogTrace, trace_authorized: false, trace_resolve_type: false
|
|
13
|
+
DataDogTrace = MonitorTrace.create_module("datadog")
|
|
5
14
|
module DataDogTrace
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
15
|
+
class DatadogMonitor < MonitorTrace::Monitor
|
|
16
|
+
def initialize(set_transaction_name:, service: nil, tracer: nil, **_rest)
|
|
17
|
+
super
|
|
18
|
+
if tracer.nil?
|
|
19
|
+
tracer = defined?(Datadog::Tracing) ? Datadog::Tracing : Datadog.tracer
|
|
20
|
+
end
|
|
21
|
+
@tracer = tracer
|
|
22
|
+
@service_name = service
|
|
23
|
+
@has_prepare_span = @trace.respond_to?(:prepare_span)
|
|
12
24
|
end
|
|
13
|
-
@tracer = tracer
|
|
14
|
-
|
|
15
|
-
@analytics_enabled = analytics_enabled
|
|
16
|
-
@analytics_sample_rate = analytics_sample_rate
|
|
17
|
-
|
|
18
|
-
@service_name = service
|
|
19
|
-
@has_prepare_span = respond_to?(:prepare_span)
|
|
20
|
-
super
|
|
21
|
-
end
|
|
22
|
-
|
|
23
|
-
{
|
|
24
|
-
'lex' => 'lex.graphql',
|
|
25
|
-
'parse' => 'parse.graphql',
|
|
26
|
-
'validate' => 'validate.graphql',
|
|
27
|
-
'analyze_query' => 'analyze.graphql',
|
|
28
|
-
'analyze_multiplex' => 'analyze.graphql',
|
|
29
|
-
'execute_multiplex' => 'execute.graphql',
|
|
30
|
-
'execute_query' => 'execute.graphql',
|
|
31
|
-
'execute_query_lazy' => 'execute.graphql',
|
|
32
|
-
}.each do |trace_method, trace_key|
|
|
33
|
-
module_eval <<-RUBY, __FILE__, __LINE__
|
|
34
|
-
def #{trace_method}(**data)
|
|
35
|
-
@tracer.trace("#{trace_key}", service: @service_name, type: 'custom') do |span|
|
|
36
|
-
span.set_tag('component', 'graphql')
|
|
37
|
-
span.set_tag('operation', '#{trace_method}')
|
|
38
|
-
|
|
39
|
-
#{
|
|
40
|
-
if trace_method == 'execute_multiplex'
|
|
41
|
-
<<-RUBY
|
|
42
|
-
operations = data[:multiplex].queries.map(&:selected_operation_name).join(', ')
|
|
43
25
|
|
|
44
|
-
|
|
45
|
-
first_query = data[:multiplex].queries.first
|
|
46
|
-
fallback_transaction_name(first_query && first_query.context)
|
|
47
|
-
else
|
|
48
|
-
operations
|
|
49
|
-
end
|
|
50
|
-
span.resource = resource if resource
|
|
26
|
+
attr_reader :tracer, :service_name
|
|
51
27
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
28
|
+
def instrument(keyword, object)
|
|
29
|
+
trace_key = name_for(keyword, object)
|
|
30
|
+
@tracer.trace(trace_key, service: @service_name, type: 'custom') do |span|
|
|
31
|
+
span.set_tag('component', 'graphql')
|
|
32
|
+
op_name = keyword.respond_to?(:name) ? keyword.name : keyword.to_s
|
|
33
|
+
span.set_tag('operation', op_name)
|
|
34
|
+
|
|
35
|
+
if keyword == :execute
|
|
36
|
+
operations = object.queries.map(&:selected_operation_name).join(', ')
|
|
37
|
+
first_query = object.queries.first
|
|
38
|
+
resource = if operations.empty?
|
|
39
|
+
fallback_transaction_name(first_query && first_query.context)
|
|
40
|
+
else
|
|
41
|
+
operations
|
|
65
42
|
end
|
|
66
|
-
|
|
67
|
-
end
|
|
68
|
-
end
|
|
69
|
-
RUBY
|
|
70
|
-
end
|
|
43
|
+
span.resource = resource if resource
|
|
71
44
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
else
|
|
77
|
-
true
|
|
78
|
-
end
|
|
79
|
-
platform_key = if trace_field
|
|
80
|
-
@platform_key_cache[DataDogTrace].platform_field_key_cache[field]
|
|
81
|
-
else
|
|
82
|
-
nil
|
|
83
|
-
end
|
|
84
|
-
if platform_key && trace_field
|
|
85
|
-
@tracer.trace(platform_key, service: @service_name, type: 'custom') do |span|
|
|
86
|
-
span.set_tag('component', 'graphql')
|
|
87
|
-
span.set_tag('operation', span_key)
|
|
45
|
+
span.set_tag("selected_operation_name", first_query.selected_operation_name)
|
|
46
|
+
span.set_tag("selected_operation_type", first_query.selected_operation&.operation_type)
|
|
47
|
+
span.set_tag("query_string", first_query.query_string)
|
|
48
|
+
end
|
|
88
49
|
|
|
89
50
|
if @has_prepare_span
|
|
90
|
-
|
|
91
|
-
prepare_span(span_key, prepare_span_data, span)
|
|
51
|
+
@trace.prepare_span(keyword, object, span)
|
|
92
52
|
end
|
|
93
53
|
yield
|
|
94
54
|
end
|
|
95
|
-
else
|
|
96
|
-
yield
|
|
97
|
-
end
|
|
98
|
-
end
|
|
99
|
-
def execute_field(query:, field:, ast_node:, arguments:, object:)
|
|
100
|
-
execute_field_span("execute_field", query, field, ast_node, arguments, object) do
|
|
101
|
-
super(query: query, field: field, ast_node: ast_node, arguments: arguments, object: object)
|
|
102
|
-
end
|
|
103
|
-
end
|
|
104
|
-
|
|
105
|
-
def execute_field_lazy(query:, field:, ast_node:, arguments:, object:)
|
|
106
|
-
execute_field_span("execute_field_lazy", query, field, ast_node, arguments, object) do
|
|
107
|
-
super(query: query, field: field, ast_node: ast_node, arguments: arguments, object: object)
|
|
108
|
-
end
|
|
109
|
-
end
|
|
110
|
-
|
|
111
|
-
def authorized(query:, type:, object:)
|
|
112
|
-
authorized_span("authorized", object, type, query) do
|
|
113
|
-
super(query: query, type: type, object: object)
|
|
114
55
|
end
|
|
115
|
-
end
|
|
116
|
-
|
|
117
|
-
def authorized_span(span_key, object, type, query)
|
|
118
|
-
platform_key = @platform_key_cache[DataDogTrace].platform_authorized_key_cache[type]
|
|
119
|
-
@tracer.trace(platform_key, service: @service_name, type: 'custom') do |span|
|
|
120
|
-
span.set_tag('component', 'graphql')
|
|
121
|
-
span.set_tag('operation', span_key)
|
|
122
56
|
|
|
123
|
-
|
|
124
|
-
|
|
57
|
+
include MonitorTrace::Monitor::GraphQLSuffixNames
|
|
58
|
+
class Event < MonitorTrace::Monitor::Event
|
|
59
|
+
def start
|
|
60
|
+
name = @monitor.name_for(keyword, object)
|
|
61
|
+
@dd_span = @monitor.tracer.trace(name, service: @monitor.service_name, type: 'custom')
|
|
125
62
|
end
|
|
126
|
-
yield
|
|
127
|
-
end
|
|
128
|
-
end
|
|
129
|
-
|
|
130
|
-
def authorized_lazy(object:, type:, query:)
|
|
131
|
-
authorized_span("authorized_lazy", object, type, query) do
|
|
132
|
-
super(query: query, type: type, object: object)
|
|
133
|
-
end
|
|
134
|
-
end
|
|
135
|
-
|
|
136
|
-
def resolve_type(object:, type:, query:)
|
|
137
|
-
resolve_type_span("resolve_type", object, type, query) do
|
|
138
|
-
super(object: object, query: query, type: type)
|
|
139
|
-
end
|
|
140
|
-
end
|
|
141
|
-
|
|
142
|
-
def resolve_type_lazy(object:, type:, query:)
|
|
143
|
-
resolve_type_span("resolve_type_lazy", object, type, query) do
|
|
144
|
-
super(object: object, query: query, type: type)
|
|
145
|
-
end
|
|
146
|
-
end
|
|
147
|
-
|
|
148
|
-
def resolve_type_span(span_key, object, type, query)
|
|
149
|
-
platform_key = @platform_key_cache[DataDogTrace].platform_resolve_type_key_cache[type]
|
|
150
|
-
@tracer.trace(platform_key, service: @service_name, type: 'custom') do |span|
|
|
151
|
-
span.set_tag('component', 'graphql')
|
|
152
|
-
span.set_tag('operation', span_key)
|
|
153
63
|
|
|
154
|
-
|
|
155
|
-
|
|
64
|
+
def finish
|
|
65
|
+
@dd_span.finish
|
|
156
66
|
end
|
|
157
|
-
yield
|
|
158
67
|
end
|
|
159
68
|
end
|
|
160
|
-
|
|
161
|
-
include PlatformTrace
|
|
162
|
-
|
|
163
|
-
# Implement this method in a subclass to apply custom tags to datadog spans
|
|
164
|
-
# @param key [String] The event being traced
|
|
165
|
-
# @param data [Hash] The runtime data for this event (@see GraphQL::Tracing for keys for each event)
|
|
166
|
-
# @param span [Datadog::Tracing::SpanOperation] The datadog span for this event
|
|
167
|
-
# def prepare_span(key, data, span)
|
|
168
|
-
# end
|
|
169
|
-
|
|
170
|
-
def platform_field_key(field)
|
|
171
|
-
field.path
|
|
172
|
-
end
|
|
173
|
-
|
|
174
|
-
def platform_authorized_key(type)
|
|
175
|
-
"#{type.graphql_name}.authorized"
|
|
176
|
-
end
|
|
177
|
-
|
|
178
|
-
def platform_resolve_type_key(type)
|
|
179
|
-
"#{type.graphql_name}.resolve_type"
|
|
180
|
-
end
|
|
181
69
|
end
|
|
182
70
|
end
|
|
183
71
|
end
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module GraphQL
|
|
4
|
+
module Tracing
|
|
5
|
+
class DetailedTrace
|
|
6
|
+
class ActiveRecordBackend
|
|
7
|
+
class GraphqlDetailedTrace < ActiveRecord::Base
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def initialize(limit: nil, model_class: nil)
|
|
11
|
+
@limit = limit
|
|
12
|
+
@model_class = model_class || GraphqlDetailedTrace
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def traces(last:, before:)
|
|
16
|
+
gdts = @model_class.all.order("begin_ms DESC")
|
|
17
|
+
if before
|
|
18
|
+
gdts = gdts.where("begin_ms < ?", before)
|
|
19
|
+
end
|
|
20
|
+
if last
|
|
21
|
+
gdts = gdts.limit(last)
|
|
22
|
+
end
|
|
23
|
+
gdts.map { |gdt| record_to_stored_trace(gdt) }
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def delete_trace(id)
|
|
27
|
+
@model_class.where(id: id).destroy_all
|
|
28
|
+
nil
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def delete_all_traces
|
|
32
|
+
@model_class.all.destroy_all
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def find_trace(id)
|
|
36
|
+
gdt = @model_class.find_by(id: id)
|
|
37
|
+
if gdt
|
|
38
|
+
record_to_stored_trace(gdt)
|
|
39
|
+
else
|
|
40
|
+
nil
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def save_trace(operation_name, duration_ms, begin_ms, trace_data)
|
|
45
|
+
gdt = @model_class.create!(
|
|
46
|
+
begin_ms: begin_ms,
|
|
47
|
+
operation_name: operation_name,
|
|
48
|
+
duration_ms: duration_ms,
|
|
49
|
+
trace_data: trace_data,
|
|
50
|
+
)
|
|
51
|
+
if @limit
|
|
52
|
+
@model_class
|
|
53
|
+
.where("id NOT IN(SELECT id FROM graphql_detailed_traces ORDER BY begin_ms DESC LIMIT ?)", @limit)
|
|
54
|
+
.delete_all
|
|
55
|
+
end
|
|
56
|
+
gdt.id
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
private
|
|
60
|
+
|
|
61
|
+
def record_to_stored_trace(gdt)
|
|
62
|
+
StoredTrace.new(
|
|
63
|
+
id: gdt.id,
|
|
64
|
+
begin_ms: gdt.begin_ms,
|
|
65
|
+
operation_name: gdt.operation_name,
|
|
66
|
+
duration_ms: gdt.duration_ms,
|
|
67
|
+
trace_data: gdt.trace_data
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
end
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module GraphQL
|
|
4
|
+
module Tracing
|
|
5
|
+
class DetailedTrace
|
|
6
|
+
# An in-memory trace storage backend. Suitable for testing and development only.
|
|
7
|
+
# It won't work for multi-process deployments and everything is erased when the app is restarted.
|
|
8
|
+
class MemoryBackend
|
|
9
|
+
def initialize(limit: nil)
|
|
10
|
+
@limit = limit
|
|
11
|
+
@traces = {}
|
|
12
|
+
@next_id = 0
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def traces(last:, before:)
|
|
16
|
+
page = []
|
|
17
|
+
@traces.values.reverse_each do |trace|
|
|
18
|
+
if page.size == last
|
|
19
|
+
break
|
|
20
|
+
elsif before.nil? || trace.begin_ms < before
|
|
21
|
+
page << trace
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
page
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def find_trace(id)
|
|
28
|
+
@traces[id]
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def delete_trace(id)
|
|
32
|
+
@traces.delete(id.to_i)
|
|
33
|
+
nil
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def delete_all_traces
|
|
37
|
+
@traces.clear
|
|
38
|
+
nil
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def save_trace(operation_name, duration, begin_ms, trace_data)
|
|
42
|
+
id = @next_id
|
|
43
|
+
@next_id += 1
|
|
44
|
+
@traces[id] = DetailedTrace::StoredTrace.new(
|
|
45
|
+
id: id,
|
|
46
|
+
operation_name: operation_name,
|
|
47
|
+
duration_ms: duration,
|
|
48
|
+
begin_ms: begin_ms,
|
|
49
|
+
trace_data: trace_data
|
|
50
|
+
)
|
|
51
|
+
if @limit && @traces.size > @limit
|
|
52
|
+
del_keys = @traces.keys[0...-@limit]
|
|
53
|
+
del_keys.each { |k| @traces.delete(k) }
|
|
54
|
+
end
|
|
55
|
+
id
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|