prometheus_exporter 0.7.0 → 2.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (65) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +298 -35
  3. data/README.md +276 -53
  4. data/{bin → exe}/prometheus_exporter +20 -7
  5. data/lib/prometheus_exporter/client.rb +41 -32
  6. data/lib/prometheus_exporter/instrumentation/active_record.rb +29 -35
  7. data/lib/prometheus_exporter/instrumentation/delayed_job.rb +28 -13
  8. data/lib/prometheus_exporter/instrumentation/good_job.rb +28 -0
  9. data/lib/prometheus_exporter/instrumentation/hutch.rb +1 -1
  10. data/lib/prometheus_exporter/instrumentation/method_profiler.rb +67 -27
  11. data/lib/prometheus_exporter/instrumentation/periodic_stats.rb +54 -0
  12. data/lib/prometheus_exporter/instrumentation/process.rb +25 -27
  13. data/lib/prometheus_exporter/instrumentation/puma.rb +36 -27
  14. data/lib/prometheus_exporter/instrumentation/resque.rb +33 -0
  15. data/lib/prometheus_exporter/instrumentation/shoryuken.rb +6 -7
  16. data/lib/prometheus_exporter/instrumentation/sidekiq.rb +51 -23
  17. data/lib/prometheus_exporter/instrumentation/sidekiq_process.rb +45 -0
  18. data/lib/prometheus_exporter/instrumentation/sidekiq_queue.rb +38 -33
  19. data/lib/prometheus_exporter/instrumentation/sidekiq_stats.rb +32 -0
  20. data/lib/prometheus_exporter/instrumentation/unicorn.rb +12 -17
  21. data/lib/prometheus_exporter/instrumentation.rb +5 -0
  22. data/lib/prometheus_exporter/metric/base.rb +20 -17
  23. data/lib/prometheus_exporter/metric/counter.rb +1 -3
  24. data/lib/prometheus_exporter/metric/gauge.rb +6 -6
  25. data/lib/prometheus_exporter/metric/histogram.rb +15 -5
  26. data/lib/prometheus_exporter/metric/summary.rb +5 -14
  27. data/lib/prometheus_exporter/middleware.rb +72 -38
  28. data/lib/prometheus_exporter/server/active_record_collector.rb +16 -14
  29. data/lib/prometheus_exporter/server/collector.rb +29 -17
  30. data/lib/prometheus_exporter/server/collector_base.rb +0 -2
  31. data/lib/prometheus_exporter/server/delayed_job_collector.rb +76 -33
  32. data/lib/prometheus_exporter/server/good_job_collector.rb +52 -0
  33. data/lib/prometheus_exporter/server/hutch_collector.rb +19 -11
  34. data/lib/prometheus_exporter/server/metrics_container.rb +66 -0
  35. data/lib/prometheus_exporter/server/process_collector.rb +15 -14
  36. data/lib/prometheus_exporter/server/puma_collector.rb +21 -18
  37. data/lib/prometheus_exporter/server/resque_collector.rb +50 -0
  38. data/lib/prometheus_exporter/server/runner.rb +49 -13
  39. data/lib/prometheus_exporter/server/shoryuken_collector.rb +22 -17
  40. data/lib/prometheus_exporter/server/sidekiq_collector.rb +22 -14
  41. data/lib/prometheus_exporter/server/sidekiq_process_collector.rb +47 -0
  42. data/lib/prometheus_exporter/server/sidekiq_queue_collector.rb +12 -12
  43. data/lib/prometheus_exporter/server/sidekiq_stats_collector.rb +49 -0
  44. data/lib/prometheus_exporter/server/type_collector.rb +2 -0
  45. data/lib/prometheus_exporter/server/unicorn_collector.rb +32 -33
  46. data/lib/prometheus_exporter/server/web_collector.rb +48 -31
  47. data/lib/prometheus_exporter/server/web_server.rb +70 -48
  48. data/lib/prometheus_exporter/server.rb +4 -0
  49. data/lib/prometheus_exporter/version.rb +1 -1
  50. data/lib/prometheus_exporter.rb +12 -13
  51. metadata +19 -206
  52. data/.github/workflows/ci.yml +0 -42
  53. data/.gitignore +0 -13
  54. data/.rubocop.yml +0 -7
  55. data/Appraisals +0 -10
  56. data/CODE_OF_CONDUCT.md +0 -74
  57. data/Gemfile +0 -8
  58. data/Guardfile +0 -8
  59. data/Rakefile +0 -12
  60. data/bench/bench.rb +0 -45
  61. data/examples/custom_collector.rb +0 -27
  62. data/gemfiles/.bundle/config +0 -2
  63. data/gemfiles/ar_60.gemfile +0 -5
  64. data/gemfiles/ar_61.gemfile +0 -7
  65. data/prometheus_exporter.gemspec +0 -46
