class ScoutApm::Instruments::Process::ProcessCpu

Attributes

context[R]
last_run[RW]
last_stime[RW]
last_utime[RW]
num_processors[R]

Public Class Methods

new(context) click to toggle source
# File lib/scout_apm/instruments/process/process_cpu.rb, line 9
def initialize(context)
  @context = context

  @num_processors = [context.environment.processors, 1].compact.max

  t = ::Process.times
  @last_run = Time.now
  @last_utime = t.utime
  @last_stime = t.stime
end

Public Instance Methods

human_name() click to toggle source
# File lib/scout_apm/instruments/process/process_cpu.rb, line 28
def human_name
  "Process CPU"
end
logger() click to toggle source
# File lib/scout_apm/instruments/process/process_cpu.rb, line 102
def logger
  context.logger
end
metric_name() click to toggle source
# File lib/scout_apm/instruments/process/process_cpu.rb, line 24
def metric_name
  "Utilization"
end
metric_type() click to toggle source
# File lib/scout_apm/instruments/process/process_cpu.rb, line 20
def metric_type
  "CPU"
end
metrics(timestamp, store) click to toggle source
# File lib/scout_apm/instruments/process/process_cpu.rb, line 32
def metrics(timestamp, store)
  result = run
  if result
    meta = MetricMeta.new("#{metric_type}/#{metric_name}")
    stat = MetricStats.new(false)
    stat.update!(result)
    store.track!({ meta => stat }, :timestamp => timestamp)
  else
    {}
  end

end
run() click to toggle source

TODO: Figure out a good default instead of nil

# File lib/scout_apm/instruments/process/process_cpu.rb, line 46
def run
  res = nil

  t = ::Process.times
  now = Time.now
  utime = t.utime
  stime = t.stime

  wall_clock_elapsed  = now - last_run
  if wall_clock_elapsed < 0
    save_times(now, utime, stime)
    logger.info "#{human_name}: Negative time elapsed.  now: #{now}, last_run: #{last_run}, total time: #{wall_clock_elapsed}."
    return nil
  end

  utime_elapsed   = utime - last_utime
  stime_elapsed   = stime - last_stime
  process_elapsed = utime_elapsed + stime_elapsed

  # This can happen right after a fork.  This class starts up in
  # pre-fork, records {u,s}time, then forks. This resets {u,s}time to 0
  if process_elapsed < 0
    save_times(now, utime, stime)
    logger.debug "#{human_name}: Negative process time elapsed.  utime: #{utime_elapsed}, stime: #{stime_elapsed}, total time: #{process_elapsed}. This is normal to see when starting a forking web server."
    return nil
  end

  # Normalized to # of processors
  normalized_wall_clock_elapsed = wall_clock_elapsed * num_processors

  # If somehow we run for 0 seconds between calls, don't try to divide by 0
  res = if normalized_wall_clock_elapsed == 0
          0
        else
          ( process_elapsed / normalized_wall_clock_elapsed )*100
        end

  if res < 0
    save_times(now, utime, stime)
    logger.info "#{human_name}: Negative CPU.  #{process_elapsed} / #{normalized_wall_clock_elapsed} * 100 ==> #{res}"
    return nil
  end

  save_times(now, utime, stime)

  logger.debug "#{human_name}: #{res.inspect} [#{num_processors} CPU(s)]"

  return res
end
save_times(now, utime, stime) click to toggle source
# File lib/scout_apm/instruments/process/process_cpu.rb, line 96
def save_times(now, utime, stime)
  self.last_run = now
  self.last_utime = utime
  self.last_stime = stime
end