Skip to content

Commit ebf4a92

Browse files
committed
Add custom collectors to Prometheus registry
The gem `prometheus-client` doesn't support a custom collectors [issue 90](prometheus/client_ruby#90). This PL add missing functionality, but only if the management server is used.
1 parent 58df65f commit ebf4a92

File tree

5 files changed

+62
-19
lines changed

5 files changed

+62
-19
lines changed

CHANGELOG.md

+4
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

10+
### Added
11+
12+
- Ability to use custom metric collectors
13+
1014
### Changed
1115

1216
- __Breaking__ The class `BM::Instrumentations::Aws::Collector` turned into gem private,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
# frozen_string_literal: true
2+
3+
module BM
4+
module Instrumentations
5+
# Extends the Prometheus registry to support custom metric collectors
6+
#
7+
# @see https://github.com/prometheus/client_ruby/issues/90
8+
module PrometheusRegistryCustomCollectors
9+
# Initialize custom_collectors array
10+
def initialize(*args, **kwargs)
11+
super(*args, **kwargs)
12+
@custom_collectors = []
13+
end
14+
15+
# Registers an updater that poll and update metrics periodically
16+
#
17+
# @param collector [Proc]
18+
def add_custom_collector(&collector)
19+
@mutex.synchronize do
20+
@custom_collectors << collector
21+
end
22+
end
23+
24+
# Invokes all registered custom collectors to update metrics
25+
#
26+
# @return [void]
27+
def custom_collectors!
28+
@custom_collectors.each(&:call)
29+
end
30+
end
31+
end
32+
end
33+
34+
Prometheus::Client::Registry.prepend(BM::Instrumentations::PrometheusRegistryCustomCollectors)

lib/bm/instrumentations/management/server.rb

+7-16
Original file line numberDiff line numberDiff line change
@@ -16,18 +16,14 @@ module Management
1616
# @param host [String] is a bind address that a server uses for listening (default: `0.0.0.0`)
1717
# @param logger [Logger, nil] is a logger instance for notifications (default: `Logger.new($stdout)`)
1818
# @param registry [Prometheus::Client::Registry, nil] override a default Prometheus registry
19-
# @param puma_launcher [Puma::Launcher, nil]
2019
#
2120
# @return [Server]
22-
def self.server(port: nil, host: nil, logger: nil, registry: nil, puma_launcher: nil)
23-
registry ||= ::Prometheus::Client.registry
24-
puma_metrics = puma_launcher ? Puma::Collector.new(registry: registry, launcher: puma_launcher) : nil
21+
def self.server(port: nil, host: nil, logger: nil, registry: nil)
2522
Server.new(
2623
port: port || 9990,
2724
host: host || '0.0.0.0',
2825
logger: logger || ::Logger.new($stdout, progname: Server.name),
29-
registry: registry,
30-
puma_metrics: puma_metrics
26+
registry: registry || ::Prometheus::Client.registry
3127
)
3228
end
3329

