`prometheus` backend for `metrics` crate

Overview

metrics + prometheus = ❤️

crates.io Rust 1.65+ Unsafe Forbidden
CI Rust docs

API Docs | Changelog

prometheus backend for metrics crate.

Motivation

Rust has at least two ecosystems regarding metrics collection:

As the result, some crates use prometheus crate for providing their metrics, and another crates do use metrics crate for that. Furthermore, prometheus and metrics crates are designed quite differently, making their composition a non-trivial task. This crate aims to mitigate this gap, allowing to combine both prometheus and metrics ecosystems in a single project.

Alternatives

If you're not obligated to deal with prometheus crate directly or via third-party crates which do use it, consider the metrics-exporter-prometheus crate, which provides a simple Prometheus backend for metrics facade, without bringing in the whole prometheus crate's machinery.

Overview

This crate provides a metrics::Recorder implementation, allowing to work with a prometheus::Registry via metrics facade.

It comes in 3 flavours, allowing to choose the smallest performance overhead depending on a use case:

Not any prometheus metric is supported, because metrics crate implies only few of them. This is how the metrics crate's metrics are mapped onto prometheus ones:

prometheus::MetricVec types are used whenever any labels are specified via metrics facade.

To satisfy the metrics::Recorder's requirement of allowing changing metrics description anytime after its registration (prometheus crate doesn't imply and allow that), the Describable wrapper is used, allowing to arc-swap the description.

// By default `prometheus::default_registry()` is used.
let recorder = metrics_prometheus::install();

// Either use `metrics` crate interfaces.
metrics::increment_counter!("count", "whose" => "mine", "kind" => "owned");
metrics::increment_counter!("count", "whose" => "mine", "kind" => "ref");
metrics::increment_counter!("count", "kind" => "owned", "whose" => "dummy");

// Or construct and provide `prometheus` metrics directly.
recorder.register_metric(prometheus::Gauge::new("value", "help")?);

let report = prometheus::TextEncoder::new()
    .encode_to_string(&prometheus::default_registry().gather())?;
assert_eq!(
    report.trim(),
    r#"
## HELP count count
## TYPE count counter
count{kind="owned",whose="dummy"} 1
count{kind="owned",whose="mine"} 1
count{kind="ref",whose="mine"} 1
## HELP value help
## TYPE value gauge
value 0
    "#
    .trim(),
);

// Metrics can be described anytime after being registered in
// `prometheus::Registry`.
metrics::describe_counter!("count", "Example of counter.");
metrics::describe_gauge!("value", "Example of gauge.");

let report = prometheus::TextEncoder::new()
    .encode_to_string(&recorder.registry().gather())?;
assert_eq!(
    report.trim(),
    r#"
## HELP count Example of counter.
## TYPE count counter
count{kind="owned",whose="dummy"} 1
count{kind="owned",whose="mine"} 1
count{kind="ref",whose="mine"} 1
## HELP value Example of gauge.
## TYPE value gauge
value 0
    "#
    .trim(),
);

// Description can be changed multiple times and anytime.
metrics::describe_counter!("count", "Another description.");

// Even before a metric is registered in `prometheus::Registry`.
metrics::describe_counter!("another", "Yet another counter.");
metrics::increment_counter!("another");

let report = prometheus::TextEncoder::new()
    .encode_to_string(&recorder.registry().gather())?;
assert_eq!(
    report.trim(),
    r#"
## HELP another Yet another counter.
## TYPE another counter
another 1
## HELP count Another description.
## TYPE count counter
count{kind="owned",whose="dummy"} 1
count{kind="owned",whose="mine"} 1
count{kind="ref",whose="mine"} 1
## HELP value Example of gauge.
## TYPE value gauge
value 0
    "#
    .trim(),
);
# Ok::<_, prometheus::Error>(())

Limitations

