use {
http_body_util::Full,
hyper::{Request, Response, body::Bytes, server::conn::http1, service::service_fn},
hyper_util::rt::TokioIo,
prometheus::{Registry, TextEncoder},
std::{convert::Infallible, net::SocketAddr},
tokio::net::TcpListener,
};
pub async fn prometheus_service_fn(
registry: &Registry,
_: Request<hyper::body::Incoming>,
) -> Result<hyper::Response<Full<Bytes>>, Infallible> {
let metrics = TextEncoder::new().encode_to_string(®istry.gather());
match metrics {
Ok(metrics) => Ok(Response::new(Full::new(Bytes::from(metrics)))),
Err(e) => Ok(Response::new(Full::new(Bytes::from(format!(
"Failed to encode metrics: {}",
e
))))),
}
}
pub async fn prometheus_server(bind_addr: SocketAddr, registry: Registry) {
let listener = TcpListener::bind(bind_addr)
.await
.expect("Failed to bind TCP listener");
let addr = listener.local_addr().expect("Failed to get local address");
println!("Prometheus listening on http://{}", addr);
loop {
let (stream, _) = listener
.accept()
.await
.expect("Failed to accept connection");
let io = TokioIo::new(stream);
let registry2 = registry.clone();
tokio::task::spawn(async move {
let svc_fn = |req| {
let registry = registry2.clone();
async move { prometheus_service_fn(®istry, req).await }
};
let result = http1::Builder::new()
.serve_connection(io, service_fn(svc_fn))
.await;
if let Err(err) = result {
eprintln!("Error serving connection: {:?}", err);
}
});
}
}