prometheus_exporter 0.8.1 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 47861f365c3db51e840d0c62e30120518dfc64fe208cc639ae6653903d14c578
4
- data.tar.gz: 8e67b542b68012b0bd2cb64122eb2ba987a18eed1397449cab4363eea009ba44
3
+ metadata.gz: 4b9fae4f661a2154e78754266e14a491a3d4271a7f2bf308586d1d5c16975452
4
+ data.tar.gz: 3c12e51359809b26963be8d148ebf0775f83ad588ca6b7f352b855e73f553598
5
5
  SHA512:
6
- metadata.gz: 1f4ff589012b8f28f81ee3b63f7535c1eb138c232826da36f1b621dc6381398e7fa515aca6b6413423e965630d23b47ad9b505801d418e3c9a625c7d7a0c7030
7
- data.tar.gz: d457d6d2c60c11960b6b241d5f2e0864994f11f808624845b9f28a42968dc3b9da07214321631d9f1644b2fdcad4b5ad443c843c53416ad6326afae68bd4d879
6
+ metadata.gz: 866d3593b9d575451efec2a87dac5d0e4b03b9ed265b776c9ae3f65ce417e7cb04c8ee29769ba38d3fc5ad1abb5824c56c10f5ee7b8461d62629c95145f8c58d
7
+ data.tar.gz: 6dd42a1d78443807ca0b026617e55e23089e772620d0dfd88ccc3c2871a6096fb081ca0065060e943680158f7b78c88035bb8d64327f95e4756f3902add36fe3
@@ -12,7 +12,7 @@ jobs:
12
12
  name: Ruby ${{ matrix.ruby }}
13
13
  strategy:
14
14
  matrix:
15
- ruby: ["3.0","2.7", "2.6"]
15
+ ruby: ["3.0", "2.7", "2.6"]
16
16
  steps:
17
17
  - uses: actions/checkout@master
18
18
  with:
@@ -26,11 +26,18 @@ jobs:
26
26
  key: ${{ runner.os }}-${{ matrix.ruby }}-gems-v2-${{ hashFiles('**/Gemfile.lock') }}
27
27
  restore-keys: |
28
28
  ${{ runner.os }}-${{ matrix.ruby }}-gems-v2-
29
+
30
+ - name: Downgrade rubygems
31
+ run: |
32
+ # for Ruby <= 2.6 , details https://github.com/rubygems/rubygems/issues/3284
33
+ gem update --system 3.0.8
34
+ if: ${{ matrix.ruby == '2.6' || matrix.ruby == '2.7' }}
35
+ - name: Upgrade rubygems
36
+ run: |
37
+ gem update --system
29
38
  - name: Setup gems
30
39
  run: |
31
40
  gem install bundler
32
- # for Ruby <= 2.6 , details https://github.com/rubygems/rubygems/issues/3284
33
- gem update --system 3.0.8 && gem update --system
34
41
  bundle config path vendor/bundle
35
42
  bundle install --jobs 4
36
43
  bundle exec appraisal install