@@ -14,8 +14,8 @@ module PrometheusExporter::Server
14
14
  end
15
15
 
16
16
  def collect(obj)
17
- default_labels = { job_name: obj['name'] }
18
- custom_labels = obj['custom_labels']
17
+ default_labels = { job_name: obj["name"] }
18
+ custom_labels = obj["custom_labels"]
19
19
  labels = custom_labels.nil? ? default_labels : default_labels.merge(custom_labels)
20
20
 
21
21
  ensure_hutch_metrics
@@ -36,15 +36,23 @@ module PrometheusExporter::Server
36
36
 
37
37
  def ensure_hutch_metrics
38
38
  if !@hutch_jobs_total
39
-
40
- @hutch_job_duration_seconds = PrometheusExporter::Metric::Counter.new(
41
- "hutch_job_duration_seconds", "Total time spent in hutch jobs.")
42
-
43
- @hutch_jobs_total = PrometheusExporter::Metric::Counter.new(
44
- "hutch_jobs_total", "Total number of hutch jobs executed.")
45
-
46
- @hutch_failed_jobs_total = PrometheusExporter::Metric::Counter.new(
47
- "hutch_failed_jobs_total", "Total number failed hutch jobs executed.")
39
+ @hutch_job_duration_seconds =
40
+ PrometheusExporter::Metric::Counter.new(
41
+ "hutch_job_duration_seconds",
42
+ "Total time spent in hutch jobs.",
43
+ )
44
+
45
+ @hutch_jobs_total =
46
+ PrometheusExporter::Metric::Counter.new(
47
+ "hutch_jobs_total",
48
+ "Total number of hutch jobs executed.",
49
+ )
50
+
51
+ @hutch_failed_jobs_total =
52
+ PrometheusExporter::Metric::Counter.new(
53
+ "hutch_failed_jobs_total",
54
+ "Total number failed hutch jobs executed.",
55
+ )
48
56
  end
49
57
  end
50
58
  end
