viz_handlers/
prometheus.rs1use http_body_util::Full;
6use prometheus::{Encoder, TextEncoder};
7
8use viz_core::{
9 header::{HeaderValue, CONTENT_TYPE},
10 Handler, IntoResponse, Request, Response, Result, StatusCode,
11};
12
13#[doc(inline)]
14pub use opentelemetry_prometheus::ExporterBuilder;
15#[doc(inline)]
16pub use prometheus::Registry;
17
18#[derive(Clone, Debug)]
20pub struct Prometheus {
21 registry: Registry,
22}
23
24impl Prometheus {
25 #[must_use]
27 pub const fn new(registry: Registry) -> Self {
28 Self { registry }
29 }
30}
31
32#[viz_core::async_trait]
33impl Handler<Request> for Prometheus {
34 type Output = Result<Response>;
35
36 async fn call(&self, _: Request) -> Self::Output {
37 let metric_families = self.registry.gather();
38 let encoder = TextEncoder::new();
39 let mut body = Vec::new();
40
41 if let Err(err) = encoder.encode(&metric_families, &mut body) {
42 let error = StatusCode::INTERNAL_SERVER_ERROR;
43 let text = err.to_string();
44
45 #[cfg(feature = "internal-logs")]
46 opentelemetry::otel_error!(name: "prometheus_encode_failure", error_code = error.as_u16(), error = text.clone());
47
48 Err((error, text).into_error())?;
49 }
50
51 let mut res = Response::new(Full::from(body).into());
52
53 res.headers_mut().append(
54 CONTENT_TYPE,
55 HeaderValue::from_str(encoder.format_type())
56 .map_err(|err| (StatusCode::INTERNAL_SERVER_ERROR, err.to_string()).into_error())?,
57 );
58
59 Ok(res)
60 }
61}