Class: OpticsAgent::Reporting::Report

Inherits:
Object
  • Object
show all
Includes:
Apollo::Optics::Proto, Normalization, OpticsAgent::Reporting
Defined in:
lib/optics-agent/reporting/report.rb

Overview

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

Constant Summary

Constants included from Apollo::Optics::Proto

Apollo::Optics::Proto::Error, Apollo::Optics::Proto::Field, Apollo::Optics::Proto::FieldStat, Apollo::Optics::Proto::Id128, Apollo::Optics::Proto::ReportHeader, Apollo::Optics::Proto::SchemaReport, Apollo::Optics::Proto::StatsPerClientName, Apollo::Optics::Proto::StatsPerSignature, Apollo::Optics::Proto::StatsReport, Apollo::Optics::Proto::Timestamp, Apollo::Optics::Proto::Trace, Apollo::Optics::Proto::TracesReport, Apollo::Optics::Proto::Type, Apollo::Optics::Proto::TypeStat

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from Normalization

#empty_latency_count, #latency_bucket

Methods included from OpticsAgent::Reporting

#add_latency, #client_info, #duration_micros, #duration_nanos, #generate_report_header, #generate_timestamp, #latency_bucket_for_duration

Constructor Details

#initialize(report_traces: true) ⇒ Report

Returns a new instance of Report.



18
19
20
21
22
23
24
25
26
27
28
29
30
31
# 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

Instance Attribute Details

#reportObject (readonly)

Returns the value of attribute report.



15
16
17
# File 'lib/optics-agent/reporting/report.rb', line 15

def report
  @report
end

#traces_to_reportObject (readonly)

Returns the value of attribute traces_to_report.



16
17
18
# File 'lib/optics-agent/reporting/report.rb', line 16

def traces_to_report
  @traces_to_report
end

Instance Method Details

#add_query(query, rack_env = nil) ⇒ Object



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
81
# 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) ⇒ Object

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



84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
# 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_fieldObject

do something once per field we’ve collected



107
108
109
110
111
112
113
114
115
# 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!Object



33
34
35
36
# 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) ⇒ Object



38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
# 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