Class: SidekiqPrometheus::PeriodicMetrics

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

Defined Under Namespace

Classes: Senate

Constant Summary collapse

GLOBAL_STATS =
i[failed processed retry_size dead_size scheduled_size workers_size processes_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, config: 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



50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
# File 'lib/sidekiq_prometheus/periodic_metrics.rb', line 50

def initialize(interval: SidekiqPrometheus.periodic_reporting_interval, sidekiq_stats: Sidekiq::Stats, sidekiq_queue: Sidekiq::Queue, senate: nil, config: nil)
  self.done = false
  @interval = interval
  @config = config
  @sidekiq_stats = sidekiq_stats
  @sidekiq_queue = sidekiq_queue
  @senate = if senate.nil?
    if Object.const_defined?("Sidekiq::Senate")
      if respond_to?(:leader?)
        self
      else
        Sidekiq::Senate
      end
    else
      Senate
    end
  else
    senate
  end
  @leader = false
end

Instance Attribute Details

#doneBoolean

Returns When true will stop the reporting loop.

Returns:

  • (Boolean)

    When true will stop the reporting loop.



23
24
25
# File 'lib/sidekiq_prometheus/periodic_metrics.rb', line 23

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]



29
30
31
# File 'lib/sidekiq_prometheus/periodic_metrics.rb', line 29

def interval
  @interval
end

#leaderBoolean

Returns Indicates if this instance is currently the leader.

Returns:

  • (Boolean)

    Indicates if this instance is currently the leader.



26
27
28
# File 'lib/sidekiq_prometheus/periodic_metrics.rb', line 26

def leader
  @leader
end

#senateObject (readonly)

Returns the value of attribute senate.



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

def senate
  @senate
end

#sidekiq_queueObject (readonly)

Returns the value of attribute sidekiq_queue.



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

def sidekiq_queue
  @sidekiq_queue
end

#sidekiq_statsObject (readonly)

Returns the value of attribute sidekiq_stats.



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

def sidekiq_stats
  @sidekiq_stats
end

Class Method Details

.reporter(config) ⇒ SidekiqPrometheus:PeriodicMetrics

Instance of SidekiqPrometheus::PeriodicMetrics

Returns:

  • (SidekiqPrometheus:PeriodicMetrics)


42
43
44
# File 'lib/sidekiq_prometheus/periodic_metrics.rb', line 42

def self.reporter(config)
  @reporter ||= new(config: config)
end

Instance Method Details

#handle_leader_state(senate_leader) ⇒ Object

If this instance was promoted to the leader set the local state and register the metrics.



181
182
183
184
185
186
187
188
189
190
191
192
193
# File 'lib/sidekiq_prometheus/periodic_metrics.rb', line 181

def handle_leader_state(senate_leader)
  return if senate_leader == leader

  if senate_leader && !leader
    # This instance is now the leader
    self.leader = true
    SidekiqPrometheus::Metrics.register_sidekiq_global_metrics
  else
    # we've been demoted!
    SidekiqPrometheus::Metrics.unregister_sidekiq_global_metrics
    self.leader = false
  end
end

#report_gc_metricsObject

Record GC and RSS metrics



87
88
89
90
91
92
93
94
95
96
97
# File 'lib/sidekiq_prometheus/periodic_metrics.rb', line 87

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



101
102
103
104
105
106
107
108
109
110
111
# File 'lib/sidekiq_prometheus/periodic_metrics.rb', line 101

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



115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
# File 'lib/sidekiq_prometheus/periodic_metrics.rb', line 115

def report_redis_metrics
  redis_info = begin
    if SidekiqPrometheus.sidekiq_seven?
      Sidekiq.default_configuration.redis_info
    else
      Sidekiq.redis_info
    end
  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



144
145
146
147
148
149
150
151
152
153
154
155
156
# File 'lib/sidekiq_prometheus/periodic_metrics.rb', line 144

def rss
  pid = Process.pid
  @pagesize ||= begin
    `getconf PAGESIZE`.to_i
  rescue
    4096
  end
  begin
    File.read("/proc/#{pid}/statm").split(" ")[1].to_i * @pagesize
  rescue
    0
  end
end

#runObject

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



161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
# File 'lib/sidekiq_prometheus/periodic_metrics.rb', line 161

def run
  until done
    begin
      if SidekiqPrometheus.global_metrics_enabled?
        handle_leader_state(senate.leader?)
        report_global_metrics if leader
        report_redis_metrics if leader
      end

      report_gc_metrics if SidekiqPrometheus.gc_metrics_enabled?
    rescue => e
      Sidekiq.logger.error e
    ensure
      sleep interval
    end
  end
end

#startObject

Start the period mettric reporter



74
75
76
77
# File 'lib/sidekiq_prometheus/periodic_metrics.rb', line 74

def start
  Sidekiq.logger.info("SidekiqPrometheus: Starting periodic metrics reporting")
  @thread = Thread.new(&method(:run))
end

#stopObject

Stop the periodic metric reporter



81
82
83
# File 'lib/sidekiq_prometheus/periodic_metrics.rb', line 81

def stop
  self.done = true
end