prometheus_exporter 2.1.0 → 2.2.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 (56) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ci.yml +10 -6
  3. data/.rubocop +1 -0
  4. data/.rubocop.yml +12 -1
  5. data/.streerc +2 -0
  6. data/CHANGELOG +12 -1
  7. data/README.md +22 -9
  8. data/bench/bench.rb +12 -11
  9. data/bin/prometheus_exporter +2 -2
  10. data/examples/custom_collector.rb +1 -3
  11. data/gemfiles/ar_70.gemfile +2 -0
  12. data/gemfiles/ar_71.gemfile +7 -0
  13. data/lib/prometheus_exporter/client.rb +16 -32
  14. data/lib/prometheus_exporter/instrumentation/active_record.rb +20 -8
  15. data/lib/prometheus_exporter/instrumentation/delayed_job.rb +23 -13
  16. data/lib/prometheus_exporter/instrumentation/good_job.rb +2 -4
  17. data/lib/prometheus_exporter/instrumentation/hutch.rb +1 -1
  18. data/lib/prometheus_exporter/instrumentation/method_profiler.rb +16 -16
  19. data/lib/prometheus_exporter/instrumentation/periodic_stats.rb +13 -21
  20. data/lib/prometheus_exporter/instrumentation/process.rb +14 -6
  21. data/lib/prometheus_exporter/instrumentation/puma.rb +1 -1
  22. data/lib/prometheus_exporter/instrumentation/resque.rb +1 -3
  23. data/lib/prometheus_exporter/instrumentation/shoryuken.rb +6 -7
  24. data/lib/prometheus_exporter/instrumentation/sidekiq.rb +4 -6
  25. data/lib/prometheus_exporter/instrumentation/sidekiq_process.rb +12 -19
  26. data/lib/prometheus_exporter/instrumentation/sidekiq_queue.rb +15 -18
  27. data/lib/prometheus_exporter/instrumentation/sidekiq_stats.rb +10 -15
  28. data/lib/prometheus_exporter/instrumentation/unicorn.rb +2 -2
  29. data/lib/prometheus_exporter/metric/base.rb +8 -7
  30. data/lib/prometheus_exporter/metric/counter.rb +1 -3
  31. data/lib/prometheus_exporter/metric/gauge.rb +2 -6
  32. data/lib/prometheus_exporter/metric/histogram.rb +0 -2
  33. data/lib/prometheus_exporter/metric/summary.rb +5 -14
  34. data/lib/prometheus_exporter/middleware.rb +40 -32
  35. data/lib/prometheus_exporter/server/active_record_collector.rb +11 -6
  36. data/lib/prometheus_exporter/server/collector.rb +12 -16
  37. data/lib/prometheus_exporter/server/collector_base.rb +0 -2
  38. data/lib/prometheus_exporter/server/delayed_job_collector.rb +65 -28
  39. data/lib/prometheus_exporter/server/good_job_collector.rb +1 -1
  40. data/lib/prometheus_exporter/server/hutch_collector.rb +19 -11
  41. data/lib/prometheus_exporter/server/metrics_container.rb +4 -4
  42. data/lib/prometheus_exporter/server/process_collector.rb +7 -3
  43. data/lib/prometheus_exporter/server/puma_collector.rb +4 -10
  44. data/lib/prometheus_exporter/server/resque_collector.rb +1 -1
  45. data/lib/prometheus_exporter/server/runner.rb +34 -13
  46. data/lib/prometheus_exporter/server/shoryuken_collector.rb +22 -17
  47. data/lib/prometheus_exporter/server/sidekiq_collector.rb +22 -14
  48. data/lib/prometheus_exporter/server/sidekiq_process_collector.rb +9 -5
  49. data/lib/prometheus_exporter/server/sidekiq_queue_collector.rb +7 -6
  50. data/lib/prometheus_exporter/server/sidekiq_stats_collector.rb +12 -11
  51. data/lib/prometheus_exporter/server/unicorn_collector.rb +4 -4
  52. data/lib/prometheus_exporter/server/web_collector.rb +39 -22
  53. data/lib/prometheus_exporter/server/web_server.rb +10 -20
  54. data/lib/prometheus_exporter/version.rb +1 -1
  55. data/prometheus_exporter.gemspec +20 -22
  56. metadata +44 -13
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: '042907307e8e1555111f34fb69d6cf66127e7742a76130952e3708a742ada24a'
4
- data.tar.gz: 1e474618e9ddfa870a47e3ba7d7e55935677f3770d73968bb60457474b49502d
3
+ metadata.gz: e8f8c9307f755aa00e95f323d2be11cdf6830f7e420c9bf70d90260e9bc81a85
4
+ data.tar.gz: 216bedbba838f0a84391ef2e85b290aa596cf3c42d27b4cc6bce7a9fae8be01d
5
5
  SHA512:
