class SidekiqPrometheus::PeriodicMetrics

Report Sidekiq::Stats to prometheus on a defined interval

Global Metrics reporting requires Sidekiq::Enterprise as it uses the leader election functionality to ensure that the global metrics are only reported by one worker.

@see github.com/mperham/sidekiq/wiki/Ent-Leader-Election @see github.com/mperham/sidekiq/blob/main/lib/sidekiq/api.rb

Constants

GC_STATS
GLOBAL_STATS
REDIS_STATS

Attributes

done[RW]

@return [Boolean] When true will stop the reporting loop.

interval[R]

@return [Integer] Interval in seconds to record metrics. Default: [SidekiqPrometheus.periodic_reporting_interval]

senate[R]
sidekiq_queue[R]
sidekiq_stats[R]

Public Class Methods

new(interval: SidekiqPrometheus.periodic_reporting_interval, sidekiq_stats: Sidekiq::Stats, sidekiq_queue: Sidekiq::Queue, senate: nil) click to toggle source

@param interval [Integer] Interval in seconds to record metrics. @param sidekiq_stats [Sidekiq::Stats] @param senate [#leader?] Sidekiq::Senate

# File lib/sidekiq_prometheus/periodic_metrics.rb, line 39
def initialize(interval: SidekiqPrometheus.periodic_reporting_interval, sidekiq_stats: Sidekiq::Stats, sidekiq_queue: Sidekiq::Queue, senate: nil)
  self.done = false
  @interval = interval

  @sidekiq_stats = sidekiq_stats
  @sidekiq_queue = sidekiq_queue
  @senate = if senate.nil?
              if Object.const_defined?('Sidekiq::Senate')
                Sidekiq::Senate
              else
                Senate
              end
            else
              senate
            end
end
reporter() click to toggle source

Instance of SidekiqPrometheus::PeriodicMetrics @return [SidekiqPrometheus:PeriodicMetrics]

# File lib/sidekiq_prometheus/periodic_metrics.rb, line 31
def self.reporter
  @reporter ||= new
end

Public Instance Methods

report_gc_metrics() click to toggle source

Record GC and RSS metrics

# File lib/sidekiq_prometheus/periodic_metrics.rb, line 71
def report_gc_metrics
  stats = GC.stat
  GC_STATS[:counters].each do |stat|
    SidekiqPrometheus["sidekiq_#{stat}"]&.increment(labels: {}, by: stats[stat])
  end
  GC_STATS[:gauges].each do |stat|
    SidekiqPrometheus["sidekiq_#{stat}"]&.set(stats[stat], labels: {})
  end

  SidekiqPrometheus[:sidekiq_rss]&.set(rss, labels: {})
end
report_global_metrics() click to toggle source

Records Sidekiq global metrics

# File lib/sidekiq_prometheus/periodic_metrics.rb, line 85
def report_global_metrics
  current_stats = sidekiq_stats.new
  GLOBAL_STATS.each do |stat|
    SidekiqPrometheus["sidekiq_#{stat}"]&.set(current_stats.send(stat), labels: {})
  end

  sidekiq_queue.all.each do |queue|
    SidekiqPrometheus[:sidekiq_enqueued]&.set(queue.size, labels: { queue: queue.name })
    SidekiqPrometheus[:sidekiq_queue_latency]&.observe(queue.latency, labels: { queue: queue.name })
  end
end
report_redis_metrics() click to toggle source

Records metrics from Redis

# File lib/sidekiq_prometheus/periodic_metrics.rb, line 99
def report_redis_metrics
  redis_info = begin
    Sidekiq.redis_info
  rescue Redis::BaseConnectionError
    nil
  end

  return if redis_info.nil?

  REDIS_STATS.each do |stat|
    SidekiqPrometheus["sidekiq_redis_#{stat}"]&.set(redis_info[stat].to_i, labels: {})
  end

  db_stats = redis_info.select { |k, _v| k.match(/^db/) }
  db_stats.each do |db, stat|
    label = { database: db }
    values = stat.scan(/\d+/)
    SidekiqPrometheus[:sidekiq_redis_keys]&.set(values[0].to_i, labels: label)
    SidekiqPrometheus[:sidekiq_redis_expires]&.set(values[1].to_i, labels: label)
  end
end
rss() click to toggle source

Fetch rss from proc filesystem @see github.com/discourse/prometheus_exporter/blob/v0.3.3/lib/prometheus_exporter/instrumentation/process.rb#L39-L42

# File lib/sidekiq_prometheus/periodic_metrics.rb, line 124
def rss
  pid = Process.pid
  @pagesize ||= `getconf PAGESIZE`.to_i rescue 4096
  File.read("/proc/#{pid}/statm").split(' ')[1].to_i * @pagesize rescue 0
end
run() click to toggle source

Report metrics and sleep for @interval seconds in a loop. Runs until @done is true

# File lib/sidekiq_prometheus/periodic_metrics.rb, line 133
def run
  until done
    begin
      report_global_metrics if SidekiqPrometheus.global_metrics_enabled? && senate.leader?
      report_redis_metrics if SidekiqPrometheus.global_metrics_enabled? && senate.leader?
      report_gc_metrics if SidekiqPrometheus.gc_metrics_enabled?
    rescue StandardError => e
      Sidekiq.logger.error e
    ensure
      sleep interval
    end
  end
end
start() click to toggle source

Start the period mettric reporter

# File lib/sidekiq_prometheus/periodic_metrics.rb, line 58
def start
  Sidekiq.logger.info('SidekiqPrometheus: Starting periodic metrics reporting')
  @thread = Thread.new(&method(:run))
end
stop() click to toggle source

Stop the periodic metric reporter

# File lib/sidekiq_prometheus/periodic_metrics.rb, line 65
def stop
  self.done = true
end