Skip to content

Example metrics server using Hyper #94

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

Merged
merged 2 commits into from
Sep 27, 2022

Conversation

adamchalmers
Copy link
Contributor

@adamchalmers adamchalmers commented Sep 19, 2022

Say your Rust app is not a web server. You probably don't need to pull in a whole web framework like Axum or Actix-web just to serve one little OpenMetrics endpoint. You can just use Hyper instead. Here's an example of how to do that.

I also added this an example target in Cargo.toml, so that you can run it with cargo:

$ cargo run --example hyper  &
$ curl localhost:8001
# HELP tokio_hyper_example_example_counter An example counter that will get incremented automatically.
# TYPE tokio_hyper_example_example_counter counter
tokio_hyper_example_example_counter_total 6
# EOF

@adamchalmers adamchalmers force-pushed the achalmers/hyper-example branch from 9400593 to 5021289 Compare September 19, 2022 22:36
Copy link
Member

@mxinden mxinden left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚀 Thanks for the help. Very much appreciated.

Response::builder()
.header(
hyper::header::CONTENT_TYPE,
"application/openmetrics-text; version=1.0.0; charset=utf-8",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🙏

Comment on lines 28 to 31
loop {
EXAMPLE_COUNTER.inc();
tokio::time::sleep(Duration::from_secs(1)).await;
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a neat idea, though for the sake of consistency with the other examples, I would prefer showcasing a standard HTTP request counter in this example.

See tide example:

#[derive(Clone, Hash, PartialEq, Eq, Encode)]
struct Labels {
method: Method,
path: String,
}
#[derive(Clone, Hash, PartialEq, Eq, Encode)]
enum Method {
Get,
Put,
}

I think an example should showcase a single thing only. (Though it is hard where the boundaries of a thing are.) I am fine for something like the above to be shown in a separate example.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree that examples should only show a single thing. From my point of view, having an example counter simplifies the example, because this is really just teaching users how to serve a metrics endpoint using Hyper. It's not really about how metrics are created and used, just about serving them.

@adamchalmers adamchalmers force-pushed the achalmers/hyper-example branch 5 times, most recently from 53f15ad to 99c62f4 Compare September 21, 2022 17:17
Copy link
Member

@mxinden mxinden left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just two comments. Otherwise looks good to me. Thanks for the follow-ups.

async fn main() {
let request_counter = Counter::default();

let registry = register_metrics(&request_counter);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I find it confusing that the logic for registering a metric is extracted into a separate function. It is only called once. Say one would have more than one metric to register, the function would register all of them under the same name.

Can you expand on the rational for the extraction? With what I understand today, I would prefer for it to be removed.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I removed the function, you're right

Box::pin(async move {
let mut buf = Vec::new();
encode(&mut buf, &reg.clone()).map(|_| {
let body = std::str::from_utf8(buf.as_slice()).unwrap().to_string();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Given that one can build a Body from a [u8] why is the conversion to a string necessary here?

https://docs.rs/hyper/latest/hyper/struct.Body.html#impl-From%3C%26%27static%20%5Bu8%5D%3E-for-Body

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah great point, thanks for pointing that out. Simplified it.

@adamchalmers adamchalmers force-pushed the achalmers/hyper-example branch from 6d909c6 to c73f09c Compare September 26, 2022 18:14
Copy link
Member

@mxinden mxinden left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the help. Thanks for making it easier for others to get started.

@mxinden mxinden merged commit 682b24e into prometheus:master Sep 27, 2022
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

Successfully merging this pull request may close these issues.

2 participants