From c73f09cc00423cbff775361cc04b89f7cfe332a3 Mon Sep 17 00:00:00 2001 From: Adam Chalmers Date: Mon, 19 Sep 2022 17:35:46 -0500 Subject: [PATCH] example: Use hyper to serve metrics Signed-off-by: Adam Chalmers --- Cargo.toml | 2 ++ examples/hyper.rs | 75 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 77 insertions(+) create mode 100644 examples/hyper.rs diff --git a/Cargo.toml b/Cargo.toml index 74fe6257..88bc0569 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -34,6 +34,8 @@ quickcheck = "1" rand = "0.8.4" tide = "0.16" actix-web = "4" +tokio = { version = "1", features = ["rt-multi-thread", "net", "macros", "signal"] } +hyper = { version = "0.14.16", features = ["server", "http1", "tcp"] } [build-dependencies] prost-build = { version = "0.9.0", optional = true } diff --git a/examples/hyper.rs b/examples/hyper.rs new file mode 100644 index 00000000..404b6c17 --- /dev/null +++ b/examples/hyper.rs @@ -0,0 +1,75 @@ +use hyper::{ + service::{make_service_fn, service_fn}, + Body, Request, Response, Server, +}; +use prometheus_client::{encoding::text::encode, metrics::counter::Counter, registry::Registry}; +use std::{ + future::Future, + io, + net::{IpAddr, Ipv4Addr, SocketAddr}, + pin::Pin, + sync::Arc, +}; +use tokio::signal::unix::{signal, SignalKind}; + +#[tokio::main] +async fn main() { + let request_counter: Counter = Default::default(); + + let mut registry = ::with_prefix("tokio_hyper_example"); + + registry.register( + "requests", + "How many requests the application has received", + Box::new(request_counter.clone()), + ); + + // Spawn a server to serve the OpenMetrics endpoint. + let metrics_addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8001); + start_metrics_server(metrics_addr, registry).await +} + +/// Start a HTTP server to report metrics. +pub async fn start_metrics_server(metrics_addr: SocketAddr, registry: Registry) { + let mut shutdown_stream = signal(SignalKind::terminate()).unwrap(); + + eprintln!("Starting metrics server on {metrics_addr}"); + + let registry = Arc::new(registry); + Server::bind(&metrics_addr) + .serve(make_service_fn(move |_conn| { + let registry = registry.clone(); + async move { + let handler = make_handler(registry); + Ok::<_, io::Error>(service_fn(handler)) + } + })) + .with_graceful_shutdown(async move { + shutdown_stream.recv().await; + }) + .await + .unwrap(); +} + +/// This function returns a HTTP handler (i.e. another function) +pub fn make_handler( + registry: Arc, +) -> impl Fn(Request) -> Pin>> + Send>> { + // This closure accepts a request and responds with the OpenMetrics encoding of our metrics. + move |_req: Request| { + let reg = registry.clone(); + Box::pin(async move { + let mut buf = Vec::new(); + encode(&mut buf, ®.clone()).map(|_| { + let body = Body::from(buf); + Response::builder() + .header( + hyper::header::CONTENT_TYPE, + "application/openmetrics-text; version=1.0.0; charset=utf-8", + ) + .body(body) + .unwrap() + }) + }) + } +}