Since prometheus crate validates the metrics format very strictly, not everything, expressed via metrics facade, may be put into a prometheus::Registry, ending up with a prometheus::Error being emitted.

  • Metric names cannot be namespaced with dots (and should follow Prometheus format).

    metrics_prometheus::install();
    
    // panics: 'queries.count' is not a valid metric name
    metrics::increment_counter!("queries.count");
  • The same metric should use always the same set of labels:

    metrics_prometheus::install();
    
    metrics::increment_counter!("count");
    // panics: Inconsistent label cardinality, expect 0 label values, but got 1
    metrics::increment_counter!("count", "whose" => "mine");
    metrics_prometheus::install();
    
    metrics::increment_counter!("count", "kind" => "owned");
    // panics: label name kind missing in label map
    metrics::increment_counter!("count", "whose" => "mine");
    metrics_prometheus::install();
    
    metrics::increment_counter!("count", "kind" => "owned");
    // panics: Inconsistent label cardinality, expect 1 label values, but got 2
    metrics::increment_counter!("count", "kind" => "ref", "whose" => "mine");
  • The same name cannot be used for different types of metrics:

    metrics_prometheus::install();
    
    metrics::increment_counter!("count");
    // panics: Duplicate metrics collector registration attempted
    metrics::increment_gauge!("count", 1.0);
  • Any metric registered in a prometheus::Registry directly, without using metrics or this crate interfaces, is not usable via metrics facade and will cause a prometheus::Error.

    metrics_prometheus::install();
    
    prometheus::default_registry()
        .register(Box::new(prometheus::Gauge::new("value", "help")?))?;
    
    // panics: Duplicate metrics collector registration attempted
    metrics::increment_gauge!("value", 4.5);
    # Ok::<_, prometheus::Error>(())
  • metrics::Units are not supported, as Prometheus has no notion of ones. Specifying them via metrics macros will be no-op.

prometheus::Error handling

Since metrics::Recorder doesn't expose any errors in its API, the emitted prometheus::Errors can be either turned into a panic, or just silently ignored, returning a no-op metric instead (see metrics::Counter::noop() for example).

This can be tuned by providing a failure::Strategy when building a Recorder.

use metrics_prometheus::failure::strategy;

metrics_prometheus::Recorder::builder()
    .with_failure_strategy(strategy::NoOp)
    .build_and_install();

// `prometheus::Error` is ignored inside.
metrics::increment_counter!("invalid.name");

let stats = prometheus::default_registry().gather();
assert_eq!(stats.len(), 0);

The default failure::Strategy is PanicInDebugNoOpInRelease. See failure::strategy module for other available failure::Strategys, or provide your own one by implementing the failure::Strategy trait.

License

Copyright © 2022 Instrumentisto Team, https://github.com/instrumentisto

This software is subject to the terms of the Blue Oak Model License 1.0.0. If a copy of the BlueOak-1.0.0 license was not distributed with this file, You can obtain one at https://blueoakcouncil.org/license/1.0.0.

You might also like...
Export statistics of Mosquitto MQTT broker (topic: $SYS) to Prometheus

Preface The Mosquitto MQTT broker provides a number of statistics on the special $SYS/# topic (see mosquitto(8)). Build requirements As a Rust program

Prometheus instrumentation service for the NGINX RTMP module.

nginx-rtmp-exporter Prometheus instrumentation service for the NGINX RTMP module. Usage nginx-rtmp-exporter [OPTIONS] --scrape-url SCRAPE_URL O

A metrics collection application for Linux machines. Created for MSCS 710 Project at Marist College.

Linux-Metrics-Collector A metrics collection application for Linux machines. Created for MSCS 710 Project at Marist College. Development Environment S

🚀 10x easier, 🚀 10x cheaper, 🚀 high performance,  🚀 petabyte scale - Elasticsearch/Splunk/Datadog alternative for 🚀 (logs, metrics, traces).
🚀 10x easier, 🚀 10x cheaper, 🚀 high performance, 🚀 petabyte scale - Elasticsearch/Splunk/Datadog alternative for 🚀 (logs, metrics, traces).

🚀 10x easier, 🚀 10x cheaper, 🚀 petabyte scale - Elasticsearch/Splunk/Datadog alternative for 🚀 (logs, metrics, traces). ZincObserve ZincObserve is