data/CHANGELOG CHANGED
@@ -1,3 +1,11 @@
1
+ 1.0.0 - 23-11-2021
2
+
3
+ - BREAKING: rename metrics to match prometheus official naming conventions (See https://prometheus.io/docs/practices/naming/#metric-names)
4
+ - FEATURE: Sidekiq process metrics
5
+ - FEATURE: Allow collecting web metrics as histograms
6
+ - FIX: logger improved for web server
7
+ - FIX: Remove job labels from DelayedJob queues
8
+
1
9
  0.8.1 - 04-08-2021
2
10
 
3
11
  - FEATURE: swap from hardcoded STDERR to logger pattern (see README for details)
data/README.md CHANGED
@@ -5,6 +5,7 @@ Prometheus Exporter allows you to aggregate custom metrics from multiple process
5
5
  To learn more see [Instrumenting Rails with Prometheus](https://samsaffron.com/archive/2018/02/02/instrumenting-rails-with-prometheus) (it has pretty pictures!)
6
6
 
7
7
  * [Requirements](#requirements)
8
+ * [Migrating from v0.x](#migrating-from-v0.x)
8
9
  * [Installation](#installation)
9
10
  * [Usage](#usage)
10
11
  * [Single process mode](#single-process-mode)
@@ -26,6 +27,7 @@ To learn more see [Instrumenting Rails with Prometheus](https://samsaffron.com/a
26
27
  * [Metrics default prefix / labels](#metrics-default-prefix--labels)
27
28
  * [Client default labels](#client-default-labels)
28
29
  * [Client default host](#client-default-host)
30
+ * [Histogram mode](#histogram-mode)
29
31
  * [Transport concerns](#transport-concerns)
30
32
  * [JSON generation and parsing](#json-generation-and-parsing)
31
33
  * [Logging](#logging)
@@ -35,7 +37,13 @@ To learn more see [Instrumenting Rails with Prometheus](https://samsaffron.com/a
35
37
 
36
38
  ## Requirements
37
39
 
38
- Minimum Ruby of version 2.5.0 is required, Ruby 2.4.0 is EOL as of 2020-04-05
40
+ Minimum Ruby of version 2.6.0 is required, Ruby 2.5.0 is EOL as of March 31st 2021.
41
+
42
+ ## Migrating from v0.x
43
+
44
+ There are some major changes in v1.x from v0.x.
45
+
46
+ - Some of metrics are renamed to match [prometheus official guide for metric names](https://prometheus.io/docs/practices/naming/#metric-names). (#184)
39
47
 
40
48
  ## Installation
41
49
 
@@ -382,6 +390,8 @@ Sidekiq.configure_server do |config|
382
390
  end
383
391
  ```
384
392
 
393
+ This will only monitor the queues that are consumed by the sidekiq process you are on. You can pass an `all_queues` parameter to monitor metrics on all queues.
394
+
385
395
  To monitor Sidekiq process info:
386
396
 
387
397
  ```ruby
@@ -389,6 +399,7 @@ Sidekiq.configure_server do |config|
389
399
  config.on :startup do
390
400
  require 'prometheus_exporter/instrumentation'
391
401
  PrometheusExporter::Instrumentation::Process.start type: 'sidekiq'
402
+ PrometheusExporter::Instrumentation::SidekiqProcess.start
392
403
  end
393
404
  end
394
405
  ```
@@ -425,11 +436,19 @@ This metric has a `job_name` label and a `queue` label.
425
436
  **PrometheusExporter::Instrumentation::SidekiqQueue**
426
437
  | Type | Name | Description |
427
438
  | --- | --- | --- |
428
- | Gauge | `sidekiq_queue_backlog_total` | Size of the sidekiq queue |
439
+ | Gauge | `sidekiq_queue_backlog` | Size of the sidekiq queue |
429
440
  | Gauge | `sidekiq_queue_latency_seconds` | Latency of the sidekiq queue |
430
441
 
431
442
  Both metrics will have a `queue` label with the name of the queue.
432
443
 
444
+ **PrometheusExporter::Instrumentation::SidekiqProcess**
445
+ | Type | Name | Description |
446
+ | --- | --- | --- |
447
+ | Gauge | `sidekiq_process_busy` | Number of busy workers for this process |
448
+ | Gauge | `sidekiq_process_concurrency` | Concurrency for this process |
449
+
450
+ Both metrics will include the labels `labels`, `queues`, `quiet`, `tag`, `hostname` and `identity`, as returned by the [Sidekiq API](https://github.com/mperham/sidekiq/wiki/API#processes).
451
+
433
452
  _See [Metrics collected by Process Instrumentation](#metrics-collected-by-process-instrumentation) for a list of metrics the Process instrumentation will produce._
434
453
 
435
454
  #### Shoryuken metrics
@@ -529,15 +548,15 @@ end
529
548
 
530
549
  #### Metrics collected by Puma Instrumentation
531
550
 
532
- | Type | Name | Description |
533
- | --- | --- | --- |
534
- | Gauge | `puma_workers_total` | Number of puma workers |
535
- | Gauge | `puma_booted_workers_total` | Number of puma workers booted |
536
- | Gauge | `puma_old_workers_total` | Number of old puma workers |
537
- | Gauge | `puma_running_threads_total` | Number of puma threads currently running |
538
- | Gauge | `puma_request_backlog_total` | Number of requests waiting to be processed by a puma thread |
539
- | Gauge | `puma_thread_pool_capacity_total` | Number of puma threads available at current scale |
540
- | Gauge | `puma_max_threads_total` | Number of puma threads at available at max scale |
551
+ | Type | Name | Description |
552
+ | --- | --- | --- |
553
+ | Gauge | `puma_workers` | Number of puma workers |
554
+ | Gauge | `puma_booted_workers` | Number of puma workers booted |
555
+ | Gauge | `puma_old_workers` | Number of old puma workers |
556
+ | Gauge | `puma_running_threads` | Number of puma threads currently running |
557
+ | Gauge | `puma_request_backlog` | Number of requests waiting to be processed by a puma thread |
558
+ | Gauge | `puma_thread_pool_capacity` | Number of puma threads available at current scale |
559
+ | Gauge | `puma_max_threads` | Number of puma threads at available at max scale |
541
560
 
542
561
  All metrics may have a `phase` label and all custom labels provided with the `labels` option.
543
562
 
@@ -554,14 +573,14 @@ PrometheusExporter::Instrumentation::Resque.start
554
573
 
555
574
  #### Metrics collected by Resque Instrumentation
556
575
 
557
- | Type | Name | Description |
558
- | --- | --- | --- |
559
- | Gauge | `processed_jobs_total` | Total number of processed Resque jobs |
560
- | Gauge | `failed_jobs_total` | Total number of failed Resque jobs |
561
- | Gauge | `pending_jobs_total` | Total number of pending Resque jobs |
562
- | Gauge | `queues_total` | Total number of Resque queues |
563
- | Gauge | `workers_total` | Total number of Resque workers running |
564
- | Gauge | `working_total` | Total number of Resque workers working |
576
+ | Type | Name | Description |
577
+ | --- | --- | --- |
578
+ | Gauge | `resque_processed_jobs` | Total number of processed Resque jobs |
579
+ | Gauge | `resque_failed_jobs` | Total number of failed Resque jobs |
580
+ | Gauge | `resque_pending_jobs` | Total number of pending Resque jobs |
581
+ | Gauge | `resque_queues` | Total number of Resque queues |
582
+ | Gauge | `resque_workers` | Total number of Resque workers running |
583
+ | Gauge | `resque_working` | Total number of Resque workers working |
565
584
 
566
585
  ### Unicorn process metrics
567
586
 
@@ -580,11 +599,11 @@ Note: You must install the `raindrops` gem in your `Gemfile` or locally.
580
599
 
581
600
  #### Metrics collected by Unicorn Instrumentation
582
601
 
583
- | Type | Name | Description |
584
- | --- | --- | --- |
585
- | Gauge | `unicorn_workers_total` | Number of unicorn workers |
586
- | Gauge | `unicorn_active_workers_total` | Number of active unicorn workers |
587
- | Gauge | `unicorn_request_backlog_total` | Number of requests waiting to be processed by a unicorn worker |
602
+ | Type | Name | Description |
603
+ | --- | --- | --- |
604
+ | Gauge | `unicorn_workers` | Number of unicorn workers |
605
+ | Gauge | `unicorn_active_workers` | Number of active unicorn workers |
606
+ | Gauge | `unicorn_request_backlog` | Number of requests waiting to be processed by a unicorn worker |
588
607
 
589
608
  ### Custom type collectors
590
609
 
@@ -769,6 +788,7 @@ Usage: prometheus_exporter [options]
769
788
  -c, --collector FILE (optional) Custom collector to run
770
789
  -a, --type-collector FILE (optional) Custom type collectors to run in main collector
771
790
  -v, --verbose
791
+ -g, --histogram Use histogram instead of summary for aggregations
772
792
  --auth FILE (optional) enable basic authentication using a htpasswd FILE
773
793
  --realm REALM (optional) Use REALM for basic authentication (default: "Prometheus Exporter")
774
794
  --unicorn-listen-address ADDRESS
@@ -839,6 +859,18 @@ http_requests_total{service="app-server-01",app_name="app-01"} 1
839
859
 
840
860
  By default, `PrometheusExporter::Client.default` connects to `localhost:9394`. If your setup requires this (e.g. when using `docker-compose`), you can change the default host and port by setting the environment variables `PROMETHEUS_EXPORTER_HOST` and `PROMETHEUS_EXPORTER_PORT`.
841
861
 
862
+ ### Histogram mode
863
+
864
+ By default, the built-in collectors will report aggregations as summaries. If you need to aggregate metrics across labels, you can switch from summaries to histograms:
865
+
866
+ ```
867
+ $ prometheus_exporter --histogram
868
+ ```
869
+
870
+ In histogram mode, the same metrics will be collected but will be reported as histograms rather than summaries. This sacrifices some precision but allows aggregating metrics across actions and nodes using [`histogram_quantile`].
871
+
872
+ [`histogram_quantile`]: https://prometheus.io/docs/prometheus/latest/querying/functions/#histogram_quantile
873
+
842
874
  ## Transport concerns
843
875
 
844
876
  Prometheus Exporter handles transport using a simple HTTP protocol. In multi process mode we avoid needing a large number of HTTP request by using chunked encoding to send metrics. This means that a single HTTP channel can deliver 100s or even 1000s of metrics over a single HTTP session to the `/send-metrics` endpoint. All calls to `send` and `send_json` on the `PrometheusExporter::Client` class are **non-blocking** and batched.
@@ -50,6 +50,9 @@ def run
50
50
  opt.on('-v', '--verbose') do |o|
51
51
  options[:verbose] = true
52
52
  end
53
+ opt.on('-g', '--histogram', "Use histogram instead of summary for aggregations") do |o|
54
+ options[:histogram] = true
55
+ end
53
56
  opt.on('--auth FILE', String, "(optional) enable basic authentication using a htpasswd FILE") do |o|
54
57
  options[:auth] = o
55
58
  end
@@ -46,9 +46,9 @@ module PrometheusExporter::Instrumentation
46
46
 
47
47
  if stats.key?("workers")
48
48
  metric[:phase] = stats["phase"]
49
- metric[:workers_total] = stats["workers"]
50
- metric[:booted_workers_total] = stats["booted_workers"]
51
- metric[:old_workers_total] = stats["old_workers"]
49
+ metric[:workers] = stats["workers"]
50
+ metric[:booted_workers] = stats["booted_workers"]
51
+ metric[:old_workers] = stats["old_workers"]
52
52
 
53
53
  stats["worker_status"].each do |worker|
54
54
  next if worker["last_status"].empty?
@@ -62,15 +62,15 @@ module PrometheusExporter::Instrumentation
62
62
  private
63
63
 
64
64
  def collect_worker_status(metric, status)
65
- metric[:request_backlog_total] ||= 0
66
- metric[:running_threads_total] ||= 0
67
- metric[:thread_pool_capacity_total] ||= 0
68
- metric[:max_threads_total] ||= 0
65
+ metric[:request_backlog] ||= 0
66
+ metric[:running_threads] ||= 0
67
+ metric[:thread_pool_capacity] ||= 0
68
+ metric[:max_threads] ||= 0
69
69
 
70
- metric[:request_backlog_total] += status["backlog"]
71
- metric[:running_threads_total] += status["running"]
72
- metric[:thread_pool_capacity_total] += status["pool_capacity"]
73
- metric[:max_threads_total] += status["max_threads"]
70
+ metric[:request_backlog] += status["backlog"]
71
+ metric[:running_threads] += status["running"]
72
+ metric[:thread_pool_capacity] += status["pool_capacity"]
73
+ metric[:max_threads] += status["max_threads"]
74
74
  end
75
75
  end
76
76
  end
@@ -29,12 +29,12 @@ module PrometheusExporter::Instrumentation
29
29
  def collect_resque_stats(metric)
30
30
  info = ::Resque.info
31
31
 
32
- metric[:processed_jobs_total] = info[:processed]
33
- metric[:failed_jobs_total] = info[:failed]
34
- metric[:pending_jobs_total] = info[:pending]
35
- metric[:queues_total] = info[:queues]
36
- metric[:worker_total] = info[:workers]
37
- metric[:working_total] = info[:working]
32
+ metric[:processed_jobs] = info[:processed]
33
+ metric[:failed_jobs] = info[:failed]
34
+ metric[:pending_jobs] = info[:pending]
35
+ metric[:queues] = info[:queues]
36
+ metric[:worker] = info[:workers]
37
+ metric[:working] = info[:working]
38
38
  end
39
39
  end
40
40
  end
@@ -0,0 +1,58 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PrometheusExporter::Instrumentation
4
+ class SidekiqProcess
5
+ def self.start(client: nil, frequency: 30)
6
+ client ||= PrometheusExporter::Client.default
7
+ sidekiq_process_collector = new
8
+
9
+ Thread.new do
10
+ loop do
11
+ begin
12
+ client.send_json(sidekiq_process_collector.collect)
13
+ rescue StandardError => e
14
+ STDERR.puts("Prometheus Exporter Failed To Collect Sidekiq Processes metrics #{e}")
15
+ ensure
16
+ sleep frequency
17
+ end
18
+ end
19
+ end
20
+ end
21
+
22
+ def initialize
23
+ @pid = ::Process.pid
24
+ @hostname = Socket.gethostname
25
+ end
26
+
27
+ def collect
28
+ {
29
+ type: 'sidekiq_process',
30
+ process: collect_stats
31
+ }
32
+ end
33
+
34
+ def collect_stats
35
+ process = current_process
36
+ return {} unless process
37
+
38
+ {
39
+ busy: process['busy'],
40
+ concurrency: process['concurrency'],
41
+ labels: {
42
+ labels: process['labels'].sort.join(','),
43
+ queues: process['queues'].sort.join(','),
44
+ quiet: process['quiet'],
45
+ tag: process['tag'],
46
+ hostname: process['hostname'],
47
+ identity: process['identity'],
48
+ }
49
+ }
50
+ end
51
+
52
+ def current_process
53
+ ::Sidekiq::ProcessSet.new.find do |sp|
54
+ sp['hostname'] == @hostname && sp['pid'] == @pid
55
+ end
56
+ end
57
+ end
58
+ end
@@ -2,9 +2,9 @@
2
2
 
3
3
  module PrometheusExporter::Instrumentation
4
4
  class SidekiqQueue
5
- def self.start(client: nil, frequency: 30)
5
+ def self.start(client: nil, frequency: 30, all_queues: false)
6
6
  client ||= PrometheusExporter::Client.default
7
- sidekiq_queue_collector = new
7
+ sidekiq_queue_collector = new(all_queues: all_queues)
8
8
 
9
9
  Thread.new do
10
10
  loop do
@@ -19,6 +19,12 @@ module PrometheusExporter::Instrumentation
19
19
  end
20
20
  end
21
21
 
22
+ def initialize(all_queues: false)
23
+ @all_queues = all_queues
24
+ @pid = ::Process.pid
25
+ @hostname = Socket.gethostname
26
+ end
27
+
22
28
  def collect
23
29
  {
24
30
  type: 'sidekiq_queue',
@@ -27,24 +33,32 @@ module PrometheusExporter::Instrumentation
27
33
  end
28
34
 
29
35
  def collect_queue_stats
30
- hostname = Socket.gethostname
31
- pid = ::Process.pid
32
- ps = ::Sidekiq::ProcessSet.new
36
+ sidekiq_queues = ::Sidekiq::Queue.all
33
37
 
34
- process = ps.find do |sp|
35
- sp['hostname'] == hostname && sp['pid'] == pid
38
+ unless @all_queues
39
+ queues = collect_current_process_queues
40
+ sidekiq_queues.select! { |sidekiq_queue| queues.include?(sidekiq_queue.name) }
36
41
  end
37
42
 
38
- queues = process.nil? ? [] : process['queues']
39
-
40
- ::Sidekiq::Queue.all.map do |queue|
41
- next unless queues.include? queue.name
43
+ sidekiq_queues.map do |queue|
42
44
  {
43
- backlog_total: queue.size,
45
+ backlog: queue.size,
44
46
  latency_seconds: queue.latency.to_i,
45
47
  labels: { queue: queue.name }
46
48
  }
47
49
  end.compact
48
50
  end
51
+
52
+ private
53
+
54
+ def collect_current_process_queues
55
+ ps = ::Sidekiq::ProcessSet.new
56
+
57
+ process = ps.find do |sp|
58
+ sp['hostname'] == @hostname && sp['pid'] == @pid
59
+ end
60
+
61
+ process.nil? ? [] : process['queues']
62
+ end
49
63
  end
50
64
  end
@@ -42,9 +42,9 @@ module PrometheusExporter::Instrumentation
42
42
  def collect_unicorn_stats(metric)
43
43
  stats = listener_address_stats
44
44
 
45
- metric[:active_workers_total] = stats.active
46
- metric[:request_backlog_total] = stats.queued
47
- metric[:workers_total] = worker_process_count
45
+ metric[:active_workers] = stats.active
46
+ metric[:request_backlog] = stats.queued
47
+ metric[:workers] = worker_process_count
48
48
  end
49
49
 
50
50
  private
@@ -5,6 +5,7 @@ require_relative "instrumentation/process"
5
5
  require_relative "instrumentation/method_profiler"
6
6
  require_relative "instrumentation/sidekiq"
7
7
  require_relative "instrumentation/sidekiq_queue"
8
+ require_relative "instrumentation/sidekiq_process"
8
9
  require_relative "instrumentation/delayed_job"
9
10
  require_relative "instrumentation/puma"
10
11
  require_relative "instrumentation/hutch"
@@ -5,6 +5,7 @@ module PrometheusExporter::Metric
5
5
 
6
6
  @default_prefix = nil if !defined?(@default_prefix)
7
7
  @default_labels = nil if !defined?(@default_labels)
8
+ @default_aggregation = nil if !defined?(@default_aggregation)
8
9
 
9
10
  # prefix applied to all metrics
10
11
  def self.default_prefix=(name)
@@ -23,6 +24,14 @@ module PrometheusExporter::Metric
23
24
  @default_labels || {}
24
25
  end
25
26
 
27
+ def self.default_aggregation=(aggregation)
28
+ @default_aggregation = aggregation
29
+ end
30
+
31
+ def self.default_aggregation
32
+ @default_aggregation ||= Summary
33
+ end
34
+
26
35
  attr_accessor :help, :name, :data
27
36
 
28
37
  def initialize(name, help)
@@ -5,6 +5,10 @@ module PrometheusExporter::Metric
5
5
  attr_reader :data
6
6
 
7
7
  def initialize(name, help)
8
+ if name.end_with?("_total")
9
+ raise ArgumentError, "The metric name of gauge must not have _total suffix. Given: #{name}"
10
+ end
11
+
8
12
  super
9
13
  reset!
10
14
  end
@@ -14,6 +14,7 @@ module PrometheusExporter::Server
14
14
  register_collector(ProcessCollector.new)
15
15
  register_collector(SidekiqCollector.new)
16
16
  register_collector(SidekiqQueueCollector.new)
17
+ register_collector(SidekiqProcessCollector.new)
17
18
  register_collector(DelayedJobCollector.new)
18
19
  register_collector(PumaCollector.new)
19
20
  register_collector(HutchCollector.new)
@@ -19,22 +19,21 @@ module PrometheusExporter::Server
19
19
  end
20
20
 
21
21
  def collect(obj)
22
- default_labels = { job_name: obj['name'], queue_name: obj['queue_name'] }
23
- custom_labels = obj['custom_labels']
24
-
25
- labels = custom_labels.nil? ? default_labels : default_labels.merge(custom_labels)
22
+ custom_labels = obj['custom_labels'] || {}
23
+ gauge_labels = { queue_name: obj['queue_name'] }.merge(custom_labels)
24
+ counter_labels = gauge_labels.merge(job_name: obj['name'])
26
25
 
27
26
  ensure_delayed_job_metrics
28
- @delayed_job_duration_seconds.observe(obj["duration"], labels)
29
- @delayed_jobs_total.observe(1, labels)
30
- @delayed_failed_jobs_total.observe(1, labels) if !obj["success"]
31
- @delayed_jobs_max_attempts_reached_total.observe(1, labels) if obj["attempts"] >= obj["max_attempts"]
32
- @delayed_job_duration_seconds_summary.observe(obj["duration"], labels)
33
- @delayed_job_duration_seconds_summary.observe(obj["duration"], labels.merge(status: "success")) if obj["success"]
34
- @delayed_job_duration_seconds_summary.observe(obj["duration"], labels.merge(status: "failed")) if !obj["success"]
35
- @delayed_job_attempts_summary.observe(obj["attempts"], labels) if obj["success"]
36
- @delayed_jobs_enqueued.observe(obj["enqueued"], labels)
37
- @delayed_jobs_pending.observe(obj["pending"], labels)
27
+ @delayed_job_duration_seconds.observe(obj["duration"], counter_labels)
28
+ @delayed_jobs_total.observe(1, counter_labels)
29
+ @delayed_failed_jobs_total.observe(1, counter_labels) if !obj["success"]
30
+ @delayed_jobs_max_attempts_reached_total.observe(1, counter_labels) if obj["attempts"] >= obj["max_attempts"]
31
+ @delayed_job_duration_seconds_summary.observe(obj["duration"], counter_labels)
32
+ @delayed_job_duration_seconds_summary.observe(obj["duration"], counter_labels.merge(status: "success")) if obj["success"]
33
+ @delayed_job_duration_seconds_summary.observe(obj["duration"], counter_labels.merge(status: "failed")) if !obj["success"]
34
+ @delayed_job_attempts_summary.observe(obj["attempts"], counter_labels) if obj["success"]
35
+ @delayed_jobs_enqueued.observe(obj["enqueued"], gauge_labels)
36
+ @delayed_jobs_pending.observe(obj["pending"], gauge_labels)
38
37
  end
39
38
 
40
39
  def metrics
@@ -77,12 +76,12 @@ module PrometheusExporter::Server
77
76
  "delayed_jobs_max_attempts_reached_total", "Total number of delayed jobs that reached max attempts.")
78
77
 
79
78
  @delayed_job_duration_seconds_summary =
80
- PrometheusExporter::Metric::Summary.new("delayed_job_duration_seconds_summary",
81
- "Summary of the time it takes jobs to execute.")
79
+ PrometheusExporter::Metric::Base.default_aggregation.new("delayed_job_duration_seconds_summary",
80
+ "Summary of the time it takes jobs to execute.")
82
81
 
83
82
  @delayed_job_attempts_summary =
84
- PrometheusExporter::Metric::Summary.new("delayed_job_attempts_summary",
85
- "Summary of the amount of attempts it takes delayed jobs to succeed.")
83
+ PrometheusExporter::Metric::Base.default_aggregation.new("delayed_job_attempts_summary",
84
+ "Summary of the amount of attempts it takes delayed jobs to succeed.")
86
85
  end
87
86
  end
88
87
  end
@@ -4,13 +4,13 @@ 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
16
  def initialize
@@ -4,12 +4,12 @@ module PrometheusExporter::Server
4
4
  class ResqueCollector < TypeCollector
5
5
  MAX_RESQUE_METRIC_AGE = 30
6
6
  RESQUE_GAUGES = {
7
- processed_jobs_total: "Total number of processed Resque jobs.",
8
- failed_jobs_total: "Total number of failed Resque jobs.",
9
- pending_jobs_total: "Total number of pending Resque jobs.",
10
- queues_total: "Total number of Resque queues.",
11
- workers_total: "Total number of Resque workers running.",
12
- working_total: "Total number of Resque workers working."
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
13
  }
14
14
 
15
15
  def initialize
@@ -17,6 +17,7 @@ module PrometheusExporter::Server
17
17
  @prefix = nil
18
18
  @auth = nil
19
19
  @realm = nil
20
+ @histogram = nil
20
21
 
21
22
  options.each do |k, v|
22
23
  send("#{k}=", v) if self.class.method_defined?("#{k}=")
@@ -27,6 +28,10 @@ module PrometheusExporter::Server
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)
@@ -47,7 +52,7 @@ module PrometheusExporter::Server
47
52
  end
48
53
 
49
54
  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
55
+ attr_writer :prefix, :port, :bind, :collector_class, :type_collectors, :timeout, :verbose, :server_class, :label, :auth, :realm, :histogram
51
56
 
52
57
  def auth
53
58
  @auth || nil
@@ -98,6 +103,10 @@ module PrometheusExporter::Server
98
103
  @label ||= PrometheusExporter::DEFAULT_LABEL
99
104
  end
100
105
 
106
+ def histogram
107
+ @histogram || false
108
+ end
109
+
101
110
  private
102
111
 
103
112
  def register_type_collectors
@@ -52,7 +52,7 @@ module PrometheusExporter::Server
52
52
  if !@sidekiq_jobs_total
53
53
 
54
54
  @sidekiq_job_duration_seconds =
55
- PrometheusExporter::Metric::Summary.new(
55
+ PrometheusExporter::Metric::Base.default_aggregation.new(
56
56
  "sidekiq_job_duration_seconds", "Total time spent in sidekiq jobs.")
57
57
 
58
58
  @sidekiq_jobs_total =
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PrometheusExporter::Server
4
+ class SidekiqProcessCollector < PrometheusExporter::Server::TypeCollector
5
+ MAX_SIDEKIQ_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 = []
16
+ @gauges = {}
17
+ end
18
+
19
+ def type
20
+ 'sidekiq_process'
21
+ end
22
+
23
+ def metrics
24
+ sidekiq_metrics.map do |metric|
25
+ labels = metric.fetch('labels', {})
26
+ SIDEKIQ_PROCESS_GAUGES.map do |name, help|
27
+ if (value = metric[name])
28
+ gauge = gauges[name] ||= PrometheusExporter::Metric::Gauge.new("sidekiq_process_#{name}", help)
29
+ gauges[name].observe(value, labels)
30
+ end
31
+ end
32
+ end
33
+
34
+ gauges.values
35
+ end
36
+
37
+ def collect(object)
38
+ now = ::Process.clock_gettime(::Process::CLOCK_MONOTONIC)
39
+ process = object['process']
40
+
41
+ process["created_at"] = now
42
+ sidekiq_metrics.delete_if { |metric| metric['created_at'] + MAX_SIDEKIQ_METRIC_AGE < now }
43
+ sidekiq_metrics << process
44
+ end
45
+ end
46
+ end
@@ -4,7 +4,7 @@ module PrometheusExporter::Server
4
4
  MAX_SIDEKIQ_METRIC_AGE = 60
5
5
 
6
6
  SIDEKIQ_QUEUE_GAUGES = {
7
- 'backlog_total' => 'Size of the sidekiq queue.',
7
+ 'backlog' => 'Size of the sidekiq queue.',
8
8
  'latency_seconds' => 'Latency of the sidekiq queue.',
9
9
  }.freeze
10
10
 
@@ -6,9 +6,9 @@ class PrometheusExporter::Server::UnicornCollector < PrometheusExporter::Server:
6
6
  MAX_UNICORN_METRIC_AGE = 60
7
7
 
8
8
  UNICORN_GAUGES = {
9
- workers_total: 'Number of unicorn workers.',
10
- active_workers_total: 'Number of active unicorn workers',
11
- request_backlog_total: 'Number of requests waiting to be processed by a unicorn worker.'
9
+ workers: 'Number of unicorn workers.',
10
+ active_workers: 'Number of active unicorn workers',
11
+ request_backlog: 'Number of requests waiting to be processed by a unicorn worker.'
12
12
  }.freeze
13
13
 
14
14
  def initialize
@@ -33,22 +33,22 @@ module PrometheusExporter::Server
33
33
  "Total HTTP requests from web app."
34
34
  )
35
35
 
36
- @metrics["http_duration_seconds"] = @http_duration_seconds = PrometheusExporter::Metric::Summary.new(
36
+ @metrics["http_duration_seconds"] = @http_duration_seconds = PrometheusExporter::Metric::Base.default_aggregation.new(
37
37
  "http_duration_seconds",
38
38
  "Time spent in HTTP reqs in seconds."
39
39
  )
40
40
 
41
- @metrics["http_redis_duration_seconds"] = @http_redis_duration_seconds = PrometheusExporter::Metric::Summary.new(
41
+ @metrics["http_redis_duration_seconds"] = @http_redis_duration_seconds = PrometheusExporter::Metric::Base.default_aggregation.new(
42
42
  "http_redis_duration_seconds",
43
43
  "Time spent in HTTP reqs in Redis, in seconds."
44
44
  )
45
45
 
46
- @metrics["http_sql_duration_seconds"] = @http_sql_duration_seconds = PrometheusExporter::Metric::Summary.new(
46
+ @metrics["http_sql_duration_seconds"] = @http_sql_duration_seconds = PrometheusExporter::Metric::Base.default_aggregation.new(
47
47
  "http_sql_duration_seconds",
48
48
  "Time spent in HTTP reqs in SQL in seconds."
49
49
  )
50
50
 
51
- @metrics["http_queue_duration_seconds"] = @http_queue_duration_seconds = PrometheusExporter::Metric::Summary.new(
51
+ @metrics["http_queue_duration_seconds"] = @http_queue_duration_seconds = PrometheusExporter::Metric::Base.default_aggregation.new(
52
52
  "http_queue_duration_seconds",
53
53
  "Time spent queueing the request in load balancer in seconds."
54
54
  )
@@ -88,7 +88,7 @@ module PrometheusExporter::Server
88
88
  @collector.process(block)
89
89
  rescue => e
90
90
  if @verbose
91
- logger.error "\n\n#{e.inspect}\n#{e.backtrace}\n\n"
91
+ @logger.error "\n\n#{e.inspect}\n#{e.backtrace}\n\n"
92
92
  end
93
93
  @bad_metrics_total.observe
94
94
  res.body = "Bad Metrics #{e}"
@@ -106,7 +106,7 @@ module PrometheusExporter::Server
106
106
  begin
107
107
  @server.start
108
108
  rescue => e
109
- logger.error "Failed to start prometheus collector web on port #{@port}: #{e}"
109
+ @logger.error "Failed to start prometheus collector web on port #{@port}: #{e}"
110
110
  end
111
111
  end
112
112
  end
@@ -123,7 +123,7 @@ module PrometheusExporter::Server
123
123
  end
124
124
  rescue Timeout::Error
125
125
  # we timed out ... bummer
126
- logger.error "Generating Prometheus metrics text timed out"
126
+ @logger.error "Generating Prometheus metrics text timed out"
127
127
  end
128
128
 
129
129
  metrics = []
@@ -6,6 +6,7 @@ require_relative "server/web_collector"
6
6
  require_relative "server/process_collector"
7
7
  require_relative "server/sidekiq_collector"
8
8
  require_relative "server/sidekiq_queue_collector"
9
+ require_relative "server/sidekiq_process_collector"
9
10
  require_relative "server/delayed_job_collector"
10
11
  require_relative "server/collector_base"
11
12
  require_relative "server/collector"
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module PrometheusExporter
4
- VERSION = '0.8.1'
4
+ VERSION = '1.0.0'
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: prometheus_exporter
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.8.1
4
+ version: 1.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sam Saffron
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-08-04 00:00:00.000000000 Z
11
+ date: 2021-11-23 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: webrick
@@ -243,6 +243,7 @@ files:
243
243
  - lib/prometheus_exporter/instrumentation/resque.rb
244
244
  - lib/prometheus_exporter/instrumentation/shoryuken.rb
245
245
  - lib/prometheus_exporter/instrumentation/sidekiq.rb
246
+ - lib/prometheus_exporter/instrumentation/sidekiq_process.rb
246
247
  - lib/prometheus_exporter/instrumentation/sidekiq_queue.rb
247
248
  - lib/prometheus_exporter/instrumentation/unicorn.rb
248
249
  - lib/prometheus_exporter/metric.rb
@@ -264,6 +265,7 @@ files:
264
265
  - lib/prometheus_exporter/server/runner.rb
265
266
  - lib/prometheus_exporter/server/shoryuken_collector.rb
266
267
  - lib/prometheus_exporter/server/sidekiq_collector.rb
268
+ - lib/prometheus_exporter/server/sidekiq_process_collector.rb
267
269
  - lib/prometheus_exporter/server/sidekiq_queue_collector.rb
268
270
  - lib/prometheus_exporter/server/type_collector.rb
269
271
  - lib/prometheus_exporter/server/unicorn_collector.rb
@@ -292,7 +294,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
292
294
  version: '0'
293
295
  requirements: []
294
296
  rubygems_version: 3.1.6
295
- signing_key:
297
+ signing_key:
296
298
  specification_version: 4
297
299
  summary: Prometheus Exporter
298
300
  test_files: []