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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 9a885aa655d0bd9d939bef77ec5d7c37027d19882a96b184d0351c49593393b9
4
- data.tar.gz: bca2080108cb8169d5589ee481fc40b0edb39e890a8a0fba6ec301a987c35396
3
+ metadata.gz: f5e708b8c63be6ecff9750deebf18cad149d9b4fa7350a4523af2f145caffec5
4
+ data.tar.gz: 1204a53af367dc60e24ded2e1630e2b447ef79d3caff32165ac5c40b62955ded
5
5
  SHA512:
6
- metadata.gz: b21fe8ad31aec32b877ab50146759715c41968dd1edc510af7b986436983217fd7eb0c468faf2ceca7897ae855a36a88f3b6aef1e0dcf776846051e4cd35b822
7
- data.tar.gz: 5a0a61bd914c269385bb6b46df97b2eca5e6fe4573092f10dfed7d92a4f052b62761faa53902bfdf93e2286dc96a13bc804dd56e820dec99c95a00550026b741
6
+ metadata.gz: 5dee50712e73be92702e2948a0a69aa8b8007df59de35f10c5980d9d5a2653092a4247e7ad207713460df80f59bb0691835a3aa2b04fa751d28cc5b9c3f66f48
7
+ data.tar.gz: 0af7415fd207b1eb5f7f62639f8d48bc7735df9397537524c536631d24150b9b7daa87d17652f070a79f8134d50e780c9143ddeedcfc2d44eaa27586ffe64b19
@@ -6,7 +6,7 @@ on:
6
6
  - main
7
7
  pull_request:
8
8
  schedule:
9
- - cron: '0 0 * * 0' # weekly
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 | Description |
206
- | --- | --- | --- |
207
- | Counter | `http_requests_total` | Total HTTP requests from web app |
208
- | Summary | `http_duration_seconds` | Time spent in HTTP reqs in seconds |
209
- | Summary | `http_redis_duration_seconds | Time spent in HTTP reqs in Redis, in seconds |
210
- | Summary | `http_sql_duration_seconds | Time spent in HTTP reqs in SQL in seconds |
211
- | Summary | `http_queue_duration_seconds | Time spent queueing the request in load balancer in seconds |
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
- ruby_http_duration_seconds{path="/api/v1/teams/:id",method="GET",status="200",quantile="0.99"} 0.009880661998977303
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
- class_name
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
- # when we drop Ruby 2.3 we can drop this
110
- if "".respond_to? :match?
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] || DEFAULT_BUCKETS).sort.reverse
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
- @http_duration_seconds = nil
9
- @http_redis_duration_seconds = nil
10
- @http_sql_duration_seconds = nil
11
- @http_queue_duration_seconds = nil
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["http_duration_seconds"] = @http_duration_seconds = PrometheusExporter::Metric::Base.default_aggregation.new(
37
- "http_duration_seconds",
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["http_redis_duration_seconds"] = @http_redis_duration_seconds = PrometheusExporter::Metric::Base.default_aggregation.new(
42
- "http_redis_duration_seconds",
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["http_sql_duration_seconds"] = @http_sql_duration_seconds = PrometheusExporter::Metric::Base.default_aggregation.new(
47
- "http_sql_duration_seconds",
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["http_queue_duration_seconds"] = @http_queue_duration_seconds = PrometheusExporter::Metric::Base.default_aggregation.new(
52
- "http_queue_duration_seconds",
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
- @http_duration_seconds.observe(timings["total_duration"], labels)
66
+ @http_request_duration_seconds.observe(timings["total_duration"], labels)
67
67
  if redis = timings["redis"]
68
- @http_redis_duration_seconds.observe(redis["duration"], labels)
68
+ @http_request_redis_duration_seconds.observe(redis["duration"], labels)
69
69
  end
70
70
  if sql = timings["sql"]
71
- @http_sql_duration_seconds.observe(sql["duration"], labels)
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
- @http_queue_duration_seconds.observe(queue_time, labels)
75
+ @http_request_queue_duration_seconds.observe(queue_time, labels)
76
76
  end
77
77
  end
78
78
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module PrometheusExporter
4
- VERSION = '1.0.1'
4
+ VERSION = '2.0.0'
5
5
  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: 1.0.1
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: 2021-12-22 00:00:00.000000000 Z
11
+ date: 2022-02-18 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: webrick