prometheus_exporter 2.1.0 → 2.2.0

Sign up to get free protection for your applications and to get access to all the features.
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