6
- metadata.gz: '0137930945e0b254dbf0d7245d696d782044bfee9dd255ff022ffadaddafb47cdefc1aa6f83927bc84866d87fee761027590c2f08fbe730496e7099e68a883b7'
7
- data.tar.gz: 562691a91a7f4a0e380b92236f3295177481763393db7b0db9d8763f6ac631d317e6bd21bd3368471029742b8e649a1c2a3884fa2aafe93d280aa353909d9ecd
6
+ metadata.gz: 3ad534e47314779c76eb9e80d5de23619cd0865c2337147188d7f2ddba3bff9a6c9cf630e20720899c4e7ef9c95bf1db317a719d08fc241850f902aceae1b5cf
7
+ data.tar.gz: 6e71eab8cf6cdb049612c5afc404d6e399a142ee3eb5bbb3ae897e374ac6474d09f1b6c3ad94ab31f8d12fc52fc9e609a8b066051c89e44ea84be0fa0ec2cf0e
@@ -27,20 +27,24 @@ jobs:
27
27
  strategy:
28
28
  fail-fast: false
29
29
  matrix:
30
- ruby: ['3.0', '3.1', '3.2', '3.3']
31
- activerecord: [60, 61, 70]
30
+ ruby: ["3.1", "3.2", "3.3"]
31
+ activerecord: [61, 70, 71]
32
32
 
33
33
  steps:
34
- - uses: actions/checkout@v2
34
+ - uses: actions/checkout@v4
35
35
 
36
36
  - uses: ruby/setup-ruby@v1
37
37
  with:
38
38
  ruby-version: ${{ matrix.ruby }}
39
+ bundler: latest
39
40
  bundler-cache: true
40
41
 
41
42
  - name: Rubocop
42
43
  run: bundle exec rubocop
43
44
 
45
+ - name: Syntax tree
46
+ run: bundle exec stree check Gemfile $(git ls-files '*.rb') $(git ls-files '*.rake') $(git ls-files '*.thor')
47
+
44
48
  - name: Run tests
45
49
  run: bundle exec rake
46
50
 
@@ -50,10 +54,10 @@ jobs:
50
54
  runs-on: ubuntu-latest
51
55
 
52
56
  outputs:
53
- new_version_published: ${{ steps.release.outputs.new_version_published }}
57
+ new_version_published: ${{ steps.release.outputs.new_version }}
54
58
 
55
59
  steps:
56
- - uses: actions/checkout@v2
60
+ - uses: actions/checkout@v4
57
61
 
58
62
  - name: Release gem
59
63
  id: release
@@ -70,7 +74,7 @@ jobs:
70
74
  timeout-minutes: 20
71
75
 
72
76
  steps:
73
- - uses: actions/checkout@v3
77
+ - uses: actions/checkout@v4
74
78
  - uses: docker/setup-qemu-action@v2
75
79
  - uses: docker/setup-buildx-action@v2
76
80
 
data/.rubocop ADDED
@@ -0,0 +1 @@
1
+ --ignore-unrecognized-cops
data/.rubocop.yml CHANGED
@@ -4,4 +4,15 @@ inherit_gem:
4
4
  AllCops:
5
5
  Exclude:
6
6
  - 'gemfiles/**/*'