@@ -0,0 +1,66 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PrometheusExporter::Server
4
+ class MetricsContainer
5
+ METRIC_MAX_AGE = 60
6
+ METRIC_EXPIRE_ATTR = "_expire_at"
7
+
8
+ attr_reader :data, :ttl
9
+ attr_accessor :filter
10
+
11
+ def initialize(ttl: METRIC_MAX_AGE, expire_attr: METRIC_EXPIRE_ATTR, filter: nil)
12
+ @data = []
13
+ @ttl = ttl
14
+ @expire_attr = expire_attr
15
+ @filter = filter
16
+ end
17
+
18
+ def <<(obj)
19
+ now = get_time
20
+ obj[@expire_attr] = now + @ttl
21
+
22
+ expire(time: now, new_metric: obj)
23
+
24
+ @data << obj
25
+ @data
26
+ end
27
+
28
+ def [](key)
29
+ @data.tap { expire }[key]
30
+ end
31
+
32
+ def size(&blk)
33
+ wrap_expire(:size, &blk)
34
+ end
35
+ alias_method :length, :size
36
+
37
+ def map(&blk)
38
+ wrap_expire(:map, &blk)
39
+ end
40
+
41
+ def each(&blk)
42
+ wrap_expire(:each, &blk)
43
+ end
44
+
45
+ def expire(time: nil, new_metric: nil)
46
+ time ||= get_time
47
+
48
+ @data.delete_if do |metric|
49
+ expired = metric[@expire_attr] < time
50
+ expired ||= filter.call(new_metric, metric) if @filter && new_metric
51
+ expired
52
+ end
53
+ end
54
+
55
+ private
56
+
57
+ def get_time
58
+ ::Process.clock_gettime(::Process::CLOCK_MONOTONIC)
59
+ end
60
+
61
+ def wrap_expire(method_name, &blk)
62
+ expire
63
+ @data.public_send(method_name, &blk)
64
+ end
65
+ end
66
+ end
@@ -1,9 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module PrometheusExporter::Server
4
-
5
4
  class ProcessCollector < TypeCollector
6
- MAX_PROCESS_METRIC_AGE = 60
5
+ MAX_METRIC_AGE = 60
6
+
7
7
  PROCESS_GAUGES = {
8
8
  heap_free_slots: "Free ruby heap slots.",
9
9
  heap_live_slots: "Used ruby heap slots.",
@@ -12,6 +12,12 @@ module PrometheusExporter::Server
12
12
  v8_physical_size: "Physical size consumed by V8 heaps.",
13
13
  v8_heap_count: "Number of V8 contexts running.",
14
14
  rss: "Total RSS used by process.",
15
+ malloc_increase_bytes_limit:
16
+ "Limit before Ruby triggers a GC against current objects (bytes).",
17
+ oldmalloc_increase_bytes_limit:
18
+ "Limit before Ruby triggers a major GC against old objects (bytes).",
19
+ marking_time: "Time spent in GC marking.",
20
+ sweeping_time: "Time spent in GC sweeping.",
15
21
  }
16
22
 
17
23
  PROCESS_COUNTERS = {
@@ -21,7 +27,10 @@ module PrometheusExporter::Server
21
27
  }
22
28
 
23
29
  def initialize
24
- @process_metrics = []
30
+ @process_metrics = MetricsContainer.new(ttl: MAX_METRIC_AGE)
31
+ @process_metrics.filter = ->(new_metric, old_metric) do
32
+ new_metric["pid"] == old_metric["pid"] && new_metric["hostname"] == old_metric["hostname"]
33
+ end
25
34
  end
26
35
 
27
36
  def type
@@ -34,8 +43,9 @@ module PrometheusExporter::Server
34
43
  metrics = {}
35
44
 
36
45
  @process_metrics.map do |m|
37
- metric_key = m["metric_labels"].merge("pid" => m["pid"])
38
- metric_key.merge!(m["custom_labels"] || {})
46
+ metric_key =
47
+ (m["metric_labels"] || {}).merge("pid" => m["pid"], "hostname" => m["hostname"])
48
+ metric_key.merge!(m["custom_labels"]) if m["custom_labels"]
39
49
 
40
50
  PROCESS_GAUGES.map do |k, help|
41
51
  k = k.to_s
@@ -58,15 +68,6 @@ module PrometheusExporter::Server
58
68
  end
59
69
 
60
70
  def collect(obj)
61
- now = ::Process.clock_gettime(::Process::CLOCK_MONOTONIC)
62
-
63
- obj["created_at"] = now
64
-
65
- @process_metrics.delete_if do |current|
66
- (obj["pid"] == current["pid"] && obj["hostname"] == current["hostname"]) ||
67
- (current["created_at"] + MAX_PROCESS_METRIC_AGE < now)
68
- end
69
-
70
71
  @process_metrics << obj
71
72
  end
72
73
  end
