Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Potentially documentation hole in describe_* macros #541

Open
xd009642 opened this issue Nov 2, 2024 · 1 comment
Open

Potentially documentation hole in describe_* macros #541

xd009642 opened this issue Nov 2, 2024 · 1 comment

Comments

@xd009642
Copy link
Contributor

xd009642 commented Nov 2, 2024

So in counter!() from the examples I can do counter!("total_calls", "protocol" => "http"); But then how would I then use describe_counter! to describe this newly created counter? Maybe this is exporter specific but it feels like there's some documentation gap with how to refer to the same metric across different macro invocations.

Willing to help out with docs once I grok how it all works 🙏

@catbrained
Copy link
Contributor

As far as I understand it, you refer to the same metric by using the same name (and potentially the same labels). So in your example, "total_calls".
You can register a metric and give it a description in one step, by using the describe_counter! macro.
At least that's how it seems to be intended. The metrics crate provides the trait, but how a particular Recorder implements it can vary.

If you look at the description of describe_counter() for example, it says that it is implementation specific. A particular Recorder could disallow adding a description to a metric if it already exists, or it could ignore descriptions entirely, or it could do whatever it deems sensible.

To use the metrics-exporter-prometheus as an example, if you look at its implementation of Recorder, you can see that all the methods either get and update the metric if it exists, or they create the metric if it doesn't exist yet.
The metric is looked up by its Key or by its name. The Key contains both the name and the labels, and the name is only, well, the name.

The code in question:

impl PrometheusRecorder {
/// Gets a [`PrometheusHandle`] to this recorder.
pub fn handle(&self) -> PrometheusHandle {
PrometheusHandle { inner: self.inner.clone() }
}
fn add_description_if_missing(
&self,
key_name: &KeyName,
description: SharedString,
unit: Option<Unit>,
) {
let sanitized = sanitize_metric_name(key_name.as_str());
let mut descriptions =
self.inner.descriptions.write().unwrap_or_else(PoisonError::into_inner);
descriptions.entry(sanitized).or_insert((description, unit));
}
}
impl From<Inner> for PrometheusRecorder {
fn from(inner: Inner) -> Self {
PrometheusRecorder { inner: Arc::new(inner) }
}
}
impl Recorder for PrometheusRecorder {
fn describe_counter(&self, key_name: KeyName, unit: Option<Unit>, description: SharedString) {
self.add_description_if_missing(&key_name, description, unit);
}
fn describe_gauge(&self, key_name: KeyName, unit: Option<Unit>, description: SharedString) {
self.add_description_if_missing(&key_name, description, unit);
}
fn describe_histogram(&self, key_name: KeyName, unit: Option<Unit>, description: SharedString) {
self.add_description_if_missing(&key_name, description, unit);
}
fn register_counter(&self, key: &Key, _metadata: &Metadata<'_>) -> Counter {
self.inner.registry.get_or_create_counter(key, |c| c.clone().into())
}
fn register_gauge(&self, key: &Key, _metadata: &Metadata<'_>) -> Gauge {
self.inner.registry.get_or_create_gauge(key, |c| c.clone().into())
}
fn register_histogram(&self, key: &Key, _metadata: &Metadata<'_>) -> Histogram {
self.inner.registry.get_or_create_histogram(key, |c| c.clone().into())
}
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants