Class: Frankenstein::CollectedMetric
- Inherits:
-
Prometheus::Client::Metric
- Object
- Prometheus::Client::Metric
- Frankenstein::CollectedMetric
- Defined in:
- lib/frankenstein/collected_metric.rb
Overview
Populate metric data at scrape time
The usual implementation of a Prometheus registry is to create and register a suite of metrics at program initialization, and then instrument the running code by setting/incrementing/decrementing the metrics and their label sets as the program runs.
Sometimes, however, your program itself doesn't actually interact with the values that you want to return in your metrics, such as the counts of some external resource. You can hack around this by running something periodically in a thread to poll the external resource and update the value, but that's icky.
Instead, this class provides you with a way to say, "whenever we're scraped, run this block of code to generate the label sets and current values, and return that as part of the scrape data". This allows you to do away with ugly polling threads, and instead just write a simple "gather some data and return some numbers" block.
The block to run is passed to the Frankenstein::CollectedMetric
constructor, and must return a hash, containing the labelsets and
associated numeric values you want to return for the scrape. If your
block doesn't send back a hash, or raises an exception during execution,
no values will be returned for the metric, an error will be logged (if a
logger was specified), and the value of the
<metric>_collection_errors_total
counter, labelled by the exception
class
, will be incremented.
Performance & Concurrency
Bear in mind that the code that you specify for the collection action will be run on every scrape; if you've got two Prometheus servers, with a scrape interval of 30 seconds, you'll be running this code once every 15 seconds, forever. Also, Prometheus scrapes have a default timeout of five seconds. So, whatever your collection code does, make it snappy and low-overhead.
On a related note, remember that scrapes can arrive in parallel, so your collection code could potentially be running in parallel, too (depending on your metrics server). Thus, it must be thread-safe -- preferably, it should avoid mutating shared state at all.
Instance Attribute Summary collapse
-
#type ⇒ Object
readonly
The type of the metric being collected.
Instance Method Summary collapse
-
#get(labels = {}) ⇒ Object
Retrieve the value for the given labelset.
-
#initialize(name, docstring:, labels: [], type: :gauge, logger: Logger.new('/dev/null'), registry: Prometheus::Client.registry, &collector) ⇒ CollectedMetric
constructor
A new instance of CollectedMetric.
-
#values ⇒ Object
Retrieve a complete set of labels and values for the metric.
Constructor Details
#initialize(name, docstring:, labels: [], type: :gauge, logger: Logger.new('/dev/null'), registry: Prometheus::Client.registry, &collector) ⇒ CollectedMetric
Returns a new instance of CollectedMetric.
93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 |
# File 'lib/frankenstein/collected_metric.rb', line 93 def initialize(name, docstring:, labels: [], type: :gauge, logger: Logger.new('/dev/null'), registry: Prometheus::Client.registry, &collector) @validator = Prometheus::Client::LabelSetValidator.new(expected_labels: labels) validate_name(name) validate_docstring(docstring) @name = name @docstring = docstring @base_labels = {} validate_type(type) @type = type @logger = logger @registry = registry @collector = collector @errors_metric = @registry.counter(:"#{@name}_collection_errors_total", docstring: "Errors encountered while collecting for #{@name}") @registry.register(self) end |
Instance Attribute Details
#type ⇒ Object (readonly)
The type of the metric being collected.
59 60 61 |
# File 'lib/frankenstein/collected_metric.rb', line 59 def type @type end |
Instance Method Details
#get(labels = {}) ⇒ Object
Retrieve the value for the given labelset.
116 117 118 119 120 |
# File 'lib/frankenstein/collected_metric.rb', line 116 def get(labels = {}) @validator.validate_labelset!(labels) values[labels] end |
#values ⇒ Object
Retrieve a complete set of labels and values for the metric.
124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 |
# File 'lib/frankenstein/collected_metric.rb', line 124 def values begin @collector.call(self).tap do |results| unless results.is_a?(Hash) @logger.error(progname) { "Collector proc did not return a hash, got #{results.inspect}" } @errors_metric.increment(class: "NotAHashError") return {} end results.keys.each { |labelset| @validator.validate_labelset!(labelset) } end rescue StandardError => ex @logger.error(progname) { (["Exception in collection: #{ex.} (#{ex.class})"] + ex.backtrace).join("\n ") } @errors_metric.increment(class: ex.class.to_s) {} end end |