@@ -4,17 +4,27 @@ module PrometheusExporter::Server
4
4
  class PumaCollector < TypeCollector
5
5
  MAX_PUMA_METRIC_AGE = 30
6
6
  PUMA_GAUGES = {
7
- workers_total: "Number of puma workers.",
8
- booted_workers_total: "Number of puma workers booted.",
9
- old_workers_total: "Number of old puma workers.",
10
- running_threads_total: "Number of puma threads currently running.",
11
- request_backlog_total: "Number of requests waiting to be processed by a puma thread.",
12
- thread_pool_capacity_total: "Number of puma threads available at current scale.",
13
- max_threads_total: "Number of puma threads at available at max scale.",
7
+ workers: "Number of puma workers.",
8
+ booted_workers: "Number of puma workers booted.",
9
+ old_workers: "Number of old puma workers.",
10
+ running_threads: "Number of puma threads currently running.",
11
+ request_backlog: "Number of requests waiting to be processed by a puma thread.",
12
+ thread_pool_capacity: "Number of puma threads available at current scale.",
13
+ max_threads: "Number of puma threads at available at max scale.",
14
14
  }
15
15
 
16
+ if defined?(::Puma::Const) &&
17
+ Gem::Version.new(::Puma::Const::VERSION) >= Gem::Version.new("6.6.0")
18
+ PUMA_GAUGES[
19
+ :busy_threads
20
+ ] = "Wholistic stat reflecting the overall current state of work to be done and the capacity to do it"
21
+ end
22
+
16
23
  def initialize
17
- @puma_metrics = []
24
+ @puma_metrics = MetricsContainer.new(ttl: MAX_PUMA_METRIC_AGE)
25
+ @puma_metrics.filter = ->(new_metric, old_metric) do
26
+ new_metric["pid"] == old_metric["pid"] && new_metric["hostname"] == old_metric["hostname"]
27
+ end
18
28
  end
19
29
 
20
30
  def type
@@ -28,12 +38,9 @@ module PrometheusExporter::Server
28
38
 
29
39
  @puma_metrics.map do |m|
30
40
  labels = {}
31
- if m["phase"]
32
- labels.merge!(phase: m["phase"])
33
- end
34
- if m["custom_labels"]
35
- labels.merge!(m["custom_labels"])
36
- end
41
+ labels.merge!(phase: m["phase"]) if m["phase"]
42
+ labels.merge!(m["custom_labels"]) if m["custom_labels"]
43
+ labels.merge!(m["metric_labels"]) if m["metric_labels"]
37
44
 
38
45
  PUMA_GAUGES.map do |k, help|
39
46
  k = k.to_s
@@ -48,10 +55,6 @@ module PrometheusExporter::Server
48
55
  end
49
56
 
50
57
  def collect(obj)
51
- now = ::Process.clock_gettime(::Process::CLOCK_MONOTONIC)
52
-
53
- obj["created_at"] = now
54
- @puma_metrics.delete_if { |m| m["created_at"] + MAX_PUMA_METRIC_AGE < now }
55
58
  @puma_metrics << obj
56
59
  end
