class AppsignalReport::BaseReport

Report base class, defines the general flow and helper methods used by the specific report classes.

Attributes

api_token[R]
app_id[R]
app_name[R]
report[R]

Public Class Methods

new(api_token:, app_id:, app_name: nil) click to toggle source

@param [String] api_token API token, find it here:

<https://appsignal.com/users/edit>

@param [String] app_id Application ID, visible in the URL when your

application is opened on Appsignal.com

@param [String] app_name Application Name, used for the report title

# File lib/appsignal_report/base_report.rb, line 14
def initialize(api_token:, app_id:, app_name: nil)
  @api_token = api_token
  @app_id = app_id
  @app_name = app_name
  @report = {}
end

Public Instance Methods

generate() click to toggle source

To be defined by subclass, should set the instance var @report. @return [Hash]

# File lib/appsignal_report/base_report.rb, line 23
def generate
  raise NotImplementedError
end
title() click to toggle source

@return [String]

# File lib/appsignal_report/base_report.rb, line 28
def title
  [
    'AppSignal',
    self.class.name.split('::').last.split(/(?=[A-Z])/).join(' '),
    !app_name.nil? ? "(#{app_name})" : nil
  ].compact.join(' ')
end

Private Instance Methods

abs_diff(key) click to toggle source
# File lib/appsignal_report/base_report.rb, line 101
def abs_diff(key)
  report[:after][key] - report[:before][key]
end
balance_samples(samples) click to toggle source
# File lib/appsignal_report/base_report.rb, line 121
def balance_samples(samples)
  sample_size = [samples[:before].size, samples[:after].size].min
  samples[:before] = samples[:before].last(sample_size)
  samples[:after] = samples[:after].first(sample_size)
  samples
end
base_uri() click to toggle source

@return [String]

# File lib/appsignal_report/base_report.rb, line 137
def base_uri
  "https://appsignal.com/api/#{app_id}"
end
gather_samples(samples) click to toggle source
# File lib/appsignal_report/base_report.rb, line 109
def gather_samples(samples)
  split_timestamp = report_split_time.to_time.to_i
  samples.each_with_object(before: [], after: []) do |row, hash|
    next if timestamp_in_grace_period?(row[:timestamp])
    if row[:timestamp] < split_timestamp
      hash[:before] << row
    else
      hash[:after] << row
    end
  end
end
generate_diff() click to toggle source
# File lib/appsignal_report/base_report.rb, line 90
def generate_diff
  {
    error_rate: abs_diff(:error_rate),
    error_rate_pct: pct_diff(:error_rate),
    response_time: abs_diff(:response_time),
    response_time_pct: pct_diff(:response_time),
    throughput: abs_diff(:throughput),
    throughput_pct: pct_diff(:throughput),
  }
end
generate_messages() click to toggle source
# File lib/appsignal_report/base_report.rb, line 59
def generate_messages
  {
    info: info_message,
    error_rate: metric_message(:error_rate, '%'),
    response_time: metric_message(:response_time, 'ms'),
    throughput: metric_message(:throughput,
                               " req/#{@report[:resolution][0]}"),
  }
end
get_average(data, field) click to toggle source

@param [Array] data @param [Symbol] field @return [Float]

# File lib/appsignal_report/base_report.rb, line 85
def get_average(data, field)
  values = data.map { |row| row[field] }
  values.inject(0, :+).fdiv(values.size)
end
info_message() click to toggle source
# File lib/appsignal_report/base_report.rb, line 69
def info_message; end
metric_message(field, unit = '') click to toggle source
# File lib/appsignal_report/base_report.rb, line 71
    def metric_message(field, unit = '')
      <<-txt.split.join(' ')
      The #{field.to_s.sub('_', ' ')}
      #{report[:diff][field].positive? ? 'increased' : 'decreased'}
      by #{report[:diff][field].abs.round(2)}#{unit}
      (from #{report[:before][field].round(2)}#{unit}
      to #{report[:after][field].round(2)}#{unit}, that is a change of
      #{(report[:diff][:"#{field}_pct"] * 100).round(2)}%).
      txt
    end
metrics_uri() click to toggle source

@return [URI]

# File lib/appsignal_report/base_report.rb, line 142
def metrics_uri
  raise NotImplementedError
end
pct_diff(key) click to toggle source
# File lib/appsignal_report/base_report.rb, line 105
def pct_diff(key)
  abs_diff(key).fdiv(report[:before][key])
end
perform_api_request(uri) click to toggle source

@return [Hash]

# File lib/appsignal_report/base_report.rb, line 147
def perform_api_request(uri)
  http = Net::HTTP.new(uri.host, uri.port)
  http.use_ssl = true

  response = http.request(
    Net::HTTP::Get.new(uri, 'Content-Type' => 'application/json')
  )
  if response.is_a? Net::HTTPSuccess
    JSON.parse(response.body, symbolize_names: true)
  else
    raise StandardError,
          "[API ERROR] #{response.code} - #{response.message}"
  end
end
process_metrics() click to toggle source
# File lib/appsignal_report/base_report.rb, line 38
def process_metrics
  api_response = perform_api_request(metrics_uri)
  @report[:resolution] = api_response[:resolution]
  data = balance_samples(gather_samples(api_response[:data]))
  %i(before after).each do |key|
    @report[key] = {
      data_points: data[key].size,
      error_rate: get_average(data[key], :ex_rate),
      response_time: get_average(data[key], :mean),
      throughput: get_average(data[key], :count),
    }
  end
  @report.merge!(
    data_samples_from: Time.at(data[:before].first[:timestamp]).utc,
    data_samples_to: Time.at(data[:after].last[:timestamp]).utc,
    diff: generate_diff,
  )
  @report[:messages] = generate_messages
  report
end
report_split_time() click to toggle source
# File lib/appsignal_report/base_report.rb, line 128
def report_split_time
  raise NotImplementedError
end
timestamp_in_grace_period?(_) click to toggle source
# File lib/appsignal_report/base_report.rb, line 132
def timestamp_in_grace_period?(_)
  false
end