Class: GraphQL::Tracing::DetailedTrace

Inherits:
Object
  • Object
show all
Defined in:
lib/graphql/tracing/detailed_trace.rb,
lib/graphql/tracing/detailed_trace/redis_backend.rb,
lib/graphql/tracing/detailed_trace/memory_backend.rb,
lib/graphql/tracing/detailed_trace/active_record_backend.rb

Overview

DetailedTrace can make detailed profiles for a subset of production traffic. Install it in Rails with rails generate graphql:detailed_trace.

When MySchema.detailed_trace?(query) returns true, a profiler-specific trace_mode: ... will be used for the query, overriding the one in context[:trace_mode].

By default, the detailed tracer calls .inspect on application objects returned from fields. You can customize this behavior by extending DetailedTrace and overriding #inspect_object. You can opt out of debug annotations entirely with use ..., debug: false or for a single query with context: { detailed_trace_debug: false }.

You can store saved traces in two ways:

  • ActiveRecord: With rails generate graphql:detailed_trace, a new migration will be added to your app. That table will be used to store trace data.

  • Redis: Pass redis: ... to save trace data to a Redis database. Depending on your needs, you can configure this database to retain all data (persistent) or to expire data according to your rules.

If you need to save traces indefinitely, you can download them from Perfetto after opening them there.

Examples:

Installing with Rails

rails generate graphql:detailed_trace # optional: --redis

Adding the sampler to your schema

class MySchema < GraphQL::Schema
  # Add the sampler:
  use GraphQL::Tracing::DetailedTrace, redis: Redis.new(...), limit: 100

  # And implement this hook to tell it when to take a sample:
  def self.detailed_trace?(query)
    # Could use `query.context`, `query.selected_operation_name`, `query.query_string` here
    # Could call out to Flipper, etc
    rand <= 0.000_1 # one in ten thousand
  end
end

Customizing debug output in traces

class CustomDetailedTrace < GraphQL::Tracing::DetailedTrace
  def inspect_object(object)
    if object.is_a?(SomeThing)
      # handle it specially ...
    else
      super
     end
  end
end

disabling debug annotations completely

use DetailedTrace, debug: false, ...

disabling debug annotations for one query

MySchema.execute(query_str, context: { detailed_trace_debug: false })

See Also:

Defined Under Namespace

Classes: ActiveRecordBackend, MemoryBackend, RedisBackend, StoredTrace

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(storage:, trace_mode:, debug:) ⇒ DetailedTrace

Returns a new instance of DetailedTrace.



84
85
86
87
88
# File 'lib/graphql/tracing/detailed_trace.rb', line 84

def initialize(storage:, trace_mode:, debug:)
  @storage = storage
  @trace_mode = trace_mode
  @debug = debug
end

Instance Attribute Details

#trace_modeSymbol (readonly)

Returns The trace mode to use when Schema.detailed_trace? returns true.

Returns:



91
92
93
# File 'lib/graphql/tracing/detailed_trace.rb', line 91

def trace_mode
  @trace_mode
end

Class Method Details

.debug?true

Default debug setting

Returns:

  • (true)


139
140
141
# File 'lib/graphql/tracing/detailed_trace.rb', line 139

def self.debug?
  true
end

.inspect_object(object) ⇒ Object



129
130
131
132
133
134
135
# File 'lib/graphql/tracing/detailed_trace.rb', line 129

def self.inspect_object(object)
  if defined?(ActiveRecord::Relation) && object.is_a?(ActiveRecord::Relation)
    "#{object.class}, .to_sql=#{object.to_sql.inspect}"
  else
    object.inspect
  end
end

.use(schema, trace_mode: :profile_sample, memory: false, debug: debug?, , redis: nil, limit: nil, model_class: nil) ⇒ Object

Parameters:

  • redis (Redis) (defaults to: nil)

    If provided, profiles will be stored in Redis for later review

  • limit (Integer) (defaults to: nil)

    A maximum number of profiles to store

  • debug (Boolean) (defaults to: debug?, )

    if false, it won’t create debug annotations in Perfetto traces (reduces overhead)

  • model_class (Class<ActiveRecord::Base>) (defaults to: nil)


69
70
71
72
73
74
75
76
77
78
79
80
81
82
# File 'lib/graphql/tracing/detailed_trace.rb', line 69

def self.use(schema, trace_mode: :profile_sample, memory: false, debug: debug?, redis: nil, limit: nil, model_class: nil)
  storage = if redis
    RedisBackend.new(redis: redis, limit: limit)
  elsif memory
    MemoryBackend.new(limit: limit)
  elsif defined?(ActiveRecord)
    ActiveRecordBackend.new(limit: limit, model_class: model_class)
  else
    raise ArgumentError, "To store traces, install ActiveRecord or provide `redis: ...`"
  end
  detailed_trace = self.new(storage: storage, trace_mode: trace_mode, debug: debug)
  schema.detailed_trace = detailed_trace
  schema.trace_with(PerfettoTrace, mode: trace_mode, save_profile: true)
end

Instance Method Details

#debug?Boolean

Returns:

  • (Boolean)


99
100
101
# File 'lib/graphql/tracing/detailed_trace.rb', line 99

def debug?
  @debug
end

#delete_all_tracesvoid

This method returns an undefined value.



121
122
123
# File 'lib/graphql/tracing/detailed_trace.rb', line 121

def delete_all_traces
  @storage.delete_all_traces
end

#delete_trace(id) ⇒ void

This method returns an undefined value.



116
117
118
# File 'lib/graphql/tracing/detailed_trace.rb', line 116

def delete_trace(id)
  @storage.delete_trace(id)
end

#find_trace(id) ⇒ StoredTrace?

Returns:



111
112
113
# File 'lib/graphql/tracing/detailed_trace.rb', line 111

def find_trace(id)
  @storage.find_trace(id)
end

#inspect_object(object) ⇒ Object



125
126
127
# File 'lib/graphql/tracing/detailed_trace.rb', line 125

def inspect_object(object)
  self.class.inspect_object(object)
end

#save_trace(operation_name, duration_ms, begin_ms, trace_data) ⇒ String

Returns ID of saved trace.

Returns:

  • (String)

    ID of saved trace



94
95
96
# File 'lib/graphql/tracing/detailed_trace.rb', line 94

def save_trace(operation_name, duration_ms, begin_ms, trace_data)
  @storage.save_trace(operation_name, duration_ms, begin_ms, trace_data)
end

#traces(last: nil, before: nil) ⇒ Enumerable<StoredTrace>

Parameters:

  • last (Integer) (defaults to: nil)
  • before (Integer) (defaults to: nil)

    Timestamp in milliseconds since epoch

Returns:



106
107
108
# File 'lib/graphql/tracing/detailed_trace.rb', line 106

def traces(last: nil, before: nil)
  @storage.traces(last: last, before: before)
end