Druid Exporter plays a fundamental role as a receiver of metrics events coming from Druid clusters, adopting the HTTP format as a means of communication
Druid Exporter plays a fundamental role as a receiver of metrics events coming from Druid clusters, adopting the HTTP format as a means of communication

Druid Exporter plays a fundamental role as a receiver of metrics events coming from Druid clusters, adopting the HTTP format as a means of communication. In addition to this capability, its primary function is to export these metrics to Prometheus, thus allowing the creation of meaningful graphs and visualizations.

Druid Exporter plays a fundamental role as a receiver of metrics events coming from Druid clusters, adopting the HTTP format as a means of communication.
Druid Exporter plays a fundamental role as a receiver of metrics events coming from Druid clusters, adopting the HTTP format as a means of communication.

Druid Exporter plays a fundamental role as a receiver of metrics events coming from Druid clusters, adopting the HTTP format as a means of communication. In addition to this capability, its primary function is to export these metrics to Prometheus, thus allowing the creation of meaningful graphs and visualizations.

Revolt backend API server, built with Rust.

Delta Description Delta is a blazing fast API server built with Rust for Revolt. Features: Robust and efficient API routes for running a chat platform

Bioyino is a distributed statsd-protocol server with carbon backend.

Bioyino The StatsD server written in Rust Description Bioyino is a distributed statsd-protocol server with carbon backend. Features all basic metric t

Rust crate for configurable parallel web crawling, designed to crawl for content

url-crawler A configurable parallel web crawler, designed to crawl a website for content. Changelog Docs.rs Example extern crate url_crawler; use std:

Comments
  • `Recorder` freezing

    `Recorder` freezing

    Synopsis

    Quite often, the use case doesn't require changing prometheus metrics after they've been initialized on startup. In such cases, we still do pay for locks synchronization on hot paths, while we don't really need them, and interior mutability as a whole, and we could have much better performance just by looking up the immutable HashMap by a reference.

    Solution

    This PR implements two other kinds of metrics::Recorder:

    1. FreezableRecorder, which works the same way as usual Recorder, but may be .freeze()d at some point of time, after which it's not capable to register new metrics on the fly, but can change descriptions of registered ones, and provides much better performance for a metric lookup on hot paths: AtomicBool loading + HashMap lookup + Arc cloning.

    2. FrozenRecorder, which is created already being frozen, so is not capable of registering any metrics (but can change descriptions of registered ones) on the fly, but providing even better performance for a metric lookup on hot paths: HashMap lookup + Arc cloning.

    Checklist

    • Created PR:
      • [x] In draft mode
      • [x] Name contains issue reference
      • [x] Has type and k:: labels applied
      • [x] Has assignee
    • Before review:
      • [x] Documentation is updated (if required)
      • [x] Tests are updated (if required)
      • [x] Changes conform code style
      • [x] CHANGELOG entry is added (if required)
      • [x] FCM (final commit message) is posted or updated
      • [x] Draft mode is removed
    • [x] Review is completed and changes are approved
      • [x] FCM (final commit message) is approved
    • Before merge:
      • [x] Milestone is set
      • [x] PR's name and description are correct and up-to-date
      • [x] All temporary labels are removed
    feature k::api k::design k::performance semver::feature 
    opened by tyranron 0
  • Switch from `must_*` to `try_*` naming convetion

    Switch from `must_*` to `try_*` naming convetion

    Synopsis

    At the moment, we use must_* naming conventions for panicking functions.

    Solution

    Seems to be more idiomatic to use try_* naming convention for fallible functions instead.

    Checklist

    • Created PR:
      • [x] In draft mode
      • [x] Name contains issue reference
      • [x] Has type and k:: labels applied
      • [x] Has assignee
    • Before review:
      • [x] Documentation is updated (if required)
      • [x] Tests are updated (if required)
      • [x] Changes conform code style
      • [x] CHANGELOG entry is added (if required)
      • [x] FCM (final commit message) is posted or updated
      • [x] Draft mode is removed
    • [x] Review is completed and changes are approved
      • [x] FCM (final commit message) is approved
    • Before merge:
      • [x] Milestone is set
      • [x] PR's name and description are correct and up-to-date
      • [x] All temporary labels are removed
    enhancement k::api k::refactor semver::breaking 
    opened by tyranron 0
  • Strip redundant `Arc` allocation on hot pahs

    Strip redundant `Arc` allocation on hot pahs

    Requires tikv/rust-prometheus#472

    Synopsis

    There are some redundant Arc allocations on hot paths due to upstream APIs.

    Solution

    Eliminate these allocations by re-using the inner Arc of metrics from prometheus crate.

    Checklist

    • Created PR:
      • [x] In draft mode
      • [x] Name contains issue reference
      • [x] Has type and k:: labels applied
      • [x] Has assignee
    • Before review:
      • [ ] Documentation is updated (if required)
      • [ ] Tests are updated (if required)
      • [ ] Changes conform code style
      • [ ] CHANGELOG entry is added (if required)
      • [ ] FCM (final commit message) is posted or updated
      • [ ] Draft mode is removed
    • [ ] Review is completed and changes are approved
      • [ ] FCM (final commit message) is approved
    • Before merge:
      • [ ] Milestone is set
      • [ ] PR's name and description are correct and up-to-date
      • [ ] All temporary labels are removed
    enhancement k::performance k::toolchain 
    opened by tyranron 0
