prometheus_exporter 0.8.1 → 1.0.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.
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: []