prometheus_client_addons 0.1.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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: e92df93c0d837d7a85176aaf94e7c954f4eb1de51e0f0f7f87341414514e614b
4
+ data.tar.gz: 80329fa945177a4acabfca5be1caacfc4f57b6dcbc6d567a8a92bd6f14391d57
5
+ SHA512:
6
+ metadata.gz: fb58dc08009041432e37ff55b1e3a14cbbb36ee9545154671ad1ea1dae62bfece3544c016601c49348984c7da12878e10e9d6adfefa04179446e7b5849981eb0
7
+ data.tar.gz: df185a4b8b278471a3c4e49d9e96e17daca4050e5eb3a34548589b96721425507b03ee227eeea2c2fd0a9173178e66e8b62e903353b65ece62308b8e333c4f9f
data/.gitignore ADDED
@@ -0,0 +1,13 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /_yardoc/
4
+ /coverage/
5
+ /doc/
6
+ /pkg/
7
+ /spec/reports/
8
+ /tmp/
9
+ .ruby-*
10
+ Gemfile.lock
11
+
12
+ # rspec failure tracking
13
+ .rspec_status
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --format documentation
2
+ --color
3
+ --require spec_helper
data/.travis.yml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ sudo: false
3
+ language: ruby
4
+ cache: bundler
5
+ rvm:
6
+ - 2.6.3
7
+ before_install: gem install bundler -v 1.17.2
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ source "https://rubygems.org"
2
+
3
+ git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }
4
+
5
+ # Specify your gem's dependencies in prometheus_client_addons.gemspec
6
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2019 kraaaken
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,273 @@
1
+ # Prometheus Ruby Client Addons
2
+
3
+ ## Usage (with Rails)
4
+
5
+ ### Puma server
6
+
7
+ config/initializers/prometheus.rb
8
+ ```ruby
9
+ require 'prometheus/client'
10
+ require 'prometheus_client_addons'
11
+
12
+ prometheus = Prometheus::Client.registry
13
+ puma = PrometheusClientAddons::Prometheus::Client::Puma.new(
14
+ prefix: 'puma',
15
+ base_labels: { my_label: 'baz' }
16
+ )
17
+ prometheus.register(puma)
18
+ ```
19
+
20
+ config/puma.rb
21
+ ```ruby
22
+ activate_control_app('auto')
23
+ plugin :prometheus_client_addons
24
+ ```
25
+
26
+ Example response in non-clustered mode
27
+ ```
28
+ # TYPE puma_backlog gauge
29
+ # HELP puma_backlog How many requests are waiting
30
+ puma_backlog{my_label="baz"} 0
31
+ # TYPE puma_running gauge
32
+ # HELP puma_running Number of running threads
33
+ puma_running{my_label="baz"} 5
34
+ # TYPE puma_pool_capacity gauge
35
+ # HELP puma_pool_capacity Number of requests that can be processed right now
36
+ puma_pool_capacity{my_label="baz"} 4
37
+ # TYPE puma_max_threads gauge
38
+ # HELP puma_max_threads Maximum number of worker threads
39
+ puma_max_threads{my_label="baz"} 5
40
+ ```
41
+
42
+ Example response in clustered mode
43
+ ```
44
+ # TYPE puma_workers gauge
45
+ # HELP puma_workers Number of workers
46
+ puma_workers{my_label="baz"} 2
47
+ # TYPE puma_phase gauge
48
+ # HELP puma_phase Phase of worker
49
+ puma_phase{my_label="baz"} 0
50
+ # TYPE puma_booted_workers gauge
51
+ # HELP puma_booted_workers Number of booted workers
52
+ puma_booted_workers{my_label="baz"} 2
53
+ # TYPE puma_old_workers gauge
54
+ # HELP puma_old_workers Number of old workers
55
+ puma_old_workers{my_label="baz"} 0
56
+ # TYPE puma_worker_status_phase gauge
57
+ # HELP puma_worker_status_phase Phase of worker
58
+ puma_worker_status_phase{my_label="baz",worker_index="0"} 0
59
+ puma_worker_status_phase{my_label="baz",worker_index="1"} 0
60
+ # TYPE puma_worker_status_booted gauge
61
+ # HELP puma_worker_status_booted Booted or not
62
+ puma_worker_status_booted{my_label="baz",worker_index="0"} true
63
+ puma_worker_status_booted{my_label="baz",worker_index="1"} true
64
+ # TYPE puma_worker_status_last_checkin gauge
65
+ # HELP puma_worker_status_last_checkin Last update
66
+ puma_worker_status_last_checkin{my_label="baz",worker_index="0"} 2019-09-04T15:01:33Z
67
+ puma_worker_status_last_checkin{my_label="baz",worker_index="1"} 2019-09-04T15:01:33Z
68
+ # TYPE puma_worker_status_last_status_backlog gauge
69
+ # HELP puma_worker_status_last_status_backlog How many objects have yet to be processed by the pool
70
+ puma_worker_status_last_status_backlog{my_label="baz",worker_index="0"} 0
71
+ puma_worker_status_last_status_backlog{my_label="baz",worker_index="1"} 0
72
+ # TYPE puma_worker_status_last_status_running gauge
73
+ # HELP puma_worker_status_last_status_running Number of running threads
74
+ puma_worker_status_last_status_running{my_label="baz",worker_index="0"} 5
75
+ puma_worker_status_last_status_running{my_label="baz",worker_index="1"} 5
76
+ # TYPE puma_worker_status_last_status_pool_capacity gauge
77
+ # HELP puma_worker_status_last_status_pool_capacity Number of requests that can be processed right now
78
+ puma_worker_status_last_status_pool_capacity{my_label="baz",worker_index="0"} 5
79
+ puma_worker_status_last_status_pool_capacity{my_label="baz",worker_index="1"} 5
80
+ # TYPE puma_worker_status_last_status_max_threads gauge
81
+ # HELP puma_worker_status_last_status_max_threads Maximum number of worker threads
82
+ puma_worker_status_last_status_max_threads{my_label="baz",worker_index="0"} 5
83
+ puma_worker_status_last_status_max_threads{my_label="baz",worker_index="1"} 5
84
+ ```
85
+
86
+ ### ActiveRecord::Base.connection_pool.stat
87
+
88
+ *Now it's not working with pre-forked/clustered-mode web servers. Sorry.*
89
+
90
+ config/initializers/prometheus.rb
91
+ ```ruby
92
+ require 'prometheus/client'
93
+ require 'prometheus_client_addons'
94
+
95
+ prometheus = Prometheus::Client.registry
96
+ pool = PrometheusClientAddons::Prometheus::Client::ActiveRecord.new(
97
+ prefix: 'activerecord',
98
+ base_labels: { my_label: 'foo' }
99
+ )
100
+ prometheus.register(pool)
101
+ ```
102
+
103
+ Example response
104
+ ```
105
+ # TYPE activerecord_size gauge
106
+ # HELP activerecord_size Size of pool
107
+ activerecord_size{my_label="foo"} 5
108
+ # TYPE activerecord_connections gauge
109
+ # HELP activerecord_connections Connections count
110
+ activerecord_connections{my_label="foo"} 0
111
+ # TYPE activerecord_busy gauge
112
+ # HELP activerecord_busy Busy count
113
+ activerecord_busy{my_label="foo"} 0
114
+ # TYPE activerecord_dead gauge
115
+ # HELP activerecord_dead Dead count
116
+ activerecord_dead{my_label="foo"} 0
117
+ # TYPE activerecord_idle gauge
118
+ # HELP activerecord_idle Idle count
119
+ activerecord_idle{my_label="foo"} 0
120
+ # TYPE activerecord_waiting gauge
121
+ # HELP activerecord_waiting Num waiting in queue
122
+ activerecord_waiting{my_label="foo"} 0
123
+ # TYPE activerecord_checkout_timeout gauge
124
+ # HELP activerecord_checkout_timeout Checkout timeout
125
+ activerecord_checkout_timeout{my_label="foo"} 5
126
+ ```
127
+
128
+ ### GC.stat
129
+
130
+ config/initializers/prometheus.rb
131
+ ```ruby
132
+ require 'prometheus/client'
133
+ require 'prometheus_client_addons'
134
+
135
+ prometheus = Prometheus::Client.registry
136
+ gc = PrometheusClientAddons::Prometheus::Client::GC.new(
137
+ prefix: 'gc',
138
+ base_labels: { my_label: 'foo' }
139
+ )
140
+ prometheus.register(gc)
141
+ ```
142
+
143
+ Example response
144
+ ```
145
+ # TYPE gc_count gauge
146
+ # HELP gc_count count
147
+ gc_count{my_label="foo"} 62
148
+ # TYPE gc_heap_allocated_pages gauge
149
+ # HELP gc_heap_allocated_pages heap_allocated_pages
150
+ gc_heap_allocated_pages{my_label="foo"} 3593
151
+ # TYPE gc_heap_sorted_length gauge
152
+ # HELP gc_heap_sorted_length heap_sorted_length
153
+ gc_heap_sorted_length{my_label="foo"} 3593
154
+ # TYPE gc_heap_allocatable_pages gauge
155
+ # HELP gc_heap_allocatable_pages heap_allocatable_pages
156
+ gc_heap_allocatable_pages{my_label="foo"} 0
157
+ # TYPE gc_heap_available_slots gauge
158
+ # HELP gc_heap_available_slots heap_available_slots
159
+ gc_heap_available_slots{my_label="foo"} 1464501
160
+ # TYPE gc_heap_live_slots gauge
161
+ # HELP gc_heap_live_slots heap_live_slots
162
+ gc_heap_live_slots{my_label="foo"} 1464445
163
+ # TYPE gc_heap_free_slots gauge
164
+ # HELP gc_heap_free_slots heap_free_slots
165
+ gc_heap_free_slots{my_label="foo"} 56
166
+ # TYPE gc_heap_final_slots gauge
167
+ # HELP gc_heap_final_slots heap_final_slots
168
+ gc_heap_final_slots{my_label="foo"} 0
169
+ # TYPE gc_heap_marked_slots gauge
170
+ # HELP gc_heap_marked_slots heap_marked_slots
171
+ gc_heap_marked_slots{my_label="foo"} 999525
172
+ # TYPE gc_heap_eden_pages gauge
173
+ # HELP gc_heap_eden_pages heap_eden_pages
174
+ gc_heap_eden_pages{my_label="foo"} 3593
175
+ # TYPE gc_heap_tomb_pages gauge
176
+ # HELP gc_heap_tomb_pages heap_tomb_pages
177
+ gc_heap_tomb_pages{my_label="foo"} 0
178
+ # TYPE gc_total_allocated_pages gauge
179
+ # HELP gc_total_allocated_pages total_allocated_pages
180
+ gc_total_allocated_pages{my_label="foo"} 3593
181
+ # TYPE gc_total_freed_pages gauge
182
+ # HELP gc_total_freed_pages total_freed_pages
183
+ gc_total_freed_pages{my_label="foo"} 0
184
+ # TYPE gc_total_allocated_objects gauge
185
+ # HELP gc_total_allocated_objects total_allocated_objects
186
+ gc_total_allocated_objects{my_label="foo"} 4665284
187
+ # TYPE gc_total_freed_objects gauge
188
+ # HELP gc_total_freed_objects total_freed_objects
189
+ gc_total_freed_objects{my_label="foo"} 3200839
190
+ # TYPE gc_malloc_increase_bytes gauge
191
+ # HELP gc_malloc_increase_bytes malloc_increase_bytes
192
+ gc_malloc_increase_bytes{my_label="foo"} 16693664
193
+ # TYPE gc_malloc_increase_bytes_limit gauge
194
+ # HELP gc_malloc_increase_bytes_limit malloc_increase_bytes_limit
195
+ gc_malloc_increase_bytes_limit{my_label="foo"} 30330547
196
+ # TYPE gc_minor_gc_count gauge
197
+ # HELP gc_minor_gc_count minor_gc_count
198
+ gc_minor_gc_count{my_label="foo"} 48
199
+ # TYPE gc_major_gc_count gauge
200
+ # HELP gc_major_gc_count major_gc_count
201
+ gc_major_gc_count{my_label="foo"} 14
202
+ # TYPE gc_remembered_wb_unprotected_objects gauge
203
+ # HELP gc_remembered_wb_unprotected_objects remembered_wb_unprotected_objects
204
+ gc_remembered_wb_unprotected_objects{my_label="foo"} 4357
205
+ # TYPE gc_remembered_wb_unprotected_objects_limit gauge
206
+ # HELP gc_remembered_wb_unprotected_objects_limit remembered_wb_unprotected_objects_limit
207
+ gc_remembered_wb_unprotected_objects_limit{my_label="foo"} 8690
208
+ # TYPE gc_old_objects gauge
209
+ # HELP gc_old_objects old_objects
210
+ gc_old_objects{my_label="foo"} 992074
211
+ # TYPE gc_old_objects_limit gauge
212
+ # HELP gc_old_objects_limit old_objects_limit
213
+ gc_old_objects_limit{my_label="foo"} 1743172
214
+ # TYPE gc_oldmalloc_increase_bytes gauge
215
+ # HELP gc_oldmalloc_increase_bytes oldmalloc_increase_bytes
216
+ gc_oldmalloc_increase_bytes{my_label="foo"} 37700816
217
+ # TYPE gc_oldmalloc_increase_bytes_limit gauge
218
+ # HELP gc_oldmalloc_increase_bytes_limit oldmalloc_increase_bytes_limit
219
+ gc_oldmalloc_increase_bytes_limit{my_label="foo"} 33438324
220
+ ```
221
+
222
+ ### CustomCollector
223
+
224
+ config/initializers/prometheus.rb
225
+ ```ruby
226
+ require 'prometheus/client'
227
+ require 'prometheus_client_addons'
228
+
229
+ prometheus = Prometheus::Client.registry
230
+ custom_collector = PrometheusClientAddons::Prometheus::Client::CustomCollector.new(
231
+ name: 'custom_metric',
232
+ docstring: 'This is a custom metric',
233
+ base_labels: { my_label: 'foo' },
234
+ &Proc.new {
235
+ # return custom value from block
236
+ Time.now.to_i
237
+ }
238
+ )
239
+ prometheus.register(custom_collector)
240
+ ```
241
+
242
+ Example response
243
+ ```
244
+ # TYPE custom_metric gauge
245
+ # HELP custom_metric This is a custom metric
246
+ custom_metric{my_label="foo"} 1567612321
247
+ ```
248
+
249
+ ## Installation
250
+
251
+ ```ruby
252
+ gem 'prometheus_client_addons'
253
+ ```
254
+
255
+ ```bash
256
+ bundle install
257
+ # or
258
+ gem install prometheus_client_addons
259
+ ```
260
+
261
+ ## Development
262
+
263
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
264
+
265
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
266
+
267
+ ## Contributing
268
+
269
+ Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/prometheus_client_addons.
270
+
271
+ ## License
272
+
273
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
data/Rakefile ADDED
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
data/bin/console ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "prometheus_client_addons"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start(__FILE__)
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,9 @@
1
+ require "prometheus_client_addons/version"
2
+
3
+ module PrometheusClientAddons
4
+ end
5
+
6
+ require 'prometheus_client_addons/prometheus/client/puma'
7
+ require 'prometheus_client_addons/prometheus/client/active_record'
8
+ require 'prometheus_client_addons/prometheus/client/custom_collector'
9
+ require 'prometheus_client_addons/prometheus/client/gc'
@@ -0,0 +1,34 @@
1
+ require 'prometheus_client_addons/prometheus/client/formats/text_extended'
2
+ require 'prometheus_client_addons/prometheus/client/multi_metric'
3
+
4
+ module PrometheusClientAddons
5
+ module Prometheus
6
+ module Client
7
+ class ActiveRecord < MultiMetric
8
+ HANDLES = {
9
+ size: 'Size of pool',
10
+ connections: 'Connections count',
11
+ busy: 'Busy count',
12
+ dead: 'Dead count',
13
+ idle: 'Idle count',
14
+ waiting: 'Num waiting in queue',
15
+ checkout_timeout: 'Checkout timeout'
16
+ }.freeze
17
+
18
+ def multi_name_type
19
+ full_handles = HANDLES.keys.map { |key| "#{prefix}#{key}" }.map(&:to_sym)
20
+ Hash[full_handles.zip([:gauge] * HANDLES.size)]
21
+ end
22
+
23
+ def multi_name_docstring
24
+ Hash[HANDLES.map { |key, value| ["#{prefix}#{key}".to_sym, value] }]
25
+ end
26
+
27
+ def multi_values
28
+ stat = ::ActiveRecord::Base.connection_pool.stat
29
+ Hash[stat.map { |key, value| ["#{prefix}#{key}".to_sym, { {} => value }] }]
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,26 @@
1
+ require 'prometheus/client/metric'
2
+
3
+ module PrometheusClientAddons
4
+ module Prometheus
5
+ module Client
6
+ class CustomCollector < ::Prometheus::Client::Metric
7
+ def initialize(name:, docstring:, base_labels: {}, &block)
8
+ @custom_collector = block
9
+ super(name.to_sym, docstring, base_labels)
10
+ end
11
+
12
+ def get(labels = {})
13
+ @custom_collector.call
14
+ end
15
+
16
+ def values
17
+ { {} => @custom_collector.call }
18
+ end
19
+
20
+ def type
21
+ :gauge
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,17 @@
1
+ module PrometheusClientAddons
2
+ module Prometheus
3
+ module Client
4
+ FakeGauge = Struct.new(:name, :docstring, :base_labels) do
5
+ def initialize(*)
6
+ self.base_labels ||= {}
7
+ super
8
+ end
9
+
10
+ def type
11
+ :gauge
12
+ end
13
+ end
14
+
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,48 @@
1
+ require 'prometheus/middleware/exporter'
2
+ require 'prometheus/client/formats/text'
3
+ require 'prometheus_client_addons/prometheus/client/multi_metric'
4
+ require 'prometheus_client_addons/prometheus/client/fake_gauge'
5
+
6
+ module PrometheusClientAddons
7
+ module Prometheus
8
+ module Client
9
+ module Formats
10
+ TextExtended = ::Prometheus::Client::Formats::Text.dup
11
+
12
+ module TextExtended
13
+ def self.marshal(registry)
14
+ lines = []
15
+
16
+ registry.metrics.each do |metric|
17
+ if metric.is_a?(MultiMetric)
18
+ metric.multi_values.each do |name, values|
19
+ lines << format(self::TYPE_LINE, name, metric.multi_name_type[name])
20
+ lines << format(self::HELP_LINE, name, escape(metric.multi_name_docstring[name]))
21
+
22
+ values.each do |label_set, value|
23
+ _metric = FakeGauge.new(name, metric.multi_name_docstring[name], metric.base_labels)
24
+ representation(_metric, label_set, value) { |l| lines << l }
25
+ end
26
+ end
27
+ else
28
+ lines << format(self::TYPE_LINE, metric.name, metric.type)
29
+ lines << format(self::HELP_LINE, metric.name, escape(metric.docstring))
30
+
31
+ metric.values.each do |label_set, value|
32
+ representation(metric, label_set, value) { |l| lines << l }
33
+ end
34
+ end
35
+ end
36
+
37
+ (lines << nil).join(self::DELIMITER)
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
44
+
45
+ Prometheus::Middleware::Exporter.send(:remove_const, 'FORMATS')
46
+ Prometheus::Middleware::Exporter.send(:remove_const, 'FALLBACK')
47
+ Prometheus::Middleware::Exporter.send(:const_set, 'FORMATS', [PrometheusClientAddons::Prometheus::Client::Formats::TextExtended].freeze)
48
+ Prometheus::Middleware::Exporter.send(:const_set, 'FALLBACK', PrometheusClientAddons::Prometheus::Client::Formats::TextExtended)
@@ -0,0 +1,54 @@
1
+ require 'prometheus_client_addons/prometheus/client/formats/text_extended'
2
+ require 'prometheus_client_addons/prometheus/client/multi_metric'
3
+
4
+ module PrometheusClientAddons
5
+ module Prometheus
6
+ module Client
7
+ class GC < MultiMetric
8
+ KEYS = [
9
+ :count,
10
+ :heap_allocated_pages,
11
+ :heap_sorted_length,
12
+ :heap_allocatable_pages,
13
+ :heap_available_slots,
14
+ :heap_live_slots,
15
+ :heap_free_slots,
16
+ :heap_final_slots,
17
+ :heap_marked_slots,
18
+ :heap_eden_pages,
19
+ :heap_tomb_pages,
20
+ :total_allocated_pages,
21
+ :total_freed_pages,
22
+ :total_allocated_objects,
23
+ :total_freed_objects,
24
+ :malloc_increase_bytes,
25
+ :malloc_increase_bytes_limit,
26
+ :minor_gc_count,
27
+ :major_gc_count,
28
+ :remembered_wb_unprotected_objects,
29
+ :remembered_wb_unprotected_objects_limit,
30
+ :old_objects,
31
+ :old_objects_limit,
32
+ :oldmalloc_increase_bytes,
33
+ :oldmalloc_increase_bytes_limit
34
+ ].freeze
35
+
36
+ HANDLES = Hash[KEYS.zip(KEYS)].freeze
37
+
38
+ def multi_name_type
39
+ full_handles = HANDLES.keys.map { |key| "#{prefix}#{key}" }.map(&:to_sym)
40
+ Hash[full_handles.zip([:gauge] * HANDLES.size)]
41
+ end
42
+
43
+ def multi_name_docstring
44
+ Hash[HANDLES.map { |key, value| ["#{prefix}#{key}".to_sym, value] }]
45
+ end
46
+
47
+ def multi_values
48
+ stat = ::GC.stat
49
+ Hash[stat.map { |key, value| ["#{prefix}#{key}".to_sym, { {} => value }] }]
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,38 @@
1
+ require 'prometheus/client/metric'
2
+ require 'prometheus/client/label_set_validator'
3
+
4
+ module PrometheusClientAddons
5
+ module Prometheus
6
+ module Client
7
+ class MultiMetric < ::Prometheus::Client::Metric
8
+ attr_reader :name, :prefix, :base_labels
9
+
10
+ def initialize(prefix: '', base_labels: {})
11
+ prefix = "#{prefix}_" unless prefix == ''
12
+
13
+ @prefix = prefix
14
+ @name = prefix
15
+ @base_labels = base_labels
16
+
17
+ multi_name_type.keys.each { |name| validate_name("#{prefix}#{name}".to_sym) }
18
+ multi_name_docstring.keys.each { |name| validate_name("#{prefix}#{name}".to_sym) }
19
+ multi_name_docstring.values.each(&method(:validate_docstring))
20
+ @validator = ::Prometheus::Client::LabelSetValidator.new
21
+ @validator.valid?(base_labels)
22
+ end
23
+
24
+ def multi_name_type
25
+ fail('Should return hash {name => type}')
26
+ end
27
+
28
+ def multi_name_docstring
29
+ fail('Should return hash {name => docstrings}')
30
+ end
31
+
32
+ def multi_values
33
+ fail('Should return hash {name => {label_set => value, label_set => value, }}')
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,97 @@
1
+ require 'json'
2
+ require 'prometheus_client_addons/prometheus/client/formats/text_extended'
3
+ require 'prometheus_client_addons/prometheus/client/multi_metric'
4
+ require 'prometheus_client_addons/puma/plugin/prometheus_client_addons'
5
+
6
+ module PrometheusClientAddons
7
+ module Prometheus
8
+ module Client
9
+ class Puma < MultiMetric
10
+ class << self
11
+ attr_accessor :control_url, :control_auth_token
12
+ end
13
+
14
+ HANDLES = {
15
+ backlog: 'How many requests are waiting',
16
+ running: 'Number of running threads',
17
+ pool_capacity: 'Number of requests that can be processed right now',
18
+ max_threads: 'Maximum number of worker threads',
19
+
20
+ workers: 'Number of workers',
21
+ phase: 'Phase of worker',
22
+ booted_workers: 'Number of booted workers',
23
+ old_workers: 'Number of old workers',
24
+
25
+ worker_status_phase: 'Phase of worker',
26
+ worker_status_booted: 'Booted or not',
27
+ worker_status_last_checkin: 'Last update',
28
+
29
+ worker_status_last_status_backlog: 'How many objects have yet to be processed by the pool',
30
+ worker_status_last_status_running: 'Number of running threads',
31
+ worker_status_last_status_pool_capacity: 'Number of requests that can be processed right now',
32
+ worker_status_last_status_max_threads: 'Maximum number of worker threads'
33
+ }.freeze
34
+
35
+ def multi_name_type
36
+ full_handles = HANDLES.keys.map { |key| "#{prefix}#{key}" }.map(&:to_sym)
37
+ Hash[full_handles.zip([:gauge] * HANDLES.size)]
38
+ end
39
+
40
+ def multi_name_docstring
41
+ Hash[HANDLES.map { |key, value| ["#{prefix}#{key}".to_sym, value] }]
42
+ end
43
+
44
+ def extract(input, output, nested, label_set = {})
45
+ key = if input.key?(nested.last.to_s)
46
+ nested.last.to_s
47
+ elsif input.key?(nested.last.to_sym)
48
+ nested.last.to_sym
49
+ end
50
+ return unless key
51
+
52
+ output["#{prefix}#{nested.join('_')}".to_sym] ||= {}
53
+ output["#{prefix}#{nested.join('_')}".to_sym][label_set] = input[key]
54
+ end
55
+
56
+ def multi_values
57
+ body = Socket.unix(self.class.control_url.gsub("unix://", '')) do |socket|
58
+ socket << "GET /stats?token=#{self.class.control_auth_token} HTTP/1.0\r\n\r\n"
59
+ socket.read
60
+ end
61
+
62
+ data = JSON.parse(body.split("\n").last, symbolize_names: true)
63
+
64
+ result = {}
65
+
66
+ extract(data, result, [:backlog])
67
+ extract(data, result, [:running])
68
+ extract(data, result, [:pool_capacity])
69
+ extract(data, result, [:max_threads])
70
+
71
+ extract(data, result, [:workers])
72
+ extract(data, result, [:phase])
73
+ extract(data, result, [:booted_workers])
74
+ extract(data, result, [:old_workers])
75
+
76
+ if data.key?(:worker_status) && !data[:worker_status].empty?
77
+ data[:worker_status].each do |worker_status|
78
+ worker_index = worker_status[:index]
79
+
80
+ extract(worker_status, result, [:worker_status, :phase], { worker_index: worker_index })
81
+ extract(worker_status, result, [:worker_status, :booted], { worker_index: worker_index })
82
+ extract(worker_status, result, [:worker_status, :last_checkin], { worker_index: worker_index })
83
+
84
+ last_status = worker_status[:last_status]
85
+ extract(last_status, result, [:worker_status, :last_status, :backlog], { worker_index: worker_index })
86
+ extract(last_status, result, [:worker_status, :last_status, :running], { worker_index: worker_index })
87
+ extract(last_status, result, [:worker_status, :last_status, :pool_capacity], { worker_index: worker_index })
88
+ extract(last_status, result, [:worker_status, :last_status, :max_threads], { worker_index: worker_index })
89
+ end
90
+ end
91
+
92
+ result
93
+ end
94
+ end
95
+ end
96
+ end
97
+ end
@@ -0,0 +1,19 @@
1
+ require 'puma/plugin'
2
+
3
+ module PrometheusClientAddons
4
+ module Puma
5
+ module Plugin
6
+ ::Puma::Plugin.create do
7
+ def start(launcher)
8
+ control_url = launcher.options[:control_url]
9
+ raise StandardError, "Need Puma's activate_control_app" if control_url == nil
10
+
11
+ Prometheus::Client::Puma.tap do |puma|
12
+ puma.control_url = control_url
13
+ puma.control_auth_token = launcher.options[:control_auth_token]
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,3 @@
1
+ module PrometheusClientAddons
2
+ VERSION = "0.1.0"
3
+ end
@@ -0,0 +1,38 @@
1
+ lib = File.expand_path("../lib", __FILE__)
2
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
3
+ require "prometheus_client_addons/version"
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = "prometheus_client_addons"
7
+ spec.version = PrometheusClientAddons::VERSION
8
+ spec.authors = ["Anton Osenenko"]
9
+ spec.email = ["anton.osenenko@gmail.com"]
10
+
11
+ spec.summary = 'The missed parts of prometheus/client_ruby'
12
+ spec.description = 'The missed parts of prometheus/client_ruby'
13
+ spec.homepage = "https://github.com/a0s/prometheus_client_addons"
14
+ spec.license = "MIT"
15
+
16
+ if spec.respond_to?(:metadata)
17
+ spec.metadata["allowed_push_host"] = "https://rubygems.org"
18
+ spec.metadata["homepage_uri"] = spec.homepage
19
+ spec.metadata["source_code_uri"] = spec.homepage
20
+ else
21
+ raise "RubyGems 2.0 or newer is required to protect against " \
22
+ "public gem pushes."
23
+ end
24
+
25
+ spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
26
+ `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
27
+ end
28
+ spec.bindir = "exe"
29
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
30
+ spec.require_paths = ["lib"]
31
+
32
+ spec.add_dependency "prometheus-client", "~> 0.9.0"
33
+
34
+ spec.add_development_dependency "bundler", "~> 1.17"
35
+ spec.add_development_dependency "rake", "~> 10.0"
36
+ spec.add_development_dependency "rspec", "~> 3.0"
37
+ spec.add_development_dependency "puma"
38
+ end
metadata ADDED
@@ -0,0 +1,136 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: prometheus_client_addons
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Anton Osenenko
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2019-09-04 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: prometheus-client
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: 0.9.0
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: 0.9.0
27
+ - !ruby/object:Gem::Dependency
28
+ name: bundler
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '1.17'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '1.17'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rake
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '10.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '10.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rspec
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '3.0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '3.0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: puma
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ description: The missed parts of prometheus/client_ruby
84
+ email:
85
+ - anton.osenenko@gmail.com
86
+ executables: []
87
+ extensions: []
88
+ extra_rdoc_files: []
89
+ files:
90
+ - ".gitignore"
91
+ - ".rspec"
92
+ - ".travis.yml"
93
+ - Gemfile
94
+ - LICENSE.txt
95
+ - README.md
96
+ - Rakefile
97
+ - bin/console
98
+ - bin/setup
99
+ - lib/prometheus_client_addons.rb
100
+ - lib/prometheus_client_addons/prometheus/client/active_record.rb
101
+ - lib/prometheus_client_addons/prometheus/client/custom_collector.rb
102
+ - lib/prometheus_client_addons/prometheus/client/fake_gauge.rb
103
+ - lib/prometheus_client_addons/prometheus/client/formats/text_extended.rb
104
+ - lib/prometheus_client_addons/prometheus/client/gc.rb
105
+ - lib/prometheus_client_addons/prometheus/client/multi_metric.rb
106
+ - lib/prometheus_client_addons/prometheus/client/puma.rb
107
+ - lib/prometheus_client_addons/puma/plugin/prometheus_client_addons.rb
108
+ - lib/prometheus_client_addons/version.rb
109
+ - prometheus_client_addons.gemspec
110
+ homepage: https://github.com/a0s/prometheus_client_addons
111
+ licenses:
112
+ - MIT
113
+ metadata:
114
+ allowed_push_host: https://rubygems.org
115
+ homepage_uri: https://github.com/a0s/prometheus_client_addons
116
+ source_code_uri: https://github.com/a0s/prometheus_client_addons
117
+ post_install_message:
118
+ rdoc_options: []
119
+ require_paths:
120
+ - lib
121
+ required_ruby_version: !ruby/object:Gem::Requirement
122
+ requirements:
123
+ - - ">="
124
+ - !ruby/object:Gem::Version
125
+ version: '0'
126
+ required_rubygems_version: !ruby/object:Gem::Requirement
127
+ requirements:
128
+ - - ">="
129
+ - !ruby/object:Gem::Version
130
+ version: '0'
131
+ requirements: []
132
+ rubygems_version: 3.0.3
133
+ signing_key:
134
+ specification_version: 4
135
+ summary: The missed parts of prometheus/client_ruby
136
+ test_files: []