An asynchronous dumb exporter proxy for prometheus. This aggregates all the metrics and exposes as a single scrape endpoint.

A dumb light weight asynchronous exporter proxy This is a dumb lightweight asynchronous exporter proxy that will help to expose multiple application m

Dark streams 3 Dec 4, 2022
Easily add metrics to your system -- and actually understand them using automatically customized Prometheus queries

Autometrics ?? ✨ Autometrics is a macro that makes it trivial to add useful metrics to any function in your codebase. Easily understand and debug your

Fiberplane 341 Feb 6, 2023
Easily add metrics to your system -- and actually understand them using automatically customized Prometheus queries

A Rust macro that makes it easy to understand the error rate, response time, and production usage of any function in your code. Jump from your IDE to

Autometrics 462 Mar 6, 2023
A minimal, allocation-free Prometheus/OpenMetrics metrics implementation for `no-std` and embedded Rust.

tinymetrics a minimal, allocation-free Prometheus/OpenMetrics metrics implementation for no-std and embedded projects. why should you use it? you may

Eliza Weisman 282 Apr 16, 2023
Parse, edit and merge Prometheus metrics exposition format

promerge Promerge provides minimalistic and easy to use API to parse and manipulate Prometheus metrics. A simple usecase could be collecting metrics f

Mike Taghavi 4 Aug 12, 2023
🔍 Fully-featured metrics collection agent for First Tech Challenge competitions. Supports Prometheus.

Scout Scout is a fully-featured free and open source metrics collector for FTC competitions. The project is licensed under the GNU LGPLv3 license. Fea

hivemind 3 Oct 24, 2023
An asynchronous Prometheus exporter for iptables

iptables_exporter An asynchronous Prometheus exporter for iptables iptables_exporter runs iptables-save --counter and scrapes the output to build Prom

Kevin K. 21 Dec 29, 2022
A Prometheus Aggregation Gateway for FAAS applications

Gravel Gateway Gravel Gateway is a Prometheus Push Gateway for FAAS applications. In particular it allows aggregation to be controlled by the incoming

Colin Douch 85 Nov 23, 2022
A Prometheus Aggregation Gateway for FAAS applications

Gravel Gateway Gravel Gateway is a Prometheus Push Gateway for FAAS applications. In particular it allows aggregation to be controlled by the incoming

Colin Douch 85 Nov 23, 2022
A Prometheus exporter for WireGuard

wireguard_exporter An asynchronous Prometheus exporter for wireguard wireguard_exporter runs wg show [..] and scrapes the output to build Prometheus m

Kevin K. 15 Dec 29, 2022