57
60
  end
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PrometheusExporter::Server
4
+ class ResqueCollector < TypeCollector
5
+ MAX_METRIC_AGE = 30
6
+ RESQUE_GAUGES = {
7
+ processed_jobs: "Total number of processed Resque jobs.",
8
+ failed_jobs: "Total number of failed Resque jobs.",
9
+ pending_jobs: "Total number of pending Resque jobs.",
10
+ queues: "Total number of Resque queues.",
11
+ workers: "Total number of Resque workers running.",
12
+ working: "Total number of Resque workers working.",
13
+ }
14
+
15
+ def initialize
16
+ @resque_metrics = MetricsContainer.new(ttl: MAX_METRIC_AGE)
17
+ @gauges = {}
18
+ end
19
+
20
+ def type
21
+ "resque"
22
+ end
23
+
24
+ def metrics
25
+ return [] if resque_metrics.length == 0
26
+
27
+ resque_metrics.map do |metric|
28
+ labels = metric.fetch("custom_labels", {})
29
+
30
+ RESQUE_GAUGES.map do |name, help|
31
+ name = name.to_s
32
+ if value = metric[name]
33
+ gauge = gauges[name] ||= PrometheusExporter::Metric::Gauge.new("resque_#{name}", help)
34
+ gauge.observe(value, labels)
35
+ end
36
+ end
37
+ end
38
+
39
+ gauges.values
40
+ end
41
+
42
+ def collect(object)
43
+ @resque_metrics << object
44
+ end
45
+
46
+ private
47
+
48
+ attr_reader :resque_metrics, :gauges
49
+ end
50
+ end
@@ -1,11 +1,13 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'prometheus_exporter/client'
4
- require_relative '../instrumentation/unicorn'
3
+ require_relative "../client"
5
4
 
6
5
  module PrometheusExporter::Server
7
- class RunnerException < StandardError; end
8
- class WrongInheritance < RunnerException; end
6
+ class RunnerException < StandardError
7
+ end
8
+
9
+ class WrongInheritance < RunnerException
10
+ end
9
11
 
10
12
  class Runner
11
13
  def initialize(options = {})
@@ -17,37 +19,67 @@ module PrometheusExporter::Server
17
19
  @prefix = nil
18
20
  @auth = nil
19
21
  @realm = nil
22
+ @histogram = nil
20
23
 
21
- options.each do |k, v|
22
- send("#{k}=", v) if self.class.method_defined?("#{k}=")
23
- end
24
+ options.each { |k, v| send("#{k}=", v) if self.class.method_defined?("#{k}=") }
24
25
  end
25
26
 
26
27
  def start
27
28
  PrometheusExporter::Metric::Base.default_prefix = prefix
28
29
  PrometheusExporter::Metric::Base.default_labels = label
29
30
 
31
+ if histogram
32
+ PrometheusExporter::Metric::Base.default_aggregation = PrometheusExporter::Metric::Histogram
33
+ end
34
+
30
35
  register_type_collectors
31
36
 
32
37
  unless collector.is_a?(PrometheusExporter::Server::CollectorBase)
33
- raise WrongInheritance, 'Collector class must be inherited from PrometheusExporter::Server::CollectorBase'
38
+ raise WrongInheritance,
39
+ "Collector class must be inherited from PrometheusExporter::Server::CollectorBase"
34
40
  end
35
41
 
36
42
  if unicorn_listen_address && unicorn_pid_file
43
+ require_relative "../instrumentation"
44
+
37
45
  local_client = PrometheusExporter::LocalClient.new(collector: collector)
38
46
  PrometheusExporter::Instrumentation::Unicorn.start(
39
47
  pid_file: unicorn_pid_file,
40
48
  listener_address: unicorn_listen_address,
41
- client: local_client
49
+ client: local_client,
42
50
  )
43
51
  end
44
52
 
45
- server = server_class.new(port: port, bind: bind, collector: collector, timeout: timeout, verbose: verbose, auth: auth, realm: realm)
46
- server.start
53
+ @server =
54
+ server_class.new(
55
+ port: port,
56
+ bind: bind,
57
+ collector: collector,
58
+ timeout: timeout,
59
+ verbose: verbose,
60
+ auth: auth,
61
+ realm: realm,
62
+ )
63
+ @server.start
64
+ end
65
+
66
+ def stop
67
+ @server.stop
47
68
  end
48
69
 
49
70
  attr_accessor :unicorn_listen_address, :unicorn_pid_file
50
- attr_writer :prefix, :port, :bind, :collector_class, :type_collectors, :timeout, :verbose, :server_class, :label, :auth, :realm
71
+ attr_writer :prefix,
72
+ :port,
73
+ :bind,
74
+ :collector_class,
75
+ :type_collectors,
76
+ :timeout,
77
+ :verbose,
78
+ :server_class,
79
+ :label,
80
+ :auth,
81
+ :realm,
82
+ :histogram
51
83
 