7
- - 'vendor/**/*'
7
+ - 'vendor/**/*'
8
+
9
+ Discourse/Plugins/NoMonkeyPatching:
10
+ Enabled: false
11
+
12
+ Discourse/Plugins/NamespaceMethods:
13
+ Exclude:
14
+ - bin/prometheus_exporter
15
+
16
+ Style/InvertibleUnlessCondition:
17
+ Exclude:
18
+ - '*.gemspec'
data/.streerc ADDED
@@ -0,0 +1,2 @@
1
+ --print-width=100
2
+ --plugins=plugin/trailing_comma,disable_ternary
data/CHANGELOG CHANGED
@@ -1,4 +1,15 @@
1
- 2.1.0 - 2024-08-01
1
+ 2.2.0 - 2024-12-05
2
+
3
+ - FIX: Ensure socket is closed when error is raised while opening socket
4
+ - Feature: Add Dalli::Client memcache metrics for web_collector
5
+
6
+ 2.1.1 - 2024-06-19
7
+
8
+ - FEATURE: improve good_job instrumentation
9
+ - FIX: improve Ruby 3.X support
10
+ - FEATURE: imstrumentation for malloc / oldmalloc increace in GC stats
11
+
12
+ 2.1.0 - 2024-01-08
2
13
 
3
14
  - FEATURE: good_job instrumentation
4
15
  - PERF: improve performance of histogram
data/README.md CHANGED
@@ -40,7 +40,7 @@ To learn more see [Instrumenting Rails with Prometheus](https://samsaffron.com/a
40
40
 
41
41
  ## Requirements
42
42
 
43
- Minimum Ruby of version 2.6.0 is required, Ruby 2.5.0 is EOL as of March 31st 2021.
43
+ Minimum Ruby of version 3.0.0 is required, Ruby 2.7 is EOL as of March 31st 2023.
44
44
 
45
45
  ## Migrating from v0.x
46
46
 
@@ -213,13 +213,14 @@ Rails.application.middleware.unshift PrometheusExporter::Middleware, instrument:
213
213
 
214
214
  #### Metrics collected by Rails integration middleware
215
215
 
216
- | Type | Name | Description |
217
- | --- | --- | --- |
218
- | Counter | `http_requests_total` | Total HTTP requests from web app |
219
- | Summary | `http_request_duration_seconds` | Time spent in HTTP reqs in seconds |
220
- | Summary | `http_request_redis_duration_seconds`¹ | Time spent in HTTP reqs in Redis, in seconds |
221
- | Summary | `http_request_sql_duration_seconds`² | Time spent in HTTP reqs in SQL in seconds |
222
- | Summary | `http_request_queue_duration_seconds`³ | Time spent queueing the request in load balancer in seconds |
216
+ | Type | Name | Description |
217
+ | --- | --- | --- |
218
+ | Counter | `http_requests_total` | Total HTTP requests from web app |
219
+ | Summary | `http_request_duration_seconds` | Time spent in HTTP reqs in seconds |
220
+ | Summary | `http_request_redis_duration_seconds`¹ | Time spent in HTTP reqs in Redis, in seconds |
221
+ | Summary | `http_request_sql_duration_seconds`² | Time spent in HTTP reqs in SQL in seconds |
222
+ | Summary | `http_request_queue_duration_seconds`³ | Time spent queueing the request in load balancer in seconds |
223
+ | Summary | `http_request_memcache_duration_seconds`⁴ | Time spent in HTTP reqs in Memcache in seconds |
223
224
 
224
225
  All metrics have a `controller` and an `action` label.
225
226
  `http_requests_total` additionally has a (HTTP response) `status` label.
@@ -268,6 +269,7 @@ ruby_http_request_duration_seconds{path="/api/v1/teams/:id",method="GET",status=
268
269
  ¹) Only available when Redis is used.
269
270
  ²) Only available when Mysql or PostgreSQL are used.
270
271
  ³) Only available when [Instrumenting Request Queueing Time](#instrumenting-request-queueing-time) is set up.
272
+ ⁴) Only available when Dalli is used.
271
273
 
272
274
  #### Activerecord Connection Pool Metrics
273
275
 
@@ -583,6 +585,17 @@ The puma metrics are using the `Puma.stats` method and hence need to be started
583
585
  workers has been booted and from a Puma thread otherwise the metrics won't be accessible.
