class OpticsAgent::Reporting::Report

This class represents a complete report that we send to the optics server It pretty closely wraps the StatsReport protobuf message with a few convenience methods

Attributes

report[R]
traces_to_report[R]

Public Class Methods

new(report_traces: true) click to toggle source
# File lib/optics-agent/reporting/report.rb, line 18
def initialize(report_traces: true)
  # internal report that we encapsulate
  @report = StatsReport.new({
    header: ReportHeader.new({
      agent_version: '1'
    }),
    start_time: generate_timestamp(Time.now)
  })

  @interval = Hitimes::Interval.now

  @report_traces = report_traces
  @traces_to_report = []
end

Public Instance Methods

add_query(query, rack_env = nil) click to toggle source
# File lib/optics-agent/reporting/report.rb, line 55
def add_query(query, rack_env = nil)
  @report.per_signature[query.signature] ||= StatsPerSignature.new
  signature_stats = @report.per_signature[query.signature]

  info = client_info(rack_env)
  signature_stats.per_client_name[info[:client_name]] ||= StatsPerClientName.new({
    latency_count: empty_latency_count,
    error_count: empty_latency_count
  })
  client_stats = signature_stats.per_client_name[info[:client_name]]

  # XXX: handle errors
  add_latency(client_stats.latency_count, query.duration)
  client_stats.count_per_version[info[:client_version]] ||= 0
  client_stats.count_per_version[info[:client_version]] += 1

  query.add_to_stats(signature_stats)

  if @report_traces
    # Is this the first query we've seen in this reporting period and
    # latency bucket? In which case we want to send a trace
    bucket = latency_bucket_for_duration(query.duration)
    if (client_stats.latency_count[bucket] == 1)
      @traces_to_report << QueryTrace.new(query, rack_env)
    end
  end
end
decorate_from_schema(schema) click to toggle source

take a graphql schema and add returnTypes to all the fields on our report

# File lib/optics-agent/reporting/report.rb, line 84
def decorate_from_schema(schema)
  each_field do |type_stat, field_stat|
    # short circuit for special fields
    field_stat.returnType = type_stat.name if field_stat.name == '__typename'

    if type_stat.name == 'Query'
      field_stat.returnType = '__Type' if field_stat.name == '__type'
      field_stat.returnType = '__Schema' if field_stat.name == '__schema'
    end

    if field_stat.returnType.empty?
      type = schema.types[type_stat.name]
      throw "Type #{type_stat.name} not found!" unless type

      field = type.get_field(field_stat.name)
      throw "Field #{type_stat.name}.#{field_stat.name} not found!" unless field

      field_stat.returnType = field.type.to_s
    end
  end
end
each_field() { |type, field| ... } click to toggle source

do something once per field we've collected

# File lib/optics-agent/reporting/report.rb, line 107
def each_field
  @report.per_signature.values.each do |sps|
    sps.per_type.each do |type|
      type.field.each do |field|
        yield type, field
      end
    end
  end
end
finish!() click to toggle source
# File lib/optics-agent/reporting/report.rb, line 33
def finish!
  @report.end_time ||= generate_timestamp(Time.now)
  @report.realtime_duration || duration_nanos(@interval.stop)
end
send_with(agent) click to toggle source
# File lib/optics-agent/reporting/report.rb, line 38
def send_with(agent)
  agent.debug do
    n_queries = 0
    @report.per_signature.values.each do |signature_stats|
      signature_stats.per_client_name.values.each do |client_stats|
        n_queries += client_stats.count_per_version.values.reduce(&:+)
      end
    end
    "Sending #{n_queries} queries and #{@traces_to_report.length} traces"
  end
  self.finish!
  @traces_to_report.each do |trace|
    trace.send_with(agent)
  end
  agent.send_message('/api/ss/stats', @report)
end