52
84
  def auth
53
85
  @auth || nil
@@ -82,7 +114,7 @@ module PrometheusExporter::Server
82
114
  end
83
115
 
84
116
  def verbose
85
- return @verbose if defined? @verbose
117
+ return @verbose if defined?(@verbose)
86
118
  false
87
119
  end
88
120
 
@@ -98,6 +130,10 @@ module PrometheusExporter::Server
98
130
  @label ||= PrometheusExporter::DEFAULT_LABEL
99
131
  end
100
132
 
133
+ def histogram
134
+ @histogram || false
135
+ end
136
+
101
137
  private
102
138
 
103
139
  def register_type_collectors
@@ -2,7 +2,6 @@
2
2
 
3
3
  module PrometheusExporter::Server
4
4
  class ShoryukenCollector < TypeCollector
5
-
6
5
  def initialize
7
6
  @shoryuken_jobs_total = nil
8
7
  @shoryuken_job_duration_seconds = nil
@@ -16,8 +15,8 @@ module PrometheusExporter::Server
16
15
  end
17
16
 
18
17
  def collect(obj)
19
- default_labels = { job_name: obj['name'] , queue_name: obj['queue'] }
20
- custom_labels = obj['custom_labels']
18
+ default_labels = { job_name: obj["name"], queue_name: obj["queue"] }
19
+ custom_labels = obj["custom_labels"]
21
20
  labels = custom_labels.nil? ? default_labels : default_labels.merge(custom_labels)
22
21
 
23
22
  ensure_shoryuken_metrics
@@ -30,10 +29,10 @@ module PrometheusExporter::Server
30
29
  def metrics
31
30
  if @shoryuken_jobs_total
32
31
  [
33
- @shoryuken_job_duration_seconds,
34
- @shoryuken_jobs_total,
35
- @shoryuken_restarted_jobs_total,
36
- @shoryuken_failed_jobs_total,
32
+ @shoryuken_job_duration_seconds,
33
+ @shoryuken_jobs_total,
34
+ @shoryuken_restarted_jobs_total,
35
+ @shoryuken_failed_jobs_total,
37
36
  ]
38
37
  else
39
38
  []
@@ -44,23 +43,29 @@ module PrometheusExporter::Server
44
43
 
45
44
  def ensure_shoryuken_metrics
46
45
  if !@shoryuken_jobs_total
47
-
48
46
  @shoryuken_job_duration_seconds =
49
- PrometheusExporter::Metric::Counter.new(
50
- "shoryuken_job_duration_seconds", "Total time spent in shoryuken jobs.")
47
+ PrometheusExporter::Metric::Counter.new(
48
+ "shoryuken_job_duration_seconds",
49
+ "Total time spent in shoryuken jobs.",
50
+ )
51
51
 
52
52
  @shoryuken_jobs_total =
53
- PrometheusExporter::Metric::Counter.new(
54
- "shoryuken_jobs_total", "Total number of shoryuken jobs executed.")
53
+ PrometheusExporter::Metric::Counter.new(
54
+ "shoryuken_jobs_total",
55
+ "Total number of shoryuken jobs executed.",
56
+ )
55
57
 
56
58
  @shoryuken_restarted_jobs_total =
57
- PrometheusExporter::Metric::Counter.new(
58
- "shoryuken_restarted_jobs_total", "Total number of shoryuken jobs that we restarted because of a shoryuken shutdown.")
59
+ PrometheusExporter::Metric::Counter.new(
60
+ "shoryuken_restarted_jobs_total",
61
+ "Total number of shoryuken jobs that we restarted because of a shoryuken shutdown.",
62
+ )
59
63
 
60
64
  @shoryuken_failed_jobs_total =