584
586
  The easiest way to gather this metrics is to put the following in your `puma.rb` config:
585
587
 
588
+ For Puma single mode
589
+ ```ruby
590
+ # puma.rb config
591
+ require 'prometheus_exporter/instrumentation'
592
+ # optional check, avoids spinning up and down threads per worker
593
+ if !PrometheusExporter::Instrumentation::Puma.started?
594
+ PrometheusExporter::Instrumentation::Puma.start
595
+ end
596
+ ```
597
+
598
+ For Puma clustered mode
586
599
  ```ruby
587
600
  # puma.rb config
588
601
  after_worker_boot do
@@ -884,7 +897,7 @@ prometheus_exporter -p 8080 \
884
897
  --prefix 'foo_'
885
898
  ```
886
899
 
887
- You can use `-b` option to bind the `prometheus_exporter` web server to any IPv4 interface with `-b 0.0.0.0`,
900
+ You can use `-b` option to bind the `prometheus_exporter` web server to any IPv4 interface with `-b 0.0.0.0`,
888
901
  any IPv6 interface with `-b ::`, or `-b ANY` to any IPv4/IPv6 interfaces available on your host system.
889
902
 
890
903
  #### Enabling Basic Authentication
data/bench/bench.rb CHANGED
@@ -1,8 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative '../lib/prometheus_exporter'
4
- require_relative '../lib/prometheus_exporter/client'
5
- require_relative '../lib/prometheus_exporter/server'
3
+ require_relative "../lib/prometheus_exporter"
4
+ require_relative "../lib/prometheus_exporter/client"
5
+ require_relative "../lib/prometheus_exporter/server"
6
6
 
7
7
  # test how long it takes a custom collector to process 10k messages
8
8
 
@@ -26,18 +26,19 @@ end
26
26
  @client = nil
27
27
  @runs = 1000
28
28
 
29
- done = lambda do
30
- puts "Elapsed for 10k messages is #{Time.now - @start}"
31
- if (@runs -= 1) > 0
32
- @start = Time.now
33
- 10_000.times { @client.send_json(hello: "world") }
29
+ done =
30
+ lambda do
31
+ puts "Elapsed for 10k messages is #{Time.now - @start}"
32
+ if (@runs -= 1) > 0
33
+ @start = Time.now
34
+ 10_000.times { @client.send_json(hello: "world") }
35
+ end
34
36
  end
35
- end
36
37
 
37
38
  collector = Collector.new(done)
38
- server = PrometheusExporter::Server::WebServer.new port: 12349, collector: collector
39
+ server = PrometheusExporter::Server::WebServer.new port: 12_349, collector: collector
39
40
  server.start
40
- @client = PrometheusExporter::Client.new port: 12349, max_queue_size: 100_000
41
+ @client = PrometheusExporter::Client.new port: 12_349, max_queue_size: 100_000
41
42
 
42
43
  @start = Time.now
43
44
  10_000.times { @client.send_json(hello: "world") }
@@ -74,7 +74,7 @@ def run
74
74
  end.parse!
75
75
 
76
76
  logger = Logger.new(options[:logger_path])
77
- logger.level = Logger::WARN
77
+ logger.level = Logger::INFO
78
78
 
79
79
  if options.has_key?(:realm) && !options.has_key?(:auth)
80
80
  logger.warn "Providing REALM without AUTH has no effect"
@@ -121,7 +121,7 @@ def run
121
121
 
122
122
  runner = PrometheusExporter::Server::Runner.new(options)
123
123
 
124
- puts "#{Time.now} Starting prometheus exporter on #{runner.bind}:#{runner.port}"
124
+ logger.info "Starting prometheus exporter on #{runner.bind}:#{runner.port}"
125
125
  runner.start
126
126
  sleep
127
127
  end
@@ -20,8 +20,6 @@ class MyCustomCollector < PrometheusExporter::Server::BaseCollector
20
20
  end
21
21
 
22
22
  def prometheus_metrics_text
23
- @mutex.synchronize do
24
- "#{@gauge1.to_prometheus_text}\n#{@gauge2.to_prometheus_text}"
25
- end
23
+ @mutex.synchronize { "#{@gauge1.to_prometheus_text}\n#{@gauge2.to_prometheus_text}" }
26
24
  end
