Class: SidekiqPrometheus::PeriodicMetrics

Inherits:
Object
  • Object
show all
Defined in:
lib/sidekiq_prometheus/periodic_metrics.rb

Overview

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.

Defined Under Namespace

Classes: Senate

Constant Summary collapse

GLOBAL_STATS =
%i[failed processed retry_size dead_size scheduled_size workers_size].freeze
GC_STATS =
{
  counters: %i[major_gc_count minor_gc_count total_allocated_objects],
  gauges: %i[heap_live_slots heap_free_slots],
}.freeze
REDIS_STATS =
%w[connected_clients used_memory used_memory_peak].freeze

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(interval: SidekiqPrometheus.periodic_reporting_interval, sidekiq_stats: Sidekiq::Stats, sidekiq_queue: Sidekiq::Queue, senate: nil) ⇒ PeriodicMetrics

Returns a new instance of PeriodicMetrics.

Parameters:

  • interval (Integer) (defaults to: SidekiqPrometheus.periodic_reporting_interval)

    Interval in seconds to record metrics.

  • sidekiq_stats (Sidekiq::Stats) (defaults to: Sidekiq::Stats)
  • senate (#leader?) (defaults to: nil)

    Sidekiq::Senate



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

Instance Attribute Details

#doneBoolean

Returns When true will stop the reporting loop.

Returns:

  • (Boolean)

    When true will stop the reporting loop.



15
16
17
# File 'lib/sidekiq_prometheus/periodic_metrics.rb', line 15

def done
  @done
end

#intervalInteger (readonly)

Returns Interval in seconds to record metrics. Default: [SidekiqPrometheus.periodic_reporting_interval].

Returns:

  • (Integer)

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



18
19
20
# File 'lib/sidekiq_prometheus/periodic_metrics.rb', line 18

def interval
  @interval
end

#senateObject (readonly)

Returns the value of attribute senate.



19
20
21
# File 'lib/sidekiq_prometheus/periodic_metrics.rb', line 19

def senate
  @senate
end

#sidekiq_queueObject (readonly)

Returns the value of attribute sidekiq_queue.



19
20
21
# File 'lib/sidekiq_prometheus/periodic_metrics.rb', line 19

def sidekiq_queue
  @sidekiq_queue
end

#sidekiq_statsObject (readonly)

Returns the value of attribute sidekiq_stats.



19
20
21
# File 'lib/sidekiq_prometheus/periodic_metrics.rb', line 19

def sidekiq_stats
  @sidekiq_stats
end

Class Method Details

.reporterSidekiqPrometheus:PeriodicMetrics

Instance of SidekiqPrometheus::PeriodicMetrics

Returns:

  • (SidekiqPrometheus:PeriodicMetrics)


31
32
33
# File 'lib/sidekiq_prometheus/periodic_metrics.rb', line 31

def self.reporter
  @reporter ||= new
end

Instance Method Details

#report_gc_metricsObject

Record GC and RSS metrics



71
72
73
74
75
76
77
78
79
80
81
# 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_metricsObject

Records Sidekiq global metrics



85
86
87
88
89
90
91
92
93
94
95
# 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_metricsObject

Records metrics from Redis



99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
# 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

#rssObject

Fetch rss from proc filesystem



124
125
126
127
128
# 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

#runObject

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



133
134
135
136
137
138
139
140
141
142
143
144
145
# 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

#startObject

Start the period mettric reporter



58
59
60
61
# 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

#stopObject

Stop the periodic metric reporter



65
66
67
# File 'lib/sidekiq_prometheus/periodic_metrics.rb', line 65

def stop
  self.done = true
end