61
- PrometheusExporter::Metric::Counter.new(
62
- "shoryuken_failed_jobs_total", "Total number of failed shoryuken jobs.")
63
-
65
+ PrometheusExporter::Metric::Counter.new(
66
+ "shoryuken_failed_jobs_total",
67
+ "Total number of failed shoryuken jobs.",
68
+ )
64
69
  end
65
70
  end
66
71
  end
@@ -2,7 +2,6 @@
2
2
 
3
3
  module PrometheusExporter::Server
4
4
  class SidekiqCollector < TypeCollector
5
-
6
5
  def initialize
7
6
  @sidekiq_jobs_total = nil
8
7
  @sidekiq_job_duration_seconds = nil
@@ -17,8 +16,8 @@ module PrometheusExporter::Server
17
16
  end
18
17
 
19
18
  def collect(obj)
20
- default_labels = { job_name: obj['name'], queue: obj['queue'] }
21
- custom_labels = obj['custom_labels']
19
+ default_labels = { job_name: obj["name"], queue: obj["queue"] }
20
+ custom_labels = obj["custom_labels"]
22
21
  labels = custom_labels.nil? ? default_labels : default_labels.merge(custom_labels)
23
22
 
24
23
  ensure_sidekiq_metrics
@@ -50,26 +49,35 @@ module PrometheusExporter::Server
50
49
 
51
50
  def ensure_sidekiq_metrics
52
51
  if !@sidekiq_jobs_total
53
-
54
52
  @sidekiq_job_duration_seconds =
55
- PrometheusExporter::Metric::Summary.new(
56
- "sidekiq_job_duration_seconds", "Total time spent in sidekiq jobs.")
53
+ PrometheusExporter::Metric::Base.default_aggregation.new(
54
+ "sidekiq_job_duration_seconds",
55
+ "Total time spent in sidekiq jobs.",
56
+ )
57
57
 
58
58
  @sidekiq_jobs_total =
59
- PrometheusExporter::Metric::Counter.new(
60
- "sidekiq_jobs_total", "Total number of sidekiq jobs executed.")
59
+ PrometheusExporter::Metric::Counter.new(
60
+ "sidekiq_jobs_total",
61
+ "Total number of sidekiq jobs executed.",
62
+ )
61
63
 
62
64
  @sidekiq_restarted_jobs_total =
63
- PrometheusExporter::Metric::Counter.new(
64
- "sidekiq_restarted_jobs_total", "Total number of sidekiq jobs that we restarted because of a sidekiq shutdown.")
65
+ PrometheusExporter::Metric::Counter.new(
66
+ "sidekiq_restarted_jobs_total",
67
+ "Total number of sidekiq jobs that we restarted because of a sidekiq shutdown.",
68
+ )
65
69
 
66
70
  @sidekiq_failed_jobs_total =
67
- PrometheusExporter::Metric::Counter.new(
68
- "sidekiq_failed_jobs_total", "Total number of failed sidekiq jobs.")
71
+ PrometheusExporter::Metric::Counter.new(
72
+ "sidekiq_failed_jobs_total",
73
+ "Total number of failed sidekiq jobs.",
74
+ )
69
75
 
70
76
  @sidekiq_dead_jobs_total =
71
- PrometheusExporter::Metric::Counter.new(
72
- "sidekiq_dead_jobs_total", "Total number of dead sidekiq jobs.")
77
+ PrometheusExporter::Metric::Counter.new(
78
+ "sidekiq_dead_jobs_total",
79
+ "Total number of dead sidekiq jobs.",
80
+ )
73
81
  end
74
82
  end