27
25
  end
@@ -2,4 +2,6 @@
2
2
 
3
3
  source "https://rubygems.org"
4
4
 
5
+ gem "activerecord", "~> 7.0.0"
6
+
5
7
  gemspec path: "../"
@@ -0,0 +1,7 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "activerecord", "~> 7.1.0"
6
+
7
+ gemspec path: "../"
@@ -17,13 +17,7 @@ module PrometheusExporter
17
17
  end
18
18
 
19
19
  def standard_values(value, keys, prometheus_exporter_action = nil)
20
- values = {
21
- type: @type,
22
- help: @help,
23
- name: @name,
24
- keys: keys,
25
- value: value
26
- }
20
+ values = { type: @type, help: @help, name: @name, keys: keys, value: value }
27
21
  values[
28
22
  :prometheus_exporter_action
29
23
  ] = prometheus_exporter_action if prometheus_exporter_action
@@ -59,16 +53,14 @@ module PrometheusExporter
59
53
 
60
54
  def initialize(
61
55
  host: ENV.fetch("PROMETHEUS_EXPORTER_HOST", "localhost"),
62
- port: ENV.fetch(
63
- "PROMETHEUS_EXPORTER_PORT",
64
- PrometheusExporter::DEFAULT_PORT
65
- ),
56
+ port: ENV.fetch("PROMETHEUS_EXPORTER_PORT", PrometheusExporter::DEFAULT_PORT),
66
57
  max_queue_size: nil,
67
58
  thread_sleep: 0.5,
68
59
  json_serializer: nil,
69
60
  custom_labels: nil,
70
61
  logger: Logger.new(STDERR),
71
- log_level: Logger::WARN
62
+ log_level: Logger::WARN,
63
+ process_queue_once_and_stop: false
72
64
  )
73
65
  @logger = logger
74
66
  @logger.level = log_level
@@ -83,9 +75,7 @@ module PrometheusExporter
83
75
  max_queue_size ||= MAX_QUEUE_SIZE
84
76
  max_queue_size = max_queue_size.to_i
85
77
 
86
- if max_queue_size <= 0
87
- raise ArgumentError, "max_queue_size must be larger than 0"
88
- end
78
+ raise ArgumentError, "max_queue_size must be larger than 0" if max_queue_size <= 0
89
79
 
90
80
  @max_queue_size = max_queue_size
91
81
  @host = host
@@ -94,10 +84,10 @@ module PrometheusExporter
94
84
  @mutex = Mutex.new
95
85
  @thread_sleep = thread_sleep
96
86
 
97
- @json_serializer =
98
- json_serializer == :oj ? PrometheusExporter::OjCompat : JSON
87
+ @json_serializer = json_serializer == :oj ? PrometheusExporter::OjCompat : JSON
99
88
 
100
89
  @custom_labels = custom_labels
90
+ @process_queue_once_and_stop = process_queue_once_and_stop
101
91
  end
102
92
 
103
93
  def custom_labels=(custom_labels)
@@ -105,14 +95,7 @@ module PrometheusExporter
105
95
  end
106
96
 
107
97
  def register(type, name, help, opts = nil)
108
- metric =
109
- RemoteMetric.new(
110
- type: type,
111
- name: name,
112
- help: help,
113
- client: self,
114
- opts: opts
115
- )
98
+ metric = RemoteMetric.new(type: type, name: name, help: help, client: self, opts: opts)
116
99
  @metrics << metric
117
100
  metric
118
101
  end
@@ -163,7 +146,7 @@ module PrometheusExporter
163
146
  @socket.write("\r\n")
164
147
  rescue => e
165
148
  logger.warn "Prometheus Exporter is dropping a message: #{e}"
166
- @socket = nil
149
+ close_socket!
167
150
  raise
168
151
  end
169
152
  end
@@ -189,6 +172,11 @@ module PrometheusExporter
189
172
  end
190
173
 
191
174
  def ensure_worker_thread!
175
+ if @process_queue_once_and_stop
176
+ worker_loop
177
+ return
178
+ end
179
+
192
180
  unless @worker_thread&.alive?
