mirror of
https://github.com/puppetlabs/vmpooler.git
synced 2026-01-26 01:58:41 -05:00
It appears we renamed `/ondemand/` to `/ondemandvm/` at some point and, as a result, have not been stripping hostnames from that endpoint's metrics. This has caused issues with metrics collection due a very high cardinality.
124 lines
4.5 KiB
Ruby
124 lines
4.5 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
# This is an adapted Collector module for vmpooler based on the sample implementation
|
|
# available in the prometheus client_ruby library
|
|
# https://github.com/prometheus/client_ruby/blob/master/lib/prometheus/middleware/collector.rb
|
|
#
|
|
# The code was also failing Rubocop on PR check, so have addressed all the offenses.
|
|
#
|
|
# The method strip_hostnames_from_path (originally strip_ids_from_path) has been adapted
|
|
# to add a match for hostnames in paths # to replace with a single ":hostname" string to
|
|
# avoid # proliferation of stat lines for # each new vm hostname deleted, modified or
|
|
# otherwise queried.
|
|
|
|
require 'benchmark'
|
|
require 'prometheus/client'
|
|
require 'vmpooler/logger'
|
|
|
|
module Vmpooler
|
|
class Metrics
|
|
class Promstats
|
|
# CollectorMiddleware is an implementation of Rack Middleware customised
|
|
# for vmpooler use.
|
|
#
|
|
# By default metrics are registered on the global registry. Set the
|
|
# `:registry` option to use a custom registry.
|
|
#
|
|
# By default metrics all have the prefix "http_server". Set to something
|
|
# else if you like.
|
|
#
|
|
# The request counter metric is broken down by code, method and path by
|
|
# default. Set the `:counter_label_builder` option to use a custom label
|
|
# builder.
|
|
#
|
|
# The request duration metric is broken down by method and path by default.
|
|
# Set the `:duration_label_builder` option to use a custom label builder.
|
|
#
|
|
# Label Builder functions will receive a Rack env and a status code, and must
|
|
# return a hash with the labels for that request. They must also accept an empty
|
|
# env, and return a hash with the correct keys. This is necessary to initialize
|
|
# the metrics with the correct set of labels.
|
|
class CollectorMiddleware
|
|
attr_reader :app, :registry
|
|
|
|
def initialize(app, options = {})
|
|
@app = app
|
|
@registry = options[:registry] || Prometheus::Client.registry
|
|
@metrics_prefix = options[:metrics_prefix] || 'http_server'
|
|
|
|
init_request_metrics
|
|
init_exception_metrics
|
|
end
|
|
|
|
def call(env) # :nodoc:
|
|
trace(env) { @app.call(env) }
|
|
end
|
|
|
|
protected
|
|
|
|
def init_request_metrics
|
|
@requests = @registry.counter(
|
|
:"#{@metrics_prefix}_requests_total",
|
|
docstring:
|
|
'The total number of HTTP requests handled by the Rack application.',
|
|
labels: %i[code method path]
|
|
)
|
|
@durations = @registry.histogram(
|
|
:"#{@metrics_prefix}_request_duration_seconds",
|
|
docstring: 'The HTTP response duration of the Rack application.',
|
|
labels: %i[method path]
|
|
)
|
|
end
|
|
|
|
def init_exception_metrics
|
|
@exceptions = @registry.counter(
|
|
:"#{@metrics_prefix}_exceptions_total",
|
|
docstring: 'The total number of exceptions raised by the Rack application.',
|
|
labels: [:exception]
|
|
)
|
|
end
|
|
|
|
def trace(env)
|
|
response = nil
|
|
duration = Benchmark.realtime { response = yield }
|
|
record(env, response.first.to_s, duration)
|
|
response
|
|
rescue StandardError => e
|
|
@exceptions.increment(labels: { exception: e.class.name })
|
|
raise
|
|
end
|
|
|
|
def record(env, code, duration)
|
|
counter_labels = {
|
|
code: code,
|
|
method: env['REQUEST_METHOD'].downcase,
|
|
path: strip_hostnames_from_path(env['PATH_INFO'])
|
|
}
|
|
|
|
duration_labels = {
|
|
method: env['REQUEST_METHOD'].downcase,
|
|
path: strip_hostnames_from_path(env['PATH_INFO'])
|
|
}
|
|
|
|
@requests.increment(labels: counter_labels)
|
|
@durations.observe(duration, labels: duration_labels)
|
|
rescue # rubocop:disable Style/RescueStandardError
|
|
nil
|
|
end
|
|
|
|
def strip_hostnames_from_path(path)
|
|
# Custom for /vm path - so we just collect aggrate stats for all usage along this one
|
|
# path. Custom counters are then added more specific endpoints in v1.rb
|
|
# Since we aren't parsing UID/GIDs as in the original example, these are removed.
|
|
# Similarly, request IDs are also stripped from the /ondemand path.
|
|
path
|
|
.gsub(%r{/vm/.+$}, '/vm')
|
|
.gsub(%r{/ondemandvm/.+$}, '/ondemandvm')
|
|
.gsub(%r{/token/.+$}, '/token')
|
|
.gsub(%r{/lib/.+$}, '/lib')
|
|
.gsub(%r{/img/.+$}, '/img')
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|