static_web_server/
metrics.rs1use headers::{ContentType, HeaderMapExt};
10use hyper::{Body, Request, Response};
11use prometheus::{default_registry, Encoder, TextEncoder};
12
13use crate::{handler::RequestHandlerOpts, http_ext::MethodExt, Error};
14
15pub fn init(enabled: bool, handler_opts: &mut RequestHandlerOpts) {
17 handler_opts.experimental_metrics = enabled;
18 tracing::info!("metrics endpoint (experimental): enabled={enabled}");
19
20 if enabled {
21 default_registry()
22 .register(Box::new(
23 tokio_metrics_collector::default_runtime_collector(),
24 ))
25 .unwrap();
26 }
27}
28
29pub fn pre_process<T>(
31 opts: &RequestHandlerOpts,
32 req: &Request<T>,
33) -> Option<Result<Response<Body>, Error>> {
34 if !opts.experimental_metrics {
35 return None;
36 }
37
38 let uri = req.uri();
39 if uri.path() != "/metrics" {
40 return None;
41 }
42
43 let method = req.method();
44 if !method.is_get() && !method.is_head() {
45 return None;
46 }
47
48 let body = if method.is_get() {
49 let encoder = TextEncoder::new();
50 let mut buffer = Vec::new();
51 encoder
52 .encode(&default_registry().gather(), &mut buffer)
53 .unwrap();
54 let data = String::from_utf8(buffer).unwrap();
55 Body::from(data)
56 } else {
57 Body::empty()
58 };
59 let mut resp = Response::new(body);
60 resp.headers_mut()
61 .typed_insert(ContentType::from(mime_guess::mime::TEXT_PLAIN_UTF_8));
62 Some(Ok(resp))
63}
64
65#[cfg(test)]
66mod tests {
67 use super::pre_process;
68 use crate::handler::RequestHandlerOpts;
69 use hyper::{Body, Request};
70
71 fn make_request(method: &str, uri: &str) -> Request<Body> {
72 Request::builder()
73 .method(method)
74 .uri(uri)
75 .body(Body::empty())
76 .unwrap()
77 }
78
79 #[test]
80 fn test_metrics_disabled() {
81 assert!(pre_process(
82 &RequestHandlerOpts {
83 experimental_metrics: false,
84 ..Default::default()
85 },
86 &make_request("GET", "/metrics")
87 )
88 .is_none());
89 }
90
91 #[test]
92 fn test_wrong_uri() {
93 assert!(pre_process(
94 &RequestHandlerOpts {
95 experimental_metrics: true,
96 ..Default::default()
97 },
98 &make_request("GET", "/metrics2")
99 )
100 .is_none());
101 }
102
103 #[test]
104 fn test_wrong_method() {
105 assert!(pre_process(
106 &RequestHandlerOpts {
107 experimental_metrics: true,
108 ..Default::default()
109 },
110 &make_request("POST", "/metrics")
111 )
112 .is_none());
113 }
114
115 #[test]
116 fn test_correct_request() {
117 assert!(pre_process(
118 &RequestHandlerOpts {
119 experimental_metrics: true,
120 ..Default::default()
121 },
122 &make_request("GET", "/metrics")
123 )
124 .is_some());
125 }
126}