Module: ScoutApm::Agent::Reporting

Included in:
ScoutApm::Agent
Defined in:
lib/scout_apm/agent/reporting.rb

Constant Summary collapse

MAX_AGE_TO_REPORT =

ten minutes as seconds

(10 * 60)

Instance Method Summary collapse

Instance Method Details

#add_metric_ids(metrics) ⇒ Object

Before reporting, lookup metric_id for each MetricMeta. This speeds up reporting on the server-side.



98
99
100
101
102
103
104
# File 'lib/scout_apm/agent/reporting.rb', line 98

def add_metric_ids(metrics)
  metrics.each do |meta,stats|
    if metric_id = metric_lookup[meta]
      meta.metric_id = metric_id
    end
  end
end

#deliver_period(reporting_period) ⇒ Object



38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
# File 'lib/scout_apm/agent/reporting.rb', line 38

def deliver_period(reporting_period)
  metrics = reporting_period.metrics_payload
  slow_transactions = reporting_period.slow_transactions_payload
  jobs = reporting_period.jobs
  slow_jobs = reporting_period.slow_jobs_payload

   = {
    :app_root      => ScoutApm::Environment.instance.root.to_s,
    :unique_id     => ScoutApm::Utils::UniqueId.simple,
    :agent_version => ScoutApm::VERSION,
    :agent_time    => reporting_period.timestamp.to_s,
    :agent_pid     => Process.pid,
    :platform      => "ruby",
  }

  log_deliver(metrics, slow_transactions, , slow_jobs)

  payload = ScoutApm::Serializers::PayloadSerializer.serialize(, metrics, slow_transactions, jobs, slow_jobs)
  logger.debug("Payload: #{payload}")

  reporter.report(payload, headers)
rescue => e
  logger.warn "Error on checkin"
  logger.info e.message
  logger.debug e.backtrace
end

#headersObject

TODO: Move this into PayloadSerializer?



88
89
90
91
92
93
94
# File 'lib/scout_apm/agent/reporting.rb', line 88

def headers
  if ScoutApm::Agent.instance.config.value("report_format") == 'json'
    headers = {'Content-Type' => 'application/json'}
  else
    headers = {}
  end
end

#log_deliver(metrics, slow_transactions, metadata, jobs_traces) ⇒ Object



65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
# File 'lib/scout_apm/agent/reporting.rb', line 65

def log_deliver(metrics, slow_transactions, , jobs_traces)
  total_request_count = metrics.
    select { |meta,stats| meta.metric_name =~ /\AController/ }.
    inject(0) {|sum, (_, stat)| sum + stat.call_count }

  memory = metrics.
    find {|meta,stats| meta.metric_name =~ /\AMemory/ }
  process_log_str = if memory
                      "Recorded from #{memory.last.call_count} processes"
                    else
                      "Recorded across (unknown) processes"
                    end

  time_clause       = "[#{Time.parse([:agent_time]).strftime("%H:%M")}]"
  metrics_clause    = "#{metrics.length} Metrics for #{total_request_count} requests"
  slow_trans_clause = "#{slow_transactions.length} Slow Transaction Traces"
  job_clause        = "#{jobs_traces.length} Job Traces"

  logger.info "#{time_clause} Delivering #{metrics_clause} and #{slow_trans_clause} and #{job_clause}, #{process_log_str}."
  logger.debug("Metrics: #{metrics.pretty_inspect}\nSlowTrans: #{slow_transactions.pretty_inspect}\nMetadata: #{.inspect.pretty_inspect}")
end

#process_metricsObject

The data moves through a treadmill of reporting, coordinating several Rails processes by using an external file.

  • During the minute it is being recorded by the instruments, it gets recorded into the ram of each process (in the Store class).

  • The minute after, each process writes its own metrics to a shared LayawayFile

  • The minute after that, the first process to wake up pushes the combined data to the server, and wipes it. Next processes don’t have anything to do.

At any given point, there is data in each of those steps, moving its way through the process



17
18
19
20
21
22
23
24
25
# File 'lib/scout_apm/agent/reporting.rb', line 17

def process_metrics
  # Write the previous minute's data to the shared-across-process layaway file.
  store.write_to_layaway(layaway)

  # Attempt to send 2 minutes ago's data up to the server.  This
  # only acctually occurs if this process is the first to wake up this
  # minute.
  report_to_server
end

#report_to_serverObject

In a running app, one process will get one period ready for delivery, the others will see 0.



30
31
32
33
34
35
36
# File 'lib/scout_apm/agent/reporting.rb', line 30

def report_to_server
  reporting_periods = layaway.periods_ready_for_delivery
  reporting_periods.reject! {|rp| rp.timestamp.age_in_seconds > MAX_AGE_TO_REPORT }
  reporting_periods.each do |rp|
    deliver_period(rp)
  end
end

#reporterObject



5
6
7
# File 'lib/scout_apm/agent/reporting.rb', line 5

def reporter
  @reporter ||= ScoutApm::Reporter.new(:checkin, config, logger)
end