1use axum::{extract::Request, middleware::Next, response::Response};
2
3#[cfg(all(feature = "prometheus", not(test)))]
4use hopr_metrics::metrics::{MultiCounter, MultiHistogram};
5
6#[cfg(all(feature = "prometheus", not(test)))]
7lazy_static::lazy_static! {
8 static ref METRIC_COUNT_API_CALLS: MultiCounter = MultiCounter::new(
9 "hopr_http_api_call_count",
10 "Number of different REST API calls and their statuses",
11 &["endpoint", "method", "status"]
12 )
13 .unwrap();
14 static ref METRIC_COUNT_API_CALLS_TIMING: MultiHistogram = MultiHistogram::new(
15 "hopr_http_api_call_timing_sec",
16 "Timing of different REST API calls in seconds",
17 vec![0.1, 0.25, 0.5, 1.0, 2.0, 5.0, 10.0],
18 &["endpoint", "method"]
19 )
20 .unwrap();
21
22 static ref ID_REGEX: regex::Regex = regex::Regex::new(r"(0x[0-9A-Fa-f]{64})|(12D3KooW[A-z0-9]{44})").unwrap();
24}
25
26#[cfg(all(feature = "prometheus", not(test)))]
28pub(crate) async fn record(
29 uri: axum::extract::OriginalUri,
30 method: axum::http::Method,
31 request: Request,
32 next: Next,
33) -> Response {
34 let path = uri.path().to_owned();
35
36 let start = std::time::Instant::now();
37 let response: Response = next.run(request).await;
38 let response_duration = start.elapsed();
39
40 let status = response.status();
41
42 if path.starts_with("/api/v3/") && !path.contains("node/metrics") {
44 let path = ID_REGEX.replace(&path, "<id>");
45 METRIC_COUNT_API_CALLS.increment(&[&path, method.as_str(), &status.to_string()]);
46 METRIC_COUNT_API_CALLS_TIMING.observe(&[&path, method.as_str()], response_duration.as_secs_f64());
47 }
48
49 response
50}
51
52#[cfg(any(not(feature = "prometheus"), test))]
53pub(crate) async fn record(request: Request, next: Next) -> Response {
54 next.run(request).await
55}