prometheus_exporter 2.0.8 → 2.1.1
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 +51 -4
- data/.rubocop +1 -0
- data/.rubocop.yml +12 -1
- data/Appraisals +6 -2
- data/CHANGELOG +12 -0
- data/README.md +45 -8
- data/bin/prometheus_exporter +2 -2
- data/gemfiles/ar_70.gemfile +7 -0
- data/gemfiles/ar_71.gemfile +7 -0
- data/lib/prometheus_exporter/client.rb +34 -19
- data/lib/prometheus_exporter/instrumentation/delayed_job.rb +8 -5
- data/lib/prometheus_exporter/instrumentation/good_job.rb +30 -0
- data/lib/prometheus_exporter/instrumentation/method_profiler.rb +4 -4
- data/lib/prometheus_exporter/instrumentation/process.rb +2 -0
- data/lib/prometheus_exporter/instrumentation/sidekiq.rb +16 -14
- data/lib/prometheus_exporter/instrumentation.rb +1 -0
- data/lib/prometheus_exporter/metric/histogram.rb +1 -1
- data/lib/prometheus_exporter/middleware.rb +1 -1
- data/lib/prometheus_exporter/server/active_record_collector.rb +9 -13
- data/lib/prometheus_exporter/server/collector.rb +1 -0
- data/lib/prometheus_exporter/server/delayed_job_collector.rb +7 -1
- data/lib/prometheus_exporter/server/good_job_collector.rb +52 -0
- data/lib/prometheus_exporter/server/process_collector.rb +10 -13
- data/lib/prometheus_exporter/server/puma_collector.rb +1 -1
- data/lib/prometheus_exporter/server/resque_collector.rb +3 -7
- data/lib/prometheus_exporter/server/sidekiq_process_collector.rb +2 -2
- data/lib/prometheus_exporter/server/sidekiq_queue_collector.rb +2 -2
- data/lib/prometheus_exporter/server/sidekiq_stats_collector.rb +2 -2
- data/lib/prometheus_exporter/server/unicorn_collector.rb +32 -33
- data/lib/prometheus_exporter/server/web_server.rb +65 -39
- data/lib/prometheus_exporter/server.rb +1 -0
- data/lib/prometheus_exporter/version.rb +1 -1
- data/lib/prometheus_exporter.rb +12 -13
- data/prometheus_exporter.gemspec +5 -7
- metadata +19 -16
- data/.github/workflows/docker.yml +0 -47
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e5381fb78e6440d77a61269f3bf7c40a84b44a53594d687db4a683be3cb82725
|
4
|
+
data.tar.gz: 18216fbeae6978d5427fa9f97a67bbf3f0ee36e92091a2606b2dadcaa6497d83
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 626d1f7c05e5bf21bc499e69fd1ab0d73c94c5a565ac7eae81ec9d43fc93f6cf96b83a6fd72b25846e49371079a9c87a497e719d74c20dfa57c0fb5cbe4507e5
|
7
|
+
data.tar.gz: b43a57442d6326648cbc3539c50400556575e425756358d45ad39832e099671ca617ea66b6754d16d66693b9fbdf9045b96a95713b36f1f135ab8ab0e38a6d0b
|
data/.github/workflows/ci.yml
CHANGED
@@ -8,6 +8,13 @@ on:
|
|
8
8
|
schedule:
|
9
9
|
- cron: "0 0 * * 0" # weekly
|
10
10
|
|
11
|
+
permissions:
|
12
|
+
contents: write
|
13
|
+
packages: write
|
14
|
+
|
15
|
+
env:
|
16
|
+
DOCKER_REPO: ghcr.io/discourse/prometheus_exporter
|
17
|
+
|
11
18
|
jobs:
|
12
19
|
build:
|
13
20
|
runs-on: ubuntu-latest
|
@@ -20,15 +27,16 @@ jobs:
|
|
20
27
|
strategy:
|
21
28
|
fail-fast: false
|
22
29
|
matrix:
|
23
|
-
ruby: ['
|
24
|
-
activerecord: [
|
30
|
+
ruby: ['3.1', '3.2', '3.3']
|
31
|
+
activerecord: [61, 70, 71]
|
25
32
|
|
26
33
|
steps:
|
27
|
-
- uses: actions/checkout@
|
34
|
+
- uses: actions/checkout@v4
|
28
35
|
|
29
36
|
- uses: ruby/setup-ruby@v1
|
30
37
|
with:
|
31
38
|
ruby-version: ${{ matrix.ruby }}
|
39
|
+
bundler: latest
|
32
40
|
bundler-cache: true
|
33
41
|
|
34
42
|
- name: Rubocop
|
@@ -42,12 +50,51 @@ jobs:
|
|
42
50
|
needs: build
|
43
51
|
runs-on: ubuntu-latest
|
44
52
|
|
53
|
+
outputs:
|
54
|
+
new_version_published: ${{ steps.release.outputs.new_version }}
|
55
|
+
|
45
56
|
steps:
|
46
|
-
- uses: actions/checkout@
|
57
|
+
- uses: actions/checkout@v4
|
47
58
|
|
48
59
|
- name: Release gem
|
60
|
+
id: release
|
49
61
|
uses: discourse/publish-rubygems-action@v2
|
50
62
|
env:
|
51
63
|
RUBYGEMS_API_KEY: ${{ secrets.RUBYGEMS_API_KEY }}
|
52
64
|
GIT_EMAIL: team@discourse.org
|
53
65
|
GIT_NAME: discoursebot
|
66
|
+
|
67
|
+
publish_docker:
|
68
|
+
needs: publish
|
69
|
+
if: needs.publish.outputs.new_version_published == 'true'
|
70
|
+
runs-on: ubuntu-latest
|
71
|
+
timeout-minutes: 20
|
72
|
+
|
73
|
+
steps:
|
74
|
+
- uses: actions/checkout@v4
|
75
|
+
- uses: docker/setup-qemu-action@v2
|
76
|
+
- uses: docker/setup-buildx-action@v2
|
77
|
+
|
78
|
+
- name: Set vars
|
79
|
+
id: vars
|
80
|
+
run: |
|
81
|
+
ruby -r ./lib/prometheus_exporter/version.rb -e 'print "version=#{PrometheusExporter::VERSION}"' >> $GITHUB_OUTPUT
|
82
|
+
|
83
|
+
- name: Login to Github Container Registry
|
84
|
+
uses: docker/login-action@v2
|
85
|
+
with:
|
86
|
+
registry: ghcr.io
|
87
|
+
username: ${{ github.actor }}
|
88
|
+
password: ${{ secrets.GITHUB_TOKEN }}
|
89
|
+
|
90
|
+
- name: Build and push images
|
91
|
+
uses: docker/build-push-action@v3
|
92
|
+
with:
|
93
|
+
context: .
|
94
|
+
push: true
|
95
|
+
platforms: linux/amd64,linux/arm64
|
96
|
+
build-args: |
|
97
|
+
GEM_VERSION=${{ steps.vars.outputs.version }}
|
98
|
+
tags: |
|
99
|
+
${{ env.DOCKER_REPO }}:${{ steps.vars.outputs.version }}
|
100
|
+
${{ env.DOCKER_REPO }}:latest
|
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/Appraisals
CHANGED
@@ -1,10 +1,14 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
appraise "ar-60" do
|
4
|
-
|
5
|
-
# gem "activerecord", "~> 6.0.0"
|
4
|
+
gem "activerecord", "~> 6.0.0"
|
6
5
|
end
|
7
6
|
|
8
7
|
appraise "ar-61" do
|
9
8
|
gem "activerecord", "~> 6.1.1"
|
10
9
|
end
|
10
|
+
|
11
|
+
appraise "ar-70" do
|
12
|
+
# latest version
|
13
|
+
gem "activerecord", "~> 7.1.2"
|
14
|
+
end
|
data/CHANGELOG
CHANGED
@@ -1,3 +1,15 @@
|
|
1
|
+
2.1.1 - 2024-06-19
|
2
|
+
|
3
|
+
- FEATURE: improve good_job instrumentation
|
4
|
+
- FIX: improve Ruby 3.X support
|
5
|
+
- FEATURE: imstrumentation for malloc / oldmalloc increace in GC stats
|
6
|
+
|
7
|
+
2.1.0 - 2024-01-08
|
8
|
+
|
9
|
+
- FEATURE: good_job instrumentation
|
10
|
+
- PERF: improve performance of histogram
|
11
|
+
- DEV: use new metric collector pattern so we reuse code between collectors
|
12
|
+
|
1
13
|
2.0.8 - 2023-01-20
|
2
14
|
|
3
15
|
- FEATURE: attempting to make our first docker release
|
data/README.md
CHANGED
@@ -21,6 +21,7 @@ To learn more see [Instrumenting Rails with Prometheus](https://samsaffron.com/a
|
|
21
21
|
* [Puma metrics](#puma-metrics)
|
22
22
|
* [Unicorn metrics](#unicorn-process-metrics)
|
23
23
|
* [Resque metrics](#resque-metrics)
|
24
|
+
* [GoodJob metrics](#goodjob-metrics)
|
24
25
|
* [Custom type collectors](#custom-type-collectors)
|
25
26
|
* [Multi process mode with custom collector](#multi-process-mode-with-custom-collector)
|
26
27
|
* [GraphQL support](#graphql-support)
|
@@ -28,7 +29,7 @@ To learn more see [Instrumenting Rails with Prometheus](https://samsaffron.com/a
|
|
28
29
|
* [Client default labels](#client-default-labels)
|
29
30
|
* [Client default host](#client-default-host)
|
30
31
|
* [Histogram mode](#histogram-mode)
|
31
|
-
* [Histogram - custom buckets](#histogram
|
32
|
+
* [Histogram - custom buckets](#histogram---custom-buckets)
|
32
33
|
* [Transport concerns](#transport-concerns)
|
33
34
|
* [JSON generation and parsing](#json-generation-and-parsing)
|
34
35
|
* [Logging](#logging)
|
@@ -39,7 +40,7 @@ To learn more see [Instrumenting Rails with Prometheus](https://samsaffron.com/a
|
|
39
40
|
|
40
41
|
## Requirements
|
41
42
|
|
42
|
-
Minimum Ruby of version
|
43
|
+
Minimum Ruby of version 3.0.0 is required, Ruby 2.7 is EOL as of March 31st 2023.
|
43
44
|
|
44
45
|
## Migrating from v0.x
|
45
46
|
|
@@ -188,7 +189,7 @@ gem 'prometheus_exporter'
|
|
188
189
|
In an initializer:
|
189
190
|
|
190
191
|
```ruby
|
191
|
-
unless Rails.env
|
192
|
+
unless Rails.env.test?
|
192
193
|
require 'prometheus_exporter/middleware'
|
193
194
|
|
194
195
|
# This reports stats per request like HTTP status and timings
|
@@ -341,7 +342,7 @@ You may also be interested in per-process stats. This collects memory and GC sta
|
|
341
342
|
|
342
343
|
```ruby
|
343
344
|
# in an initializer
|
344
|
-
unless Rails.env
|
345
|
+
unless Rails.env.test?
|
345
346
|
require 'prometheus_exporter/instrumentation'
|
346
347
|
|
347
348
|
# this reports basic process stats like RSS and GC info
|
@@ -522,7 +523,7 @@ All metrics have labels for `job_name` and `queue_name`.
|
|
522
523
|
In an initializer:
|
523
524
|
|
524
525
|
```ruby
|
525
|
-
unless Rails.env
|
526
|
+
unless Rails.env.test?
|
526
527
|
require 'prometheus_exporter/instrumentation'
|
527
528
|
PrometheusExporter::Instrumentation::DelayedJob.register_plugin
|
528
529
|
end
|
@@ -533,6 +534,7 @@ end
|
|
533
534
|
| Type | Name | Description | Labels |
|
534
535
|
| --- | --- | --- | --- |
|
535
536
|
| Counter | `delayed_job_duration_seconds` | Total time spent in delayed jobs | `job_name` |
|
537
|
+
| Counter | `delayed_job_latency_seconds_total` | Total delayed jobs latency | `job_name` |
|
536
538
|
| Counter | `delayed_jobs_total` | Total number of delayed jobs executed | `job_name` |
|
537
539
|
| Gauge | `delayed_jobs_enqueued` | Number of enqueued delayed jobs | - |
|
538
540
|
| Gauge | `delayed_jobs_pending` | Number of pending delayed jobs | - |
|
@@ -542,13 +544,14 @@ end
|
|
542
544
|
| Summary | `delayed_job_attempts_summary` | Summary of the amount of attempts it takes delayed jobs to succeed | - |
|
543
545
|
|
544
546
|
All metrics have labels for `job_name` and `queue_name`.
|
547
|
+
`delayed_job_latency_seconds_total` is considering delayed job's [sleep_delay](https://github.com/collectiveidea/delayed_job#:~:text=If%20no%20jobs%20are%20found%2C%20the%20worker%20sleeps%20for%20the%20amount%20of%20time%20specified%20by%20the%20sleep%20delay%20option.%20Set%20Delayed%3A%3AWorker.sleep_delay%20%3D%2060%20for%20a%2060%20second%20sleep%20time.) parameter, so please be aware of this in case you are looking for high latency precision.
|
545
548
|
|
546
549
|
#### Hutch Message Processing Tracer
|
547
550
|
|
548
551
|
Capture [Hutch](https://github.com/gocardless/hutch) metrics (how many jobs ran? how many failed? how long did they take?)
|
549
552
|
|
550
553
|
```ruby
|
551
|
-
unless Rails.env
|
554
|
+
unless Rails.env.test?
|
552
555
|
require 'prometheus_exporter/instrumentation'
|
553
556
|
Hutch::Config.set(:tracer, PrometheusExporter::Instrumentation::Hutch)
|
554
557
|
end
|
@@ -570,7 +573,7 @@ Request Queueing is defined as the time it takes for a request to reach your app
|
|
570
573
|
|
571
574
|
As this metric starts before `prometheus_exporter` can handle the request, you must add a specific HTTP header as early in your infrastructure as possible (we recommend your load balancer or reverse proxy).
|
572
575
|
|
573
|
-
The Amazon Application Load Balancer [request tracing header](https://docs.aws.amazon.com/elasticloadbalancing/latest/application/load-balancer-request-tracing.html) is natively supported. If you are using another upstream entrypoint, you may configure your HTTP server / load balancer to add a header `X-Request-Start: t=<MSEC>` when passing the request upstream. For more information, please consult your software manual.
|
576
|
+
The Amazon Application Load Balancer [request tracing header](https://docs.aws.amazon.com/elasticloadbalancing/latest/application/load-balancer-request-tracing.html) is natively supported. If you are using another upstream entrypoint, you may configure your HTTP server / load balancer to add a header `X-Request-Start: t=<MSEC>` when passing the request upstream. Please keep in mind request time start is reported as epoch time (in seconds) and lacks precision, which may introduce additional latency in reported metrics. For more information, please consult your software manual.
|
574
577
|
|
575
578
|
Hint: we aim to be API-compatible with the big APM solutions, so if you've got requests queueing time configured for them, it should be expected to also work with `prometheus_exporter`.
|
576
579
|
|
@@ -580,6 +583,17 @@ The puma metrics are using the `Puma.stats` method and hence need to be started
|
|
580
583
|
workers has been booted and from a Puma thread otherwise the metrics won't be accessible.
|
581
584
|
The easiest way to gather this metrics is to put the following in your `puma.rb` config:
|
582
585
|
|
586
|
+
For Puma single mode
|
587
|
+
```ruby
|
588
|
+
# puma.rb config
|
589
|
+
require 'prometheus_exporter/instrumentation'
|
590
|
+
# optional check, avoids spinning up and down threads per worker
|
591
|
+
if !PrometheusExporter::Instrumentation::Puma.started?
|
592
|
+
PrometheusExporter::Instrumentation::Puma.start
|
593
|
+
end
|
594
|
+
```
|
595
|
+
|
596
|
+
For Puma clustered mode
|
583
597
|
```ruby
|
584
598
|
# puma.rb config
|
585
599
|
after_worker_boot do
|
@@ -627,6 +641,29 @@ PrometheusExporter::Instrumentation::Resque.start
|
|
627
641
|
| Gauge | `resque_workers` | Total number of Resque workers running |
|
628
642
|
| Gauge | `resque_working` | Total number of Resque workers working |
|
629
643
|
|
644
|
+
### GoodJob metrics
|
645
|
+
|
646
|
+
The metrics are generated from the database using the relevant scopes. To start monitoring your GoodJob
|
647
|
+
installation, you'll need to start the instrumentation:
|
648
|
+
|
649
|
+
```ruby
|
650
|
+
# e.g. config/initializers/good_job.rb
|
651
|
+
require 'prometheus_exporter/instrumentation'
|
652
|
+
PrometheusExporter::Instrumentation::GoodJob.start
|
653
|
+
```
|
654
|
+
|
655
|
+
#### Metrics collected by GoodJob Instrumentation
|
656
|
+
|
657
|
+
| Type | Name | Description |
|
658
|
+
| --- |----------------------|-----------------------------------------|
|
659
|
+
| Gauge | `good_job_scheduled` | Total number of scheduled GoodJob jobs. |
|
660
|
+
| Gauge | `good_job_retried` | Total number of retried GoodJob jobs. |
|
661
|
+
| Gauge | `good_job_queued` | Total number of queued GoodJob jobs. |
|
662
|
+
| Gauge | `good_job_running` | Total number of running GoodJob jobs. |
|
663
|
+
| Gauge | `good_job_finished` | Total number of finished GoodJob jobs. |
|
664
|
+
| Gauge | `good_job_succeeded` | Total number of succeeded GoodJob jobs. |
|
665
|
+
| Gauge | `good_job_discarded` | Total number of discarded GoodJob jobs |
|
666
|
+
|
630
667
|
### Unicorn process metrics
|
631
668
|
|
632
669
|
In order to gather metrics from unicorn processes, we use `rainbows`, which exposes `Rainbows::Linux.tcp_listener_stats` to gather information about active workers and queued requests. To start monitoring your unicorn processes, you'll need to know both the path to unicorn PID file and the listen address (`pid_file` and `listen` in your unicorn config file)
|
@@ -858,7 +895,7 @@ prometheus_exporter -p 8080 \
|
|
858
895
|
--prefix 'foo_'
|
859
896
|
```
|
860
897
|
|
861
|
-
You can use `-b` option to bind the `prometheus_exporter` web server to any IPv4 interface with `-b 0.0.0.0`,
|
898
|
+
You can use `-b` option to bind the `prometheus_exporter` web server to any IPv4 interface with `-b 0.0.0.0`,
|
862
899
|
any IPv6 interface with `-b ::`, or `-b ANY` to any IPv4/IPv6 interfaces available on your host system.
|
863
900
|
|
864
901
|
#### Enabling Basic Authentication
|
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
|
@@ -1,8 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require
|
4
|
-
require
|
5
|
-
require 'logger'
|
3
|
+
require "socket"
|
4
|
+
require "logger"
|
6
5
|
|
7
6
|
module PrometheusExporter
|
8
7
|
class Client
|
@@ -25,7 +24,9 @@ module PrometheusExporter
|
|
25
24
|
keys: keys,
|
26
25
|
value: value
|
27
26
|
}
|
28
|
-
values[
|
27
|
+
values[
|
28
|
+
:prometheus_exporter_action
|
29
|
+
] = prometheus_exporter_action if prometheus_exporter_action
|
29
30
|
values[:opts] = @opts if @opts
|
30
31
|
values
|
31
32
|
end
|
@@ -57,8 +58,11 @@ module PrometheusExporter
|
|
57
58
|
attr_reader :logger
|
58
59
|
|
59
60
|
def initialize(
|
60
|
-
host: ENV.fetch(
|
61
|
-
port: ENV.fetch(
|
61
|
+
host: ENV.fetch("PROMETHEUS_EXPORTER_HOST", "localhost"),
|
62
|
+
port: ENV.fetch(
|
63
|
+
"PROMETHEUS_EXPORTER_PORT",
|
64
|
+
PrometheusExporter::DEFAULT_PORT
|
65
|
+
),
|
62
66
|
max_queue_size: nil,
|
63
67
|
thread_sleep: 0.5,
|
64
68
|
json_serializer: nil,
|
@@ -90,7 +94,8 @@ module PrometheusExporter
|
|
90
94
|
@mutex = Mutex.new
|
91
95
|
@thread_sleep = thread_sleep
|
92
96
|
|
93
|
-
@json_serializer =
|
97
|
+
@json_serializer =
|
98
|
+
json_serializer == :oj ? PrometheusExporter::OjCompat : JSON
|
94
99
|
|
95
100
|
@custom_labels = custom_labels
|
96
101
|
end
|
@@ -100,7 +105,14 @@ module PrometheusExporter
|
|
100
105
|
end
|
101
106
|
|
102
107
|
def register(type, name, help, opts = nil)
|
103
|
-
metric =
|
108
|
+
metric =
|
109
|
+
RemoteMetric.new(
|
110
|
+
type: type,
|
111
|
+
name: name,
|
112
|
+
help: help,
|
113
|
+
client: self,
|
114
|
+
opts: opts
|
115
|
+
)
|
104
116
|
@metrics << metric
|
105
117
|
metric
|
106
118
|
end
|
@@ -161,9 +173,7 @@ module PrometheusExporter
|
|
161
173
|
@mutex.synchronize do
|
162
174
|
wait_for_empty_queue_with_timeout(wait_timeout_seconds)
|
163
175
|
@worker_thread&.kill
|
164
|
-
while @worker_thread&.alive?
|
165
|
-
sleep 0.001
|
166
|
-
end
|
176
|
+
sleep 0.001 while @worker_thread&.alive?
|
167
177
|
@worker_thread = nil
|
168
178
|
close_socket!
|
169
179
|
end
|
@@ -183,12 +193,13 @@ module PrometheusExporter
|
|
183
193
|
@mutex.synchronize do
|
184
194
|
return if @worker_thread&.alive?
|
185
195
|
|
186
|
-
@worker_thread =
|
187
|
-
|
188
|
-
|
189
|
-
|
196
|
+
@worker_thread =
|
197
|
+
Thread.new do
|
198
|
+
while true
|
199
|
+
worker_loop
|
200
|
+
sleep @thread_sleep
|
201
|
+
end
|
190
202
|
end
|
191
|
-
end
|
192
203
|
end
|
193
204
|
end
|
194
205
|
rescue ThreadError => e
|
@@ -212,7 +223,8 @@ module PrometheusExporter
|
|
212
223
|
end
|
213
224
|
|
214
225
|
def close_socket_if_old!
|
215
|
-
if @socket_pid == Process.pid && @socket && @socket_started &&
|
226
|
+
if @socket_pid == Process.pid && @socket && @socket_started &&
|
227
|
+
((@socket_started + MAX_SOCKET_AGE) < Time.now.to_f)
|
216
228
|
close_socket!
|
217
229
|
end
|
218
230
|
end
|
@@ -240,7 +252,7 @@ module PrometheusExporter
|
|
240
252
|
end
|
241
253
|
|
242
254
|
nil
|
243
|
-
rescue
|
255
|
+
rescue StandardError
|
244
256
|
@socket = nil
|
245
257
|
@socket_started = nil
|
246
258
|
@socket_pid = nil
|
@@ -250,7 +262,10 @@ module PrometheusExporter
|
|
250
262
|
def wait_for_empty_queue_with_timeout(timeout_seconds)
|
251
263
|
start_time = ::Process.clock_gettime(::Process::CLOCK_MONOTONIC)
|
252
264
|
while @queue.length > 0
|
253
|
-
|
265
|
+
if start_time + timeout_seconds <
|
266
|
+
::Process.clock_gettime(::Process::CLOCK_MONOTONIC)
|
267
|
+
break
|
268
|
+
end
|
254
269
|
sleep(0.05)
|
255
270
|
end
|
256
271
|
end
|
@@ -2,10 +2,10 @@
|
|
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 = %r{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
|
|
@@ -15,7 +15,8 @@ module PrometheusExporter::Instrumentation
|
|
15
15
|
max_attempts = Delayed::Worker.max_attempts
|
16
16
|
enqueued_count = Delayed::Job.where(queue: job.queue).count
|
17
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,
|
18
|
+
instrumenter.call(job, max_attempts, enqueued_count, pending_count, include_module_name,
|
19
|
+
*args, &block)
|
19
20
|
end
|
20
21
|
end
|
21
22
|
end
|
@@ -28,9 +29,10 @@ module PrometheusExporter::Instrumentation
|
|
28
29
|
@client = client || PrometheusExporter::Client.default
|
29
30
|
end
|
30
31
|
|
31
|
-
def call(job, max_attempts, enqueued_count, pending_count, *args, &block)
|
32
|
+
def call(job, max_attempts, enqueued_count, pending_count, include_module_name, *args, &block)
|
32
33
|
success = false
|
33
34
|
start = ::Process.clock_gettime(::Process::CLOCK_MONOTONIC)
|
35
|
+
latency = Time.current - job.run_at
|
34
36
|
attempts = job.attempts + 1 # Increment because we're adding the current attempt
|
35
37
|
result = block.call(job, *args)
|
36
38
|
success = true
|
@@ -40,10 +42,11 @@ module PrometheusExporter::Instrumentation
|
|
40
42
|
|
41
43
|
@client.send_json(
|
42
44
|
type: "delayed_job",
|
43
|
-
name: job.handler.to_s.match(JOB_CLASS_REGEXP).to_a[1].to_s,
|
45
|
+
name: job.handler.to_s.match(JOB_CLASS_REGEXP).to_a[include_module_name ? 1 : 2].to_s,
|
44
46
|
queue_name: job.queue,
|
45
47
|
success: success,
|
46
48
|
duration: duration,
|
49
|
+
latency: latency,
|
47
50
|
attempts: attempts,
|
48
51
|
max_attempts: max_attempts,
|
49
52
|
enqueued: enqueued_count,
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# collects stats from GoodJob
|
4
|
+
module PrometheusExporter::Instrumentation
|
5
|
+
class GoodJob < PeriodicStats
|
6
|
+
def self.start(client: nil, frequency: 30)
|
7
|
+
good_job_collector = new
|
8
|
+
client ||= PrometheusExporter::Client.default
|
9
|
+
|
10
|
+
worker_loop do
|
11
|
+
client.send_json(good_job_collector.collect)
|
12
|
+
end
|
13
|
+
|
14
|
+
super
|
15
|
+
end
|
16
|
+
|
17
|
+
def collect
|
18
|
+
{
|
19
|
+
type: "good_job",
|
20
|
+
scheduled: ::GoodJob::Job.scheduled.size,
|
21
|
+
retried: ::GoodJob::Job.retried.size,
|
22
|
+
queued: ::GoodJob::Job.queued.size,
|
23
|
+
running: ::GoodJob::Job.running.size,
|
24
|
+
finished: ::GoodJob::Job.finished.size,
|
25
|
+
succeeded: ::GoodJob::Job.succeeded.size,
|
26
|
+
discarded: ::GoodJob::Job.discarded.size
|
27
|
+
}
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -44,7 +44,7 @@ class PrometheusExporter::Instrumentation::MethodProfiler
|
|
44
44
|
patch_source_line = __LINE__ + 3
|
45
45
|
patches = methods.map do |method_name|
|
46
46
|
<<~RUBY
|
47
|
-
def #{method_name}(
|
47
|
+
def #{method_name}(...)
|
48
48
|
unless prof = Thread.current[:_method_profiler]
|
49
49
|
return super
|
50
50
|
end
|
@@ -75,13 +75,13 @@ class PrometheusExporter::Instrumentation::MethodProfiler
|
|
75
75
|
<<~RUBY
|
76
76
|
unless defined?(#{method_name}__mp_unpatched)
|
77
77
|
alias_method :#{method_name}__mp_unpatched, :#{method_name}
|
78
|
-
def #{method_name}(
|
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
|
begin
|
83
83
|
start = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
84
|
-
#{method_name}__mp_unpatched(
|
84
|
+
#{method_name}__mp_unpatched(...)
|
85
85
|
ensure
|
86
86
|
data = (prof[:#{name}] ||= {duration: 0.0, calls: 0})
|
87
87
|
data[:duration] += Process.clock_gettime(Process::CLOCK_MONOTONIC) - start
|
@@ -63,6 +63,8 @@ module PrometheusExporter::Instrumentation
|
|
63
63
|
metric[:major_gc_ops_total] = stat[:major_gc_count]
|
64
64
|
metric[:minor_gc_ops_total] = stat[:minor_gc_count]
|
65
65
|
metric[:allocated_objects_total] = stat[:total_allocated_objects]
|
66
|
+
metric[:malloc_increase_bytes_limit] = stat[:malloc_increase_bytes_limit]
|
67
|
+
metric[:oldmalloc_increase_bytes_limit] = stat[:oldmalloc_increase_bytes_limit]
|
66
68
|
end
|
67
69
|
|
68
70
|
def collect_v8_stats(metric)
|
@@ -1,18 +1,19 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require
|
3
|
+
require "yaml"
|
4
4
|
|
5
5
|
module PrometheusExporter::Instrumentation
|
6
|
-
JOB_WRAPPER_CLASS_NAME =
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
6
|
+
JOB_WRAPPER_CLASS_NAME =
|
7
|
+
"ActiveJob::QueueAdapters::SidekiqAdapter::JobWrapper"
|
8
|
+
DELAYED_CLASS_NAMES = %w[
|
9
|
+
Sidekiq::Extensions::DelayedClass
|
10
|
+
Sidekiq::Extensions::DelayedModel
|
11
|
+
Sidekiq::Extensions::DelayedMailer
|
11
12
|
]
|
12
13
|
|
13
14
|
class Sidekiq
|
14
15
|
def self.death_handler
|
15
|
-
->
|
16
|
+
->(job, ex) do
|
16
17
|
job_is_fire_and_forget = job["retry"] == false
|
17
18
|
|
18
19
|
worker_class = Object.const_get(job["class"])
|
@@ -43,7 +44,8 @@ module PrometheusExporter::Instrumentation
|
|
43
44
|
end
|
44
45
|
|
45
46
|
def initialize(options = { client: nil })
|
46
|
-
@client =
|
47
|
+
@client =
|
48
|
+
options.fetch(:client, nil) || PrometheusExporter::Client.default
|
47
49
|
end
|
48
50
|
|
49
51
|
def call(worker, msg, queue)
|
@@ -82,7 +84,7 @@ module PrometheusExporter::Instrumentation
|
|
82
84
|
end
|
83
85
|
|
84
86
|
def self.get_job_wrapper_name(msg)
|
85
|
-
msg[
|
87
|
+
msg["wrapped"]
|
86
88
|
end
|
87
89
|
|
88
90
|
def self.get_delayed_name(msg, class_name)
|
@@ -90,17 +92,17 @@ module PrometheusExporter::Instrumentation
|
|
90
92
|
# fallback to class_name since we're relying on the internal implementation
|
91
93
|
# of the delayed extensions
|
92
94
|
# https://github.com/mperham/sidekiq/blob/master/lib/sidekiq/extensions/class_methods.rb
|
93
|
-
|
95
|
+
target, method_name, _args = YAML.load(msg["args"].first)
|
94
96
|
if target.class == Class
|
95
97
|
"#{target.name}##{method_name}"
|
96
98
|
else
|
97
99
|
"#{target.class.name}##{method_name}"
|
98
100
|
end
|
99
101
|
rescue Psych::DisallowedClass, ArgumentError
|
100
|
-
parsed = Psych.parse(msg[
|
102
|
+
parsed = Psych.parse(msg["args"].first)
|
101
103
|
children = parsed.root.children
|
102
|
-
target = (children[0].value || children[0].tag).sub(
|
103
|
-
method_name = (children[1].value || children[1].tag).sub(
|
104
|
+
target = (children[0].value || children[0].tag).sub("!", "")
|
105
|
+
method_name = (children[1].value || children[1].tag).sub(":", "")
|
104
106
|
|
105
107
|
if target && method_name
|
106
108
|
"#{target}##{method_name}"
|
@@ -108,7 +110,7 @@ module PrometheusExporter::Instrumentation
|
|
108
110
|
class_name
|
109
111
|
end
|
110
112
|
end
|
111
|
-
rescue
|
113
|
+
rescue StandardError
|
112
114
|
class_name
|
113
115
|
end
|
114
116
|
end
|
@@ -23,7 +23,7 @@ class PrometheusExporter::Middleware
|
|
23
23
|
end
|
24
24
|
if defined? PG::Connection
|
25
25
|
MethodProfiler.patch(PG::Connection, [
|
26
|
-
:exec, :async_exec, :exec_prepared, :send_query_prepared, :query
|
26
|
+
:exec, :async_exec, :exec_prepared, :exec_params, :send_query_prepared, :query
|
27
27
|
], :sql, instrument: config[:instrument])
|
28
28
|
end
|
29
29
|
if defined? Mysql2::Client
|