193
181
  @mutex.synchronize do
194
182
  return if @worker_thread&.alive?
@@ -253,8 +241,7 @@ module PrometheusExporter
253
241
 
254
242
  nil
255
243
  rescue StandardError
256
- @socket = nil
257
- @socket_started = nil
244
+ close_socket!
258
245
  @socket_pid = nil
259
246
  raise
260
247
  end
@@ -262,10 +249,7 @@ module PrometheusExporter
262
249
  def wait_for_empty_queue_with_timeout(timeout_seconds)
263
250
  start_time = ::Process.clock_gettime(::Process::CLOCK_MONOTONIC)
264
251
  while @queue.length > 0
265
- if start_time + timeout_seconds <
266
- ::Process.clock_gettime(::Process::CLOCK_MONOTONIC)
267
- break
268
- end
252
+ break if start_time + timeout_seconds < ::Process.clock_gettime(::Process::CLOCK_MONOTONIC)
269
253
  sleep(0.05)
270
254
  end
271
255
  end
@@ -3,14 +3,16 @@
3
3
  # collects stats from currently running process
4
4
  module PrometheusExporter::Instrumentation
5
5
  class ActiveRecord < PeriodicStats
6
- ALLOWED_CONFIG_LABELS = %i(database username host port)
6
+ ALLOWED_CONFIG_LABELS = %i[database username host port]
7
7
 
8
8
  def self.start(client: nil, frequency: 30, custom_labels: {}, config_labels: [])
9
9
  client ||= PrometheusExporter::Client.default
10
10
 
11
11
  # Not all rails versions support connection pool stats
12
12
  unless ::ActiveRecord::Base.connection_pool.respond_to?(:stat)
13
- client.logger.error("ActiveRecord connection pool stats not supported in your rails version")
13
+ client.logger.error(
14
+ "ActiveRecord connection pool stats not supported in your rails version",
15
+ )
14
16
  return
15
17
  end
16
18
 
@@ -29,7 +31,9 @@ module PrometheusExporter::Instrumentation
29
31
 
30
32
  def self.validate_config_labels(config_labels)
31
33
  return if config_labels.size == 0
32
- raise "Invalid Config Labels, available options #{ALLOWED_CONFIG_LABELS}" if (config_labels - ALLOWED_CONFIG_LABELS).size > 0
34
+ if (config_labels - ALLOWED_CONFIG_LABELS).size > 0
35
+ raise "Invalid Config Labels, available options #{ALLOWED_CONFIG_LABELS}"
36
+ end
33
37
  end
34
38
 
35
39
  def initialize(metric_labels, config_labels)
@@ -55,7 +59,7 @@ module PrometheusExporter::Instrumentation
55
59
  pid: pid,
56
60
  type: "active_record",
57
61
  hostname: ::PrometheusExporter.hostname,
58
- metric_labels: labels(pool)
62
+ metric_labels: labels(pool),
59
63
  }
60
64
  metric.merge!(pool.stat)
61
65
  metrics << metric
@@ -66,12 +70,20 @@ module PrometheusExporter::Instrumentation
66
70
 
67
71
  def labels(pool)
68
72
  if ::ActiveRecord.version < Gem::Version.new("6.1.0.rc1")
69
- @metric_labels.merge(pool_name: pool.spec.name).merge(pool.spec.config
70
- .select { |k, v| @config_labels.include? k }
71
- .map { |k, v| [k.to_s.dup.prepend("dbconfig_"), v] }.to_h)
73
+ @metric_labels.merge(pool_name: pool.spec.name).merge(
74
+ pool
75
+ .spec
76
+ .config
77
+ .select { |k, v| @config_labels.include? k }
78
+ .map { |k, v| [k.to_s.dup.prepend("dbconfig_"), v] }
79
+ .to_h,
80
+ )
72
81
  else
73
82
  @metric_labels.merge(pool_name: pool.db_config.name).merge(
74
- @config_labels.each_with_object({}) { |l, acc| acc["dbconfig_#{l}"] = pool.db_config.public_send(l) })
83
+ @config_labels.each_with_object({}) do |l, acc|
84
+ acc["dbconfig_#{l}"] = pool.db_config.public_send(l)
85
+ end,
86
+ )
75
87
  end