75
83
  end
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PrometheusExporter::Server
4
+ class SidekiqProcessCollector < PrometheusExporter::Server::TypeCollector
5
+ MAX_METRIC_AGE = 60
6
+
7
+ SIDEKIQ_PROCESS_GAUGES = {
8
+ "busy" => "Number of running jobs",
9
+ "concurrency" => "Maximum concurrency",
10
+ }.freeze
11
+
12
+ attr_reader :sidekiq_metrics, :gauges
13
+
14
+ def initialize
15
+ @sidekiq_metrics = MetricsContainer.new(ttl: MAX_METRIC_AGE)
16
+ @gauges = {}
17
+ end
18
+
19
+ def type
20
+ "sidekiq_process"
21
+ end
22
+
23
+ def metrics
24
+ SIDEKIQ_PROCESS_GAUGES.each_key { |name| gauges[name]&.reset! }
25
+
26
+ sidekiq_metrics.map do |metric|
27
+ labels = metric.fetch("labels", {})
28
+ SIDEKIQ_PROCESS_GAUGES.map do |name, help|
29
+ if (value = metric[name])
30
+ gauge =
31
+ gauges[name] ||= PrometheusExporter::Metric::Gauge.new(
32
+ "sidekiq_process_#{name}",
33
+ help,
34
+ )
35
+ gauge.observe(value, labels)
36
+ end
37
+ end
38
+ end
39
+
40
+ gauges.values
41
+ end
42
+
43
+ def collect(object)
44
+ @sidekiq_metrics << object["process"]
45
+ end
46
+ end
47
+ end
@@ -1,30 +1,33 @@
1
1
  # frozen_string_literal: true
2
2
  module PrometheusExporter::Server
3
3
  class SidekiqQueueCollector < TypeCollector
4
- MAX_SIDEKIQ_METRIC_AGE = 60
4
+ MAX_METRIC_AGE = 60
5
5
 
6
6
  SIDEKIQ_QUEUE_GAUGES = {
7
- 'backlog_total' => 'Size of the sidekiq queue.',
8
- 'latency_seconds' => 'Latency of the sidekiq queue.',
7
+ "backlog" => "Size of the sidekiq queue.",
8
+ "latency_seconds" => "Latency of the sidekiq queue.",
9
9
  }.freeze
10
10
 
11
11
  attr_reader :sidekiq_metrics, :gauges
12
12
 
13
13
  def initialize
14
- @sidekiq_metrics = []
14
+ @sidekiq_metrics = MetricsContainer.new(ttl: MAX_METRIC_AGE)
15
15
  @gauges = {}
16
16
  end
17
17
 
18
18
  def type
19
- 'sidekiq_queue'
19
+ "sidekiq_queue"
20
20
  end
21
21
 
22
22
  def metrics
23
+ SIDEKIQ_QUEUE_GAUGES.each_key { |name| gauges[name]&.reset! }
24
+
23
25
  sidekiq_metrics.map do |metric|
24
26
  labels = metric.fetch("labels", {})
25
27
  SIDEKIQ_QUEUE_GAUGES.map do |name, help|
26
28
  if (value = metric[name])
27
- gauge = gauges[name] ||= PrometheusExporter::Metric::Gauge.new("sidekiq_queue_#{name}", help)
29
+ gauge =
30
+ gauges[name] ||= PrometheusExporter::Metric::Gauge.new("sidekiq_queue_#{name}", help)
28
31
  gauge.observe(value, labels)
29
32
  end
30
33
  end
@@ -34,12 +37,9 @@ module PrometheusExporter::Server
34
37
  end
35
38
 
36
39
  def collect(object)
37
- now = ::Process.clock_gettime(::Process::CLOCK_MONOTONIC)
38
- object['queues'].each do |queue|
39
- queue["created_at"] = now
40
- queue["labels"].merge!(object['custom_labels']) if object['custom_labels']
41
- sidekiq_metrics.delete_if { |metric| metric['created_at'] + MAX_SIDEKIQ_METRIC_AGE < now }
42
- sidekiq_metrics << queue
40
+ object["queues"].each do |queue|
41
+ queue["labels"].merge!(object["custom_labels"]) if object["custom_labels"]
42
+ @sidekiq_metrics << queue
43
43
  end
44
44
  end
45
45
  end