Code Monkey home page Code Monkey logo

metrics-prometheus-rs's Introduction

metrics + prometheus = ❤️

crates.io Rust 1.72+ 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::counter!("count", "whose" => "mine", "kind" => "owned").increment(1);
metrics::counter!("count", "whose" => "mine", "kind" => "ref").increment(1);
metrics::counter!("count", "kind" => "owned", "whose" => "dummy").increment(1);

// 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::counter!("another").increment(1);

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::counter!("queries.count").increment(1);
  • The same metric should use always the same set of labels:

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

    metrics_prometheus::install();
    
    metrics::counter!("count").increment(1);
    // panics: Duplicate metrics collector registration attempted
    metrics::gauge!("count").increment(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::gauge!("value").increment(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.

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::counter!("invalid.name").increment(1);

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-2024 Instrumentisto Team, https://github.com/instrumentisto

Licensed under either of Apache License, Version 2.0 or MIT license at your option.

Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in this crate by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.

metrics-prometheus-rs's People

Contributors

tyranron avatar ilslv avatar dependabot[bot] avatar zohnannor avatar

Stargazers

Croxx avatar  avatar Saturn IDC avatar dzmitry-lahoda avatar Denis Kolodin avatar Stanislav Tkach avatar

Watchers

James Cloos avatar  avatar  avatar

metrics-prometheus-rs's Issues

BlueOak license

Related to github/choosealicense.com#816.

Thank you for writing a great integration library for metrics and prometheus!

Background

When I ran our license checker, I noticed that metrics-prometheus used the BlueOak 1.0.0 license:

cargo deny check
error[L001]: failed to satisfy license requirements
  ┌─ metrics-prometheus 0.4.1 (registry+https://github.com/rust-lang/crates.io-index):4:12
  │
4 │ license = "BlueOak-1.0.0"
  │            ^^^^^^^^^^^^^

As far I can tell, this license does not appear on the OSI approved license list or the Free Software Foundation license list. This means that if we want to use metrics-prometheus, we would probably need to pay a lawyer to review the license, or wait until either the OSI or the FSF finishes their review (which may take a while). Also, without FSF approval, I'm not sure whether libraries using BlueOak should be linked into programs under the GPL.

Question

Do you feel particularly strongly about using the Blue Oak license? If so, no problem! You should use whatever license you want. (And I can always write my own metrics plugin for Prometheus.)

Or would you be willing to consider dual licensing metrics-prometheus under a more common open source license? For example, much of the Rust project uses:

license = "Apache-2.0 OR MIT"

If you wanted to dual-license your project, you could use something like:

license = "BlueOak-1.0.0 OR Apache-2.0"
# Or
license = "BlueOak-1.0.0 OR Apache-2.0 OR MIT"

But once again, it's your library, and you should of course license it however you prefer.

Once again, thanking you for writing a very helpful library!

bug: compatible with metrics 0.23

Summary

Thank you for the great lib. I was using metrics = "0.22" with metrics-prometheus = "0.6", they worked well. But after I upgrade metrics to 0.23, all data of my metrics are gone.

Steps to reproduce

Use version 0.6 with metrics 0.23

What is the current bug behavior?

Cannot get my metrics data like with metrics 0.22

What is the expected correct behavior?

Relevant logs and/or screenshots

Possible fixes

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.