Class: ApolloStudioTracing::Tracer
- Inherits:
-
Object
- Object
- ApolloStudioTracing::Tracer
- Defined in:
- lib/apollo-studio-tracing/tracer.rb
Overview
rubocop:disable Metrics/ClassLength
Constant Summary collapse
- EXECUTE_MULTIPLEX =
store string constants to avoid creating new strings for each call to .trace
'execute_multiplex'
- EXECUTE_QUERY =
'execute_query'
- EXECUTE_QUERY_LAZY =
'execute_query_lazy'
- EXECUTE_FIELD =
'execute_field'
- EXECUTE_FIELD_LAZY =
'execute_field_lazy'
Instance Attribute Summary collapse
-
#query_signature ⇒ Object
readonly
Returns the value of attribute query_signature.
-
#trace_prepare ⇒ Object
readonly
Returns the value of attribute trace_prepare.
Instance Method Summary collapse
-
#execute_field(data, &block) ⇒ Object
Step 2: * Record start and end times for the field resolver.
-
#execute_field_lazy(data, &block) ⇒ Object
Optional Step 3: Overwrite the end times on the trace node if the resolver was lazy.
- #execute_multiplex(data, &block) ⇒ Object
-
#execute_query_lazy(data, &block) ⇒ Object
Step 4: Record end times and merge them into the trace hash.
- #flush_trace_channel ⇒ Object
-
#initialize(schema_tag: nil, executable_schema_id: nil, service_version: nil, trace_prepare: nil, query_signature: nil, api_key: nil, **trace_channel_options) ⇒ Tracer
constructor
A new instance of Tracer.
- #shutdown_trace_channel ⇒ Object
- #start_trace(query) ⇒ Object
- #start_trace_channel ⇒ Object
- #trace(key, data, &block) ⇒ Object
- #tracing_enabled?(context) ⇒ Boolean
Constructor Details
#initialize(schema_tag: nil, executable_schema_id: nil, service_version: nil, trace_prepare: nil, query_signature: nil, api_key: nil, **trace_channel_options) ⇒ Tracer
Returns a new instance of Tracer.
49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 |
# File 'lib/apollo-studio-tracing/tracer.rb', line 49 def initialize( schema_tag: nil, executable_schema_id: nil, service_version: nil, trace_prepare: nil, query_signature: nil, api_key: nil, ** ) @trace_prepare = trace_prepare || proc {} @query_signature = query_signature || proc do |query| # TODO: This should be smarter # TODO (lsanwick) Replace with reference implementation from # https://github.com/apollographql/apollo-tooling/blob/master/packages/apollo-graphql/src/operationId.ts query.query_string end report_header = ApolloStudioTracing::ReportHeader.new( hostname: hostname, agent_version: agent_version, service_version: service_version, runtime_version: RUBY_DESCRIPTION, uname: uname, schema_tag: schema_tag || ENV.fetch('ENGINE_SCHEMA_TAG', 'current'), executable_schema_id: executable_schema_id, ) @trace_channel = ApolloStudioTracing::TraceChannel.new( report_header: report_header, api_key: api_key, **, ) end |
Instance Attribute Details
#query_signature ⇒ Object (readonly)
Returns the value of attribute query_signature.
47 48 49 |
# File 'lib/apollo-studio-tracing/tracer.rb', line 47 def query_signature @query_signature end |
#trace_prepare ⇒ Object (readonly)
Returns the value of attribute trace_prepare.
47 48 49 |
# File 'lib/apollo-studio-tracing/tracer.rb', line 47 def trace_prepare @trace_prepare end |
Instance Method Details
#execute_field(data, &block) ⇒ Object
Step 2:
-
Record start and end times for the field resolver.
-
Rescue errors so the method doesn’t exit early.
-
Create a trace “node” and attach field details.
-
Propagate the error (if necessary) so it ends up in the top-level errors array.
The values in ‘data` are different depending on the executor runtime. graphql-ruby.org/api-doc/1.9.3/GraphQL/Tracing
Nodes are added the NodeMap stored in the trace hash.
Errors are added to nodes in ‘ApolloStudioTracing::Tracing.attach_trace_to_result` because we don’t have the error ‘location` here. rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 |
# File 'lib/apollo-studio-tracing/tracer.rb', line 149 def execute_field(data, &block) context = data.fetch(:context, nil) || data.fetch(:query).context return block.call unless tracing_enabled?(context) start_time_nanos = Process.clock_gettime(Process::CLOCK_MONOTONIC, :nanosecond) begin result = block.call rescue StandardError => e error = e end end_time_nanos = Process.clock_gettime(Process::CLOCK_MONOTONIC, :nanosecond) # legacy runtime if data.include?(:context) path = context.path field_name = context.field.graphql_name field_type = context.field.type.to_s parent_type = context.parent_type.graphql_name else # interpreter runtime path = data.fetch(:path) field = data.fetch(:field) field_name = field.graphql_name field_type = field.type.to_type_signature parent_type = data.fetch(:owner).graphql_name end trace = context.namespace(ApolloStudioTracing::KEY) node = trace[:node_map].add(path) # original_field_name is set only for aliased fields node.original_field_name = field_name if field_name != path.last node.type = field_type node.parent_type = parent_type node.start_time = start_time_nanos - trace[:start_time_nanos] node.end_time = end_time_nanos - trace[:start_time_nanos] raise error if error result end |
#execute_field_lazy(data, &block) ⇒ Object
Optional Step 3: Overwrite the end times on the trace node if the resolver was lazy.
194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 |
# File 'lib/apollo-studio-tracing/tracer.rb', line 194 def execute_field_lazy(data, &block) context = data.fetch(:context, nil) || data.fetch(:query).context return block.call unless tracing_enabled?(context) begin result = block.call rescue StandardError => e error = e end end_time_nanos = Process.clock_gettime(Process::CLOCK_MONOTONIC, :nanosecond) # legacy runtime if data.include?(:context) path = context.path field = context.field else # interpreter runtime path = data.fetch(:path) field = data.fetch(:field) end trace = context.namespace(ApolloStudioTracing::KEY) # When a field is resolved with an array of lazy values, the interpreter fires an # `execute_field` for the resolution of the field and then a `execute_field_lazy` event for # each lazy value in the array. Since the path here will contain an index (indicating which # lazy value we're executing: e.g. ['arrayOfLazies', 0]), we won't have a node for the path. # We only care about the end of the parent field (e.g. ['arrayOfLazies']), so we get the # node for that path. What ends up happening is we update the end_time for the parent node # for each of the lazy values. The last one that's executed becomes the final end time. if field.type.list? && path.last.is_a?(Integer) path = path[0...-1] end node = trace[:node_map].node_for_path(path) node.end_time = end_time_nanos - trace[:start_time_nanos] raise error if error result end |
#execute_multiplex(data, &block) ⇒ Object
113 114 115 116 117 118 119 120 121 122 123 |
# File 'lib/apollo-studio-tracing/tracer.rb', line 113 def execute_multiplex(data, &block) # Step 1: # Create a trace hash on each query's context and record start times. data.fetch(:multiplex).queries.each { |query| start_trace(query) } results = block.call # Step 5 # Enqueue the final trace onto the TraceChannel. results.map { |result| attach_trace_to_result(result) } end |
#execute_query_lazy(data, &block) ⇒ Object
Step 4: Record end times and merge them into the trace hash
238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 |
# File 'lib/apollo-studio-tracing/tracer.rb', line 238 def execute_query_lazy(data, &block) result = block.call # Normalize to an array of queries regardless of whether we are multiplexing or performing a # single query. queries = Array(data.fetch(:multiplex)&.queries || data.fetch(:query)) queries.map do |query| next unless tracing_enabled?(query&.context) trace = query.context.namespace(ApolloStudioTracing::KEY) trace.merge!( end_time: Time.now.utc, end_time_nanos: Process.clock_gettime(Process::CLOCK_MONOTONIC, :nanosecond), ) end result end |
#flush_trace_channel ⇒ Object
90 91 92 |
# File 'lib/apollo-studio-tracing/tracer.rb', line 90 def flush_trace_channel @trace_channel.flush end |
#shutdown_trace_channel ⇒ Object
86 87 88 |
# File 'lib/apollo-studio-tracing/tracer.rb', line 86 def shutdown_trace_channel @trace_channel.shutdown end |
#start_trace(query) ⇒ Object
125 126 127 128 129 130 131 132 133 |
# File 'lib/apollo-studio-tracing/tracer.rb', line 125 def start_trace(query) return unless tracing_enabled?(query&.context) query.context.namespace(ApolloStudioTracing::KEY).merge!( start_time: Time.now.utc, start_time_nanos: Process.clock_gettime(Process::CLOCK_MONOTONIC, :nanosecond), node_map: NodeMap.new, ) end |
#start_trace_channel ⇒ Object
82 83 84 |
# File 'lib/apollo-studio-tracing/tracer.rb', line 82 def start_trace_channel @trace_channel.start end |
#trace(key, data, &block) ⇒ Object
94 95 96 97 98 99 100 101 102 103 104 105 106 107 |
# File 'lib/apollo-studio-tracing/tracer.rb', line 94 def trace(key, data, &block) case key when EXECUTE_MULTIPLEX execute_multiplex(data, &block) when EXECUTE_QUERY_LAZY execute_query_lazy(data, &block) when EXECUTE_FIELD execute_field(data, &block) when EXECUTE_FIELD_LAZY execute_field_lazy(data, &block) else yield end end |
#tracing_enabled?(context) ⇒ Boolean
109 110 111 |
# File 'lib/apollo-studio-tracing/tracer.rb', line 109 def tracing_enabled?(context) context && context[:apollo_tracing_enabled] end |