76
88
  end
77
89
  end
@@ -2,23 +2,33 @@
2
2
 
3
3
  module PrometheusExporter::Instrumentation
4
4
  class DelayedJob
5
- JOB_CLASS_REGEXP = %r{job_class: (\w+:{0,2})+}.freeze
5
+ JOB_CLASS_REGEXP = /job_class: ((\w+:{0,2})+)/.freeze
6
6
 
7
7
  class << self
8
- def register_plugin(client: nil)
8
+ def register_plugin(client: nil, include_module_name: false)
9
9
  instrumenter = self.new(client: client)
10
10
  return unless defined?(Delayed::Plugin)
11
11
 
12
- plugin = Class.new(Delayed::Plugin) do
13
- callbacks do |lifecycle|
14
- lifecycle.around(:invoke_job) do |job, *args, &block|
15
- max_attempts = Delayed::Worker.max_attempts
16
- enqueued_count = Delayed::Job.where(queue: job.queue).count
17
- pending_count = Delayed::Job.where(attempts: 0, locked_at: nil, queue: job.queue).count
18
- instrumenter.call(job, max_attempts, enqueued_count, pending_count, *args, &block)
12
+ plugin =
13
+ Class.new(Delayed::Plugin) do
14
+ callbacks do |lifecycle|
15
+ lifecycle.around(:invoke_job) do |job, *args, &block|
16
+ max_attempts = Delayed::Worker.max_attempts
17
+ enqueued_count = Delayed::Job.where(queue: job.queue).count
18
+ pending_count =
19
+ Delayed::Job.where(attempts: 0, locked_at: nil, queue: job.queue).count
20
+ instrumenter.call(
21
+ job,
22
+ max_attempts,
23
+ enqueued_count,
24
+ pending_count,
25
+ include_module_name,
26
+ *args,
27
+ &block
28
+ )
29
+ end
19
30
  end
20
31
  end
21
- end
22
32
 
23
33
  Delayed::Worker.plugins << plugin
24
34
  end
@@ -28,7 +38,7 @@ module PrometheusExporter::Instrumentation
28
38
  @client = client || PrometheusExporter::Client.default
29
39
  end
30
40
 
31
- def call(job, max_attempts, enqueued_count, pending_count, *args, &block)
41
+ def call(job, max_attempts, enqueued_count, pending_count, include_module_name, *args, &block)
32
42
  success = false
33
43
  start = ::Process.clock_gettime(::Process::CLOCK_MONOTONIC)
34
44
  latency = Time.current - job.run_at
@@ -41,7 +51,7 @@ module PrometheusExporter::Instrumentation
41
51
 
42
52
  @client.send_json(
43
53
  type: "delayed_job",
44
- name: job.handler.to_s.match(JOB_CLASS_REGEXP).to_a[1].to_s,
54
+ name: job.handler.to_s.match(JOB_CLASS_REGEXP).to_a[include_module_name ? 1 : 2].to_s,
45
55
  queue_name: job.queue,
46
56
  success: success,
47
57
  duration: duration,
@@ -49,7 +59,7 @@ module PrometheusExporter::Instrumentation
49
59
  attempts: attempts,
50
60
  max_attempts: max_attempts,
51
61
  enqueued: enqueued_count,
52
- pending: pending_count
62
+ pending: pending_count,
53
63
  )
54
64
  end
55
65
  end
@@ -7,9 +7,7 @@ module PrometheusExporter::Instrumentation
7
7
  good_job_collector = new
8
8
  client ||= PrometheusExporter::Client.default
9
9
 
10
- worker_loop do
11
- client.send_json(good_job_collector.collect)
12
- end
10
+ worker_loop { client.send_json(good_job_collector.collect) }
13
11
 
14
12
  super
15
13
  end
@@ -23,7 +21,7 @@ module PrometheusExporter::Instrumentation
23
21
  running: ::GoodJob::Job.running.size,
24
22
  finished: ::GoodJob::Job.finished.size,
25
23
  succeeded: ::GoodJob::Job.succeeded.size,
