prometheus_exporter 1.0.1 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.github/workflows/ci.yml +2 -2
- data/CHANGELOG +7 -0
- data/README.md +41 -8
- data/lib/prometheus_exporter/instrumentation/sidekiq.rb +26 -6
- data/lib/prometheus_exporter/metric/base.rb +2 -9
- data/lib/prometheus_exporter/metric/histogram.rb +13 -1
- data/lib/prometheus_exporter/middleware.rb +3 -4
- data/lib/prometheus_exporter/server/web_collector.rb +17 -17
- data/lib/prometheus_exporter/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f5e708b8c63be6ecff9750deebf18cad149d9b4fa7350a4523af2f145caffec5
|
4
|
+
data.tar.gz: 1204a53af367dc60e24ded2e1630e2b447ef79d3caff32165ac5c40b62955ded
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5dee50712e73be92702e2948a0a69aa8b8007df59de35f10c5980d9d5a2653092a4247e7ad207713460df80f59bb0691835a3aa2b04fa751d28cc5b9c3f66f48
|
7
|
+
data.tar.gz: 0af7415fd207b1eb5f7f62639f8d48bc7735df9397537524c536631d24150b9b7daa87d17652f070a79f8134d50e780c9143ddeedcfc2d44eaa27586ffe64b19
|
data/.github/workflows/ci.yml
CHANGED
@@ -6,7 +6,7 @@ on:
|
|
6
6
|
- main
|
7
7
|
pull_request:
|
8
8
|
schedule:
|
9
|
-
- cron:
|
9
|
+
- cron: "0 0 * * 0" # weekly
|
10
10
|
|
11
11
|
jobs:
|
12
12
|
build:
|
@@ -20,7 +20,7 @@ jobs:
|
|
20
20
|
strategy:
|
21
21
|
fail-fast: false
|
22
22
|
matrix:
|
23
|
-
ruby: [2.6, 2.7, 3.0]
|
23
|
+
ruby: ['2.6', '2.7', '3.0', '3.1']
|
24
24
|
activerecord: [60, 61]
|
25
25
|
|
26
26
|
steps:
|
data/CHANGELOG
CHANGED
@@ -1,3 +1,10 @@
|
|
1
|
+
2.0.0 - 2022-02-18
|
2
|
+
|
3
|
+
- FEATURE: Add per worker custom labels
|
4
|
+
- FEATURE: support custom histogram buckets
|
5
|
+
- FIX: all metrics are exposing status label, and not only `http_requests_total`
|
6
|
+
- BREAKING: rename all `http_duration` metrics to `http_request_duration` to match prometheus official naming conventions (See https://prometheus.io/docs/practices/naming/#metric-names).
|
7
|
+
|
1
8
|
1.0.1 - 2021-12-22
|
2
9
|
|
3
10
|
- FEATURE: add labels to preflight requests
|
data/README.md
CHANGED
@@ -28,6 +28,7 @@ To learn more see [Instrumenting Rails with Prometheus](https://samsaffron.com/a
|
|
28
28
|
* [Client default labels](#client-default-labels)
|
29
29
|
* [Client default host](#client-default-host)
|
30
30
|
* [Histogram mode](#histogram-mode)
|
31
|
+
* [Histogram - custom buckets](#histogram-custom-buckets)
|
31
32
|
* [Transport concerns](#transport-concerns)
|
32
33
|
* [JSON generation and parsing](#json-generation-and-parsing)
|
33
34
|
* [Logging](#logging)
|
@@ -202,13 +203,13 @@ $ bundle exec prometheus_exporter
|
|
202
203
|
|
203
204
|
#### Metrics collected by Rails integration middleware
|
204
205
|
|
205
|
-
| Type | Name
|
206
|
-
| --- | ---
|
207
|
-
| Counter | `http_requests_total`
|
208
|
-
| Summary | `
|
209
|
-
| Summary | `
|
210
|
-
| Summary | `
|
211
|
-
| Summary | `
|
206
|
+
| Type | Name | Description |
|
207
|
+
| --- | --- | --- |
|
208
|
+
| Counter | `http_requests_total` | Total HTTP requests from web app |
|
209
|
+
| Summary | `http_request_duration_seconds` | Time spent in HTTP reqs in seconds |
|
210
|
+
| Summary | `http_request_redis_duration_seconds`¹ | Time spent in HTTP reqs in Redis, in seconds |
|
211
|
+
| Summary | `http_request_sql_duration_seconds`² | Time spent in HTTP reqs in SQL in seconds |
|
212
|
+
| Summary | `http_request_queue_duration_seconds`³ | Time spent queueing the request in load balancer in seconds |
|
212
213
|
|
213
214
|
All metrics have a `controller` and an `action` label.
|
214
215
|
`http_requests_total` additionally has a (HTTP response) `status` label.
|
@@ -251,7 +252,7 @@ end
|
|
251
252
|
```
|
252
253
|
That way you won't have all metrics labeled with `controller=other` and `action=other`, but have labels such as
|
253
254
|
```
|
254
|
-
|
255
|
+
ruby_http_request_duration_seconds{path="/api/v1/teams/:id",method="GET",status="200",quantile="0.99"} 0.009880661998977303
|
255
256
|
```
|
256
257
|
|
257
258
|
¹) Only available when Redis is used.
|
@@ -420,6 +421,18 @@ Sometimes the Sidekiq server shuts down before it can send metrics, that were ge
|
|
420
421
|
end
|
421
422
|
```
|
422
423
|
|
424
|
+
Custom labels can be added for individual jobs by defining a class method on the job class. These labels will be added to all Sidekiq metrics written by the job:
|
425
|
+
|
426
|
+
```ruby
|
427
|
+
class WorkerWithCustomLabels
|
428
|
+
def self.custom_labels
|
429
|
+
{ my_label: 'value-here', other_label: 'second-val' }
|
430
|
+
end
|
431
|
+
|
432
|
+
def perform; end
|
433
|
+
end
|
434
|
+
```
|
435
|
+
|
423
436
|
##### Metrics collected by Sidekiq Instrumentation
|
424
437
|
|
425
438
|
**PrometheusExporter::Instrumentation::Sidekiq**
|
@@ -891,6 +904,26 @@ In histogram mode, the same metrics will be collected but will be reported as hi
|
|
891
904
|
|
892
905
|
[`histogram_quantile`]: https://prometheus.io/docs/prometheus/latest/querying/functions/#histogram_quantile
|
893
906
|
|
907
|
+
### Histogram - custom buckets
|
908
|
+
|
909
|
+
By default these buckets will be used:
|
910
|
+
```
|
911
|
+
[0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1, 2.5, 5.0, 10.0].freeze
|
912
|
+
```
|
913
|
+
if this is not enough you can specify `default_buckets` like this:
|
914
|
+
```
|
915
|
+
Histogram.default_buckets = [0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1, 2, 2.5, 3, 4, 5.0, 10.0, 12, 14, 15, 20, 25].freeze
|
916
|
+
```
|
917
|
+
|
918
|
+
Specfied buckets on the instance takes precedence over default:
|
919
|
+
|
920
|
+
```
|
921
|
+
Histogram.default_buckets = [0.005, 0.01, 0,5].freeze
|
922
|
+
buckets = [0.1, 0.2, 0.3]
|
923
|
+
histogram = Histogram.new('test_bucktets', 'I have specified buckets', buckets: buckets)
|
924
|
+
histogram.buckets => [0.1, 0.2, 0.3]
|
925
|
+
```
|
926
|
+
|
894
927
|
## Transport concerns
|
895
928
|
|
896
929
|
Prometheus Exporter handles transport using a simple HTTP protocol. In multi process mode we avoid needing a large number of HTTP request by using chunked encoding to send metrics. This means that a single HTTP channel can deliver 100s or even 1000s of metrics over a single HTTP session to the `/send-metrics` endpoint. All calls to `send` and `send_json` on the `PrometheusExporter::Client` class are **non-blocking** and batched.
|
@@ -15,16 +15,24 @@ module PrometheusExporter::Instrumentation
|
|
15
15
|
-> (job, ex) do
|
16
16
|
job_is_fire_and_forget = job["retry"] == false
|
17
17
|
|
18
|
+
worker_class = Object.const_get(job["class"])
|
19
|
+
worker_custom_labels = self.get_worker_custom_labels(worker_class)
|
20
|
+
|
18
21
|
unless job_is_fire_and_forget
|
19
22
|
PrometheusExporter::Client.default.send_json(
|
20
23
|
type: "sidekiq",
|
21
24
|
name: job["class"],
|
22
25
|
dead: true,
|
26
|
+
custom_labels: worker_custom_labels
|
23
27
|
)
|
24
28
|
end
|
25
29
|
end
|
26
30
|
end
|
27
31
|
|
32
|
+
def self.get_worker_custom_labels(worker_class)
|
33
|
+
worker_class.respond_to?(:custom_labels) ? worker_class.custom_labels : {}
|
34
|
+
end
|
35
|
+
|
28
36
|
def initialize(client: nil)
|
29
37
|
@client = client || PrometheusExporter::Client.default
|
30
38
|
end
|
@@ -47,7 +55,8 @@ module PrometheusExporter::Instrumentation
|
|
47
55
|
queue: queue,
|
48
56
|
success: success,
|
49
57
|
shutdown: shutdown,
|
50
|
-
duration: duration
|
58
|
+
duration: duration,
|
59
|
+
custom_labels: self.class.get_worker_custom_labels(worker.class)
|
51
60
|
)
|
52
61
|
end
|
53
62
|
|
@@ -69,19 +78,30 @@ module PrometheusExporter::Instrumentation
|
|
69
78
|
end
|
70
79
|
|
71
80
|
def get_delayed_name(msg, class_name)
|
72
|
-
# fallback to class_name since we're relying on the internal implementation
|
73
|
-
# of the delayed extensions
|
74
|
-
# https://github.com/mperham/sidekiq/blob/master/lib/sidekiq/extensions/class_methods.rb
|
75
81
|
begin
|
82
|
+
# fallback to class_name since we're relying on the internal implementation
|
83
|
+
# of the delayed extensions
|
84
|
+
# https://github.com/mperham/sidekiq/blob/master/lib/sidekiq/extensions/class_methods.rb
|
76
85
|
(target, method_name, _args) = YAML.load(msg['args'].first) # rubocop:disable Security/YAMLLoad
|
77
86
|
if target.class == Class
|
78
87
|
"#{target.name}##{method_name}"
|
79
88
|
else
|
80
89
|
"#{target.class.name}##{method_name}"
|
81
90
|
end
|
82
|
-
rescue
|
83
|
-
|
91
|
+
rescue Psych::DisallowedClass, ArgumentError
|
92
|
+
parsed = Psych.parse(msg['args'].first)
|
93
|
+
children = parsed.root.children
|
94
|
+
target = (children[0].value || children[0].tag).sub('!', '')
|
95
|
+
method_name = (children[1].value || children[1].tag).sub(':', '')
|
96
|
+
|
97
|
+
if target && method_name
|
98
|
+
"#{target}##{method_name}"
|
99
|
+
else
|
100
|
+
class_name
|
101
|
+
end
|
84
102
|
end
|
103
|
+
rescue
|
104
|
+
class_name
|
85
105
|
end
|
86
106
|
end
|
87
107
|
end
|
@@ -106,15 +106,8 @@ module PrometheusExporter::Metric
|
|
106
106
|
end
|
107
107
|
end
|
108
108
|
|
109
|
-
|
110
|
-
|
111
|
-
def needs_escape?(str)
|
112
|
-
str.match?(/[\n"\\]/m)
|
113
|
-
end
|
114
|
-
else
|
115
|
-
def needs_escape?(str)
|
116
|
-
!!str.match(/[\n"\\]/m)
|
117
|
-
end
|
109
|
+
def needs_escape?(str)
|
110
|
+
str.match?(/[\n"\\]/m)
|
118
111
|
end
|
119
112
|
|
120
113
|
end
|
@@ -5,9 +5,21 @@ module PrometheusExporter::Metric
|
|
5
5
|
|
6
6
|
DEFAULT_BUCKETS = [0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1, 2.5, 5.0, 10.0].freeze
|
7
7
|
|
8
|
+
@default_buckets = nil if !defined?(@default_buckets)
|
9
|
+
|
10
|
+
def self.default_buckets
|
11
|
+
@default_buckets || DEFAULT_BUCKETS
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.default_buckets=(buckets)
|
15
|
+
@default_buckets = buckets
|
16
|
+
end
|
17
|
+
|
18
|
+
attr_reader :buckets
|
19
|
+
|
8
20
|
def initialize(name, help, opts = {})
|
9
21
|
super(name, help)
|
10
|
-
@buckets = (opts[:buckets] ||
|
22
|
+
@buckets = (opts[:buckets] || self.class.default_buckets).sort.reverse
|
11
23
|
reset!
|
12
24
|
end
|
13
25
|
|
@@ -36,11 +36,12 @@ class PrometheusExporter::Middleware
|
|
36
36
|
|
37
37
|
result
|
38
38
|
ensure
|
39
|
-
|
39
|
+
status = (result && result[0]) || -1
|
40
40
|
obj = {
|
41
41
|
type: "web",
|
42
42
|
timings: info,
|
43
43
|
queue_time: queue_time,
|
44
|
+
status: status,
|
44
45
|
default_labels: default_labels(env, result)
|
45
46
|
}
|
46
47
|
labels = custom_labels(env)
|
@@ -52,7 +53,6 @@ class PrometheusExporter::Middleware
|
|
52
53
|
end
|
53
54
|
|
54
55
|
def default_labels(env, result)
|
55
|
-
status = (result && result[0]) || -1
|
56
56
|
params = env["action_dispatch.request.parameters"]
|
57
57
|
action = controller = nil
|
58
58
|
if params
|
@@ -67,8 +67,7 @@ class PrometheusExporter::Middleware
|
|
67
67
|
|
68
68
|
{
|
69
69
|
action: action || "other",
|
70
|
-
controller: controller || "other"
|
71
|
-
status: status
|
70
|
+
controller: controller || "other"
|
72
71
|
}
|
73
72
|
end
|
74
73
|
|
@@ -5,10 +5,10 @@ module PrometheusExporter::Server
|
|
5
5
|
def initialize
|
6
6
|
@metrics = {}
|
7
7
|
@http_requests_total = nil
|
8
|
-
@
|
9
|
-
@
|
10
|
-
@
|
11
|
-
@
|
8
|
+
@http_request_duration_seconds = nil
|
9
|
+
@http_request_redis_duration_seconds = nil
|
10
|
+
@http_request_sql_duration_seconds = nil
|
11
|
+
@http_request_queue_duration_seconds = nil
|
12
12
|
end
|
13
13
|
|
14
14
|
def type
|
@@ -33,23 +33,23 @@ module PrometheusExporter::Server
|
|
33
33
|
"Total HTTP requests from web app."
|
34
34
|
)
|
35
35
|
|
36
|
-
@metrics["
|
37
|
-
"
|
36
|
+
@metrics["http_request_duration_seconds"] = @http_request_duration_seconds = PrometheusExporter::Metric::Base.default_aggregation.new(
|
37
|
+
"http_request_duration_seconds",
|
38
38
|
"Time spent in HTTP reqs in seconds."
|
39
39
|
)
|
40
40
|
|
41
|
-
@metrics["
|
42
|
-
"
|
41
|
+
@metrics["http_request_redis_duration_seconds"] = @http_request_redis_duration_seconds = PrometheusExporter::Metric::Base.default_aggregation.new(
|
42
|
+
"http_request_redis_duration_seconds",
|
43
43
|
"Time spent in HTTP reqs in Redis, in seconds."
|
44
44
|
)
|
45
45
|
|
46
|
-
@metrics["
|
47
|
-
"
|
46
|
+
@metrics["http_request_sql_duration_seconds"] = @http_request_sql_duration_seconds = PrometheusExporter::Metric::Base.default_aggregation.new(
|
47
|
+
"http_request_sql_duration_seconds",
|
48
48
|
"Time spent in HTTP reqs in SQL in seconds."
|
49
49
|
)
|
50
50
|
|
51
|
-
@metrics["
|
52
|
-
"
|
51
|
+
@metrics["http_request_queue_duration_seconds"] = @http_request_queue_duration_seconds = PrometheusExporter::Metric::Base.default_aggregation.new(
|
52
|
+
"http_request_queue_duration_seconds",
|
53
53
|
"Time spent queueing the request in load balancer in seconds."
|
54
54
|
)
|
55
55
|
end
|
@@ -60,19 +60,19 @@ module PrometheusExporter::Server
|
|
60
60
|
custom_labels = obj['custom_labels']
|
61
61
|
labels = custom_labels.nil? ? default_labels : default_labels.merge(custom_labels)
|
62
62
|
|
63
|
-
@http_requests_total.observe(1, labels)
|
63
|
+
@http_requests_total.observe(1, labels.merge(status: obj["status"]))
|
64
64
|
|
65
65
|
if timings = obj["timings"]
|
66
|
-
@
|
66
|
+
@http_request_duration_seconds.observe(timings["total_duration"], labels)
|
67
67
|
if redis = timings["redis"]
|
68
|
-
@
|
68
|
+
@http_request_redis_duration_seconds.observe(redis["duration"], labels)
|
69
69
|
end
|
70
70
|
if sql = timings["sql"]
|
71
|
-
@
|
71
|
+
@http_request_sql_duration_seconds.observe(sql["duration"], labels)
|
72
72
|
end
|
73
73
|
end
|
74
74
|
if queue_time = obj["queue_time"]
|
75
|
-
@
|
75
|
+
@http_request_queue_duration_seconds.observe(queue_time, labels)
|
76
76
|
end
|
77
77
|
end
|
78
78
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: prometheus_exporter
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 2.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Sam Saffron
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2022-02-18 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: webrick
|