@@ -45,9 +41,8 @@ def self.server(port: nil, host: nil, logger: nil, registry: nil, puma_launcher:
4541
# @attr [Logger] logger
4642
# @attr [Prometheus::Client::Registry] registry
4743
# @attr [Puma::Events] events
48-
# @attr [Puma::Collector, nil] puma_metrics
4944
class Server
50-
attr_reader :host, :port, :logger, :registry, :events, :puma_metrics
45+
attr_reader :host, :port, :logger, :registry, :events
5146

5247
# The socket backlog value
5348
BACKLOG = 3
@@ -65,15 +60,13 @@ class Server
6560
# @param port [Integer]
6661
# @param logger [Logger]
6762
# @param registry [Prometheus::Client::Registry]
68-
# @param puma_metrics [Puma::Collector, nil]
6963
#
7064
# @api private
71-
def initialize(port:, host:, logger:, registry:, puma_metrics: nil)
65+
def initialize(port:, host:, logger:, registry:)
7266
@host = host
7367
@port = port
7468
@logger = logger
7569
@registry = registry
76-
@puma_metrics = puma_metrics
7770
end
7871

7972
# Creates a management server backed by {Puma::Server} then bind and
@@ -119,7 +112,7 @@ def puma_options
119112
#
120113
# @return [#call] a frozen rack application
121114
def rack_app
122-
::Rack::ShowExceptions.new(ServeEndpoints.new(registry, puma_metrics)).freeze
115+
::Rack::ShowExceptions.new(ServeEndpoints.new(registry)).freeze
123116
end
124117

125118
# Handles a running server instance
@@ -161,10 +154,8 @@ class ServeEndpoints
161154
NOT_FOUND = [404, PLAIN_TEXT, ['not found']].freeze
162155

163156
# @param registry [Prometheus::Client::Registry]
164-
# @param puma_metrics [Puma::Metrics, nil]
165-
def initialize(registry, puma_metrics)
157+
def initialize(registry)
166158
@registry = registry
167-
@puma_metrics = puma_metrics
168159
end
169160

170161
# @param env [Hash<String, Any>]
@@ -190,7 +181,7 @@ def call(env) # rubocop:disable Metrics/MethodLength
190181

191182
# @return [(Integer, Hash<String, String>, Array<String>)]
192183
def metrics
193-
@puma_metrics&.update
184+
@registry.custom_collectors! if @registry.respond_to?(:custom_collectors!)
194185

195186
text = Prometheus::Client::Formats::Text.marshal(@registry)
196187
[200, METRICS_TEXT, [text]]

lib/bm/instrumentations/puma/collector.rb

+14-1
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,9 @@
22

33
require 'socket'
44
require 'puma'
5-
require 'tcp_server_socket_backlog/tcp_server_socket_backlog'
65

6+
require 'tcp_server_socket_backlog/tcp_server_socket_backlog'
7+
require 'bm/instrumentations/internal/prometheus_registry_custom_collectors'
78
require_relative 'metrics_collection'
89

910
module BM
@@ -29,6 +30,11 @@ def initialize(registry:, launcher:)
2930
metrics_collection.server_version(::Puma::Server::VERSION)
3031
end
3132

33+
# @return [Proc]
34+
def to_proc
35+
-> { update }
36+
end
37+
3238
# Updates Puma metrics in the registry
3339
def update
3440
metrics_collection.update_stats(launcher.stats)
@@ -39,6 +45,13 @@ def update
3945
metrics_collection.update_backlog(listener: index, backlog: backlog) if backlog
4046
end
4147
end
48+
49+
# @param launcher [Puma::Launcher]
50+
# @param registry [Prometheus::Client::Registry, nil]
51+
def self.install(launcher, registry: nil)
52+
registry ||= Prometheus::Client.registry
53+
registry.add_custom_collector(&Collector.new(registry: registry, launcher: launcher))
54+
end
4255
end
4356
end
4457
end

lib/puma/plugin/management_server.rb

+3-2
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
require 'puma/dsl'
44
require 'puma/plugin'
55
require 'bm/instrumentations'
6+
require 'bm/instrumentations/puma/collector'
67

78
module BM
89
module Instrumentations
@@ -40,13 +41,13 @@ def start(launcher) # rubocop:disable Metrics/MethodLength
4041
host: launcher.options[:management_server_host],
4142
port: launcher.options[:management_server_port],
4243
logger: launcher.options[:management_server_logger],
43-
registry: launcher.options[:management_server_registry],
44-
puma_launcher: launcher
44+
registry: launcher.options[:management_server_registry]
4545
}
4646

4747
# @type [Puma::Events]
4848
events = launcher.events
4949
server = BM::Instrumentations::Management.server(**args)
50+
BM::Instrumentations::Puma::Collector.install(launcher, registry: args[:registry])
5051

5152
events.on_booted do
5253
running = server.run

0 commit comments

Comments
 (0)