prometheus_exporter 1.0.1 → 2.0.0

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