26
- discarded: ::GoodJob::Job.discarded.size
24
+ discarded: ::GoodJob::Job.discarded.size,
27
25
  }
28
26
  end
29
27
  end
@@ -19,7 +19,7 @@ module PrometheusExporter::Instrumentation
19
19
  type: "hutch",
20
20
  name: @klass.class.to_s,
21
21
  success: success,
22
- duration: duration
22
+ duration: duration,
23
23
  )
24
24
  end
25
25
  end
@@ -1,7 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  # see https://samsaffron.com/archive/2017/10/18/fastest-way-to-profile-a-method-in-ruby
4
- module PrometheusExporter::Instrumentation; end
4
+ module PrometheusExporter::Instrumentation
5
+ end
5
6
 
6
7
  class PrometheusExporter::Instrumentation::MethodProfiler
7
8
  def self.patch(klass, methods, name, instrument:)
@@ -21,9 +22,8 @@ class PrometheusExporter::Instrumentation::MethodProfiler
21
22
  end
22
23
 
23
24
  def self.start(transfer = nil)
24
- Thread.current[:_method_profiler] = transfer || {
25
- __start: Process.clock_gettime(Process::CLOCK_MONOTONIC)
26
- }
25
+ Thread.current[:_method_profiler] = transfer ||
26
+ { __start: Process.clock_gettime(Process::CLOCK_MONOTONIC) }
27
27
  end
28
28
 
29
29
  def self.clear
@@ -42,9 +42,9 @@ class PrometheusExporter::Instrumentation::MethodProfiler
42
42
 
43
43
  def self.define_methods_on_module(klass, methods, name)
44
44
  patch_source_line = __LINE__ + 3
45
- patches = methods.map do |method_name|
46
- <<~RUBY
47
- def #{method_name}(*args, &blk)
45
+
46
+ patches = methods.map { |method_name| <<~RUBY }.join("\n")
47
+ def #{method_name}(...)
48
48
  unless prof = Thread.current[:_method_profiler]
49
49
  return super
50
50
  end
@@ -58,9 +58,8 @@ class PrometheusExporter::Instrumentation::MethodProfiler
58
58
  end
59
59
  end
60
60
  RUBY
61
- end.join("\n")
62
61
 
63
- klass.module_eval patches, __FILE__, patch_source_line
62
+ klass.module_eval(patches, __FILE__, patch_source_line)
64
63
  end
65
64
 
66
65
  def self.patch_using_prepend(klass, methods, name)
@@ -71,17 +70,19 @@ class PrometheusExporter::Instrumentation::MethodProfiler
71
70
 
72
71
  def self.patch_using_alias_method(klass, methods, name)
73
72
  patch_source_line = __LINE__ + 3
74
- patches = methods.map do |method_name|
75
- <<~RUBY
73
+
74
+ patches = methods.map { |method_name| <<~RUBY }.join("\n")
76
75
  unless defined?(#{method_name}__mp_unpatched)
77
76
  alias_method :#{method_name}__mp_unpatched, :#{method_name}
78
- def #{method_name}(*args, &blk)
77
+
78
+ def #{method_name}(...)
79
79
  unless prof = Thread.current[:_method_profiler]
80
- return #{method_name}__mp_unpatched(*args, &blk)
80
+ return #{method_name}__mp_unpatched(...)
81
81
  end
82
+
82
83
  begin
83
84
  start = Process.clock_gettime(Process::CLOCK_MONOTONIC)
84
- #{method_name}__mp_unpatched(*args, &blk)
85
+ #{method_name}__mp_unpatched(...)
85
86
  ensure
86
87
  data = (prof[:#{name}] ||= {duration: 0.0, calls: 0})
87
88
  data[:duration] += Process.clock_gettime(Process::CLOCK_MONOTONIC) - start
@@ -90,8 +91,7 @@ class PrometheusExporter::Instrumentation::MethodProfiler
90
91
  end
91
92
  end
92
93
  RUBY
93
- end.join("\n")
94
94
 
95
- klass.class_eval patches, __FILE__, patch_source_line
95
+ klass.class_eval(patches, __FILE__, patch_source_line)
96
96
  end
97
97
  end