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.
- checksums.yaml +4 -4
- data/.github/workflows/ci.yml +10 -6
- data/.rubocop +1 -0
- data/.rubocop.yml +12 -1
- data/.streerc +2 -0
- data/CHANGELOG +12 -1
- data/README.md +22 -9
- data/bench/bench.rb +12 -11
- data/bin/prometheus_exporter +2 -2
- data/examples/custom_collector.rb +1 -3
- data/gemfiles/ar_70.gemfile +2 -0
- data/gemfiles/ar_71.gemfile +7 -0
- data/lib/prometheus_exporter/client.rb +16 -32
- data/lib/prometheus_exporter/instrumentation/active_record.rb +20 -8
- data/lib/prometheus_exporter/instrumentation/delayed_job.rb +23 -13
- data/lib/prometheus_exporter/instrumentation/good_job.rb +2 -4
- data/lib/prometheus_exporter/instrumentation/hutch.rb +1 -1
- data/lib/prometheus_exporter/instrumentation/method_profiler.rb +16 -16
- data/lib/prometheus_exporter/instrumentation/periodic_stats.rb +13 -21
- data/lib/prometheus_exporter/instrumentation/process.rb +14 -6
- data/lib/prometheus_exporter/instrumentation/puma.rb +1 -1
- data/lib/prometheus_exporter/instrumentation/resque.rb +1 -3
- data/lib/prometheus_exporter/instrumentation/shoryuken.rb +6 -7
- data/lib/prometheus_exporter/instrumentation/sidekiq.rb +4 -6
- data/lib/prometheus_exporter/instrumentation/sidekiq_process.rb +12 -19
- data/lib/prometheus_exporter/instrumentation/sidekiq_queue.rb +15 -18
- data/lib/prometheus_exporter/instrumentation/sidekiq_stats.rb +10 -15
- data/lib/prometheus_exporter/instrumentation/unicorn.rb +2 -2
- data/lib/prometheus_exporter/metric/base.rb +8 -7
- data/lib/prometheus_exporter/metric/counter.rb +1 -3
- data/lib/prometheus_exporter/metric/gauge.rb +2 -6
- data/lib/prometheus_exporter/metric/histogram.rb +0 -2
- data/lib/prometheus_exporter/metric/summary.rb +5 -14
- data/lib/prometheus_exporter/middleware.rb +40 -32
- data/lib/prometheus_exporter/server/active_record_collector.rb +11 -6
- data/lib/prometheus_exporter/server/collector.rb +12 -16
- data/lib/prometheus_exporter/server/collector_base.rb +0 -2
- data/lib/prometheus_exporter/server/delayed_job_collector.rb +65 -28
- data/lib/prometheus_exporter/server/good_job_collector.rb +1 -1
- data/lib/prometheus_exporter/server/hutch_collector.rb +19 -11
- data/lib/prometheus_exporter/server/metrics_container.rb +4 -4
- data/lib/prometheus_exporter/server/process_collector.rb +7 -3
- data/lib/prometheus_exporter/server/puma_collector.rb +4 -10
- data/lib/prometheus_exporter/server/resque_collector.rb +1 -1
- data/lib/prometheus_exporter/server/runner.rb +34 -13
- data/lib/prometheus_exporter/server/shoryuken_collector.rb +22 -17
- data/lib/prometheus_exporter/server/sidekiq_collector.rb +22 -14
- data/lib/prometheus_exporter/server/sidekiq_process_collector.rb +9 -5
- data/lib/prometheus_exporter/server/sidekiq_queue_collector.rb +7 -6
- data/lib/prometheus_exporter/server/sidekiq_stats_collector.rb +12 -11
- data/lib/prometheus_exporter/server/unicorn_collector.rb +4 -4
- data/lib/prometheus_exporter/server/web_collector.rb +39 -22
- data/lib/prometheus_exporter/server/web_server.rb +10 -20
- data/lib/prometheus_exporter/version.rb +1 -1
- data/prometheus_exporter.gemspec +20 -22
- metadata +44 -13
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e8f8c9307f755aa00e95f323d2be11cdf6830f7e420c9bf70d90260e9bc81a85
|
4
|
+
data.tar.gz: 216bedbba838f0a84391ef2e85b290aa596cf3c42d27b4cc6bce7a9fae8be01d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3ad534e47314779c76eb9e80d5de23619cd0865c2337147188d7f2ddba3bff9a6c9cf630e20720899c4e7ef9c95bf1db317a719d08fc241850f902aceae1b5cf
|
7
|
+
data.tar.gz: 6e71eab8cf6cdb049612c5afc404d6e399a142ee3eb5bbb3ae897e374ac6474d09f1b6c3ad94ab31f8d12fc52fc9e609a8b066051c89e44ea84be0fa0ec2cf0e
|
data/.github/workflows/ci.yml
CHANGED
@@ -27,20 +27,24 @@ jobs:
|
|
27
27
|
strategy:
|
28
28
|
fail-fast: false
|
29
29
|
matrix:
|
30
|
-
ruby: [
|
31
|
-
activerecord: [
|
30
|
+
ruby: ["3.1", "3.2", "3.3"]
|
31
|
+
activerecord: [61, 70, 71]
|
32
32
|
|
33
33
|
steps:
|
34
|
-
- uses: actions/checkout@
|
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.
|
57
|
+
new_version_published: ${{ steps.release.outputs.new_version }}
|
54
58
|
|
55
59
|
steps:
|
56
|
-
- uses: actions/checkout@
|
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@
|
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
data/CHANGELOG
CHANGED
@@ -1,4 +1,15 @@
|
|
1
|
-
2.
|
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
|
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
|
217
|
-
| --- | ---
|
218
|
-
| Counter | `http_requests_total`
|
219
|
-
| Summary | `http_request_duration_seconds`
|
220
|
-
| Summary | `http_request_redis_duration_seconds`¹
|
221
|
-
| Summary | `http_request_sql_duration_seconds`²
|
222
|
-
| Summary | `http_request_queue_duration_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
|
4
|
-
require_relative
|
5
|
-
require_relative
|
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 =
|
30
|
-
|
31
|
-
|
32
|
-
@
|
33
|
-
|
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:
|
39
|
+
server = PrometheusExporter::Server::WebServer.new port: 12_349, collector: collector
|
39
40
|
server.start
|
40
|
-
@client = PrometheusExporter::Client.new port:
|
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") }
|
data/bin/prometheus_exporter
CHANGED
@@ -74,7 +74,7 @@ def run
|
|
74
74
|
end.parse!
|
75
75
|
|
76
76
|
logger = Logger.new(options[:logger_path])
|
77
|
-
logger.level = Logger::
|
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
|
-
|
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
|
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
|
data/gemfiles/ar_70.gemfile
CHANGED
@@ -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
|
-
|
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
|
-
|
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
|
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(
|
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
|
-
|
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(
|
70
|
-
|
71
|
-
|
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({})
|
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 =
|
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 =
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
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
|
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
|
@@ -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
|
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
|
-
|
46
|
-
|
47
|
-
def #{method_name}(
|
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
|
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
|
-
|
75
|
-
|
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
|
-
|
77
|
+
|
78
|
+
def #{method_name}(...)
|
79
79
|
unless prof = Thread.current[:_method_profiler]
|
80
|
-
return #{method_name}__mp_unpatched(
|
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(
|
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
|
95
|
+
klass.class_eval(patches, __FILE__, patch_source_line)
|
96
96
|
end
|
97
97
|
end
|