Skip to content

Commit a2c1c77

Browse files
committed
add HTTP metrics
1 parent 32349d6 commit a2c1c77

File tree

5 files changed

+102
-13
lines changed

5 files changed

+102
-13
lines changed

Cargo.lock

Lines changed: 3 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ fqdn = { version = "0.4.6", features = ["serde"] }
1616
hostname = "0.4.0"
1717
http = "1.3.1"
1818
humantime = "2.2.0"
19-
ic-bn-lib = { git = "https://github.com/dfinity/ic-bn-lib", rev = "00f6202f0939034c22281830736956d502a84d21", features = [
19+
ic-bn-lib = { git = "https://github.com/dfinity/ic-bn-lib", rev = "df55aadf35cae38e5bc60febfc3170d448249b98", features = [
2020
"cert-providers",
2121
"clients-hyper",
2222
"acme-alpn",

src/core.rs

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -102,9 +102,14 @@ pub async fn main(cli: &Cli) -> Result<(), Error> {
102102
None
103103
};
104104

105-
let axum_router =
106-
setup_axum_router(cli, axum_router_api.clone(), backend_router, vector.clone())
107-
.context("unable to setup Axum Router")?;
105+
let axum_router = setup_axum_router(
106+
cli,
107+
axum_router_api.clone(),
108+
backend_router,
109+
vector.clone(),
110+
&registry,
111+
)
112+
.context("unable to setup Axum Router")?;
108113

109114
// HTTP server metrics
110115
let http_metrics = bnhttp::server::Metrics::new(&registry);

src/middleware/metrics.rs

Lines changed: 82 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1-
use std::sync::Arc;
1+
use std::{
2+
sync::Arc,
3+
time::{Duration, Instant},
4+
};
25

36
use axum::{
47
Extension,
@@ -11,17 +14,57 @@ use bytes::Bytes;
1114
use derive_new::new;
1215
use http::HeaderValue;
1316
use ic_bn_lib::{
14-
http::{ConnInfo, extract_authority, headers::X_REAL_IP, http_method, http_version},
17+
http::{
18+
ConnInfo, extract_authority, headers::X_REAL_IP, http_method, http_version, server::TlsInfo,
19+
},
1520
vector::client::Vector,
1621
};
22+
use prometheus::{
23+
HistogramVec, IntCounterVec, Registry, register_histogram_vec_with_registry,
24+
register_int_counter_vec_with_registry,
25+
};
1726
use serde_json::json;
1827
use tracing::info;
1928

2029
use crate::{backend::Backend, middleware::request_id::RequestId};
2130

31+
pub const HTTP_DURATION_BUCKETS: &[f64] = &[0.05, 0.2, 1.0, 2.0];
32+
33+
#[derive(Clone)]
34+
pub struct Metrics {
35+
pub requests: IntCounterVec,
36+
pub duration: HistogramVec,
37+
}
38+
39+
impl Metrics {
40+
pub fn new(registry: &Registry) -> Self {
41+
const LABELS_HTTP: &[&str] = &["tls", "method", "http", "status", "backend"];
42+
43+
Self {
44+
requests: register_int_counter_vec_with_registry!(
45+
format!("http_requests"),
46+
format!("Counts occurrences of requests"),
47+
LABELS_HTTP,
48+
registry
49+
)
50+
.unwrap(),
51+
52+
duration: register_histogram_vec_with_registry!(
53+
format!("http_requests_duration_sec"),
54+
format!("Records the duration of request processing in seconds"),
55+
LABELS_HTTP,
56+
HTTP_DURATION_BUCKETS.to_vec(),
57+
registry
58+
)
59+
.unwrap(),
60+
}
61+
}
62+
}
63+
2264
#[derive(new)]
2365
pub struct MetricsState {
2466
vector: Option<Arc<Vector>>,
67+
metrics: Metrics,
2568
log_requests: bool,
2669
}
2770

@@ -31,6 +74,7 @@ pub async fn middleware(
3174
mut request: Request,
3275
next: Next,
3376
) -> Response {
77+
let tls_info = request.extensions().get::<Arc<TlsInfo>>().cloned();
3478
let method = http_method(request.method());
3579
let authority = extract_authority(&request).unwrap_or_default().to_string();
3680
let http_version = http_version(request.version());
@@ -49,7 +93,11 @@ pub async fn middleware(
4993
HeaderValue::from_maybe_shared(Bytes::from(remote_addr.clone())).unwrap(),
5094
);
5195

96+
// Execute the request
97+
let start = Instant::now();
5298
let mut response = next.run(request).await;
99+
let duration = start.elapsed().as_secs_f64();
100+
53101
let backend = response
54102
.extensions_mut()
55103
.remove::<Arc<Backend>>()
@@ -61,25 +109,51 @@ pub async fn middleware(
61109
.exact()
62110
.map(|x| x as i64)
63111
.unwrap_or(-1);
64-
65112
let request_id = response
66113
.extensions_mut()
67114
.remove::<RequestId>()
68115
.map(|x| x.to_string())
69116
.unwrap_or_default();
70-
71117
let status = response.status();
72118

119+
let (tls_version, tls_cipher, tls_handshake) =
120+
tls_info.as_ref().map_or(("", "", Duration::ZERO), |x| {
121+
(
122+
x.protocol.as_str().unwrap(),
123+
x.cipher.as_str().unwrap(),
124+
x.handshake_dur,
125+
)
126+
});
127+
128+
let labels = &[
129+
tls_version,
130+
method,
131+
http_version,
132+
status.as_str(),
133+
backend.as_str(),
134+
];
135+
136+
state.metrics.requests.with_label_values(labels).inc();
137+
state
138+
.metrics
139+
.duration
140+
.with_label_values(labels)
141+
.observe(duration);
142+
73143
if state.log_requests {
74144
info!(
75145
request_id,
146+
tls_version,
147+
tls_cipher,
148+
tls_handshake = tls_handshake.as_secs_f64(),
76149
http_version,
77150
authority,
78151
method,
79152
path,
80153
query,
81154
remote_addr,
82155
status = status.as_str(),
156+
duration,
83157
backend,
84158
request_size,
85159
response_size,
@@ -89,12 +163,16 @@ pub async fn middleware(
89163
if let Some(v) = &state.vector {
90164
let event = json! ({
91165
"request_id": request_id,
166+
"tls_version": tls_version,
167+
"tls_cipher": tls_cipher,
168+
"tls_handshake": tls_handshake.as_secs_f64(),
92169
"http_version": http_version,
93170
"authority": authority,
94171
"method": method,
95172
"path": path,
96173
"query": query,
97174
"status": status.as_str(),
175+
"duration": duration,
98176
"backend": backend,
99177
"remote_addr": remote_addr,
100178
"request_size": request_size,

src/routing.rs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,16 @@ use ic_bn_lib::{
1818
http::{extract_host, headers::X_FORWARDED_HOST},
1919
vector::client::Vector,
2020
};
21+
use prometheus::Registry;
2122
use tower::ServiceExt;
2223

2324
use crate::{
2425
backend::LBBackendRouter,
2526
cli::Cli,
26-
middleware::{self, metrics::MetricsState},
27+
middleware::{
28+
self,
29+
metrics::{Metrics, MetricsState},
30+
},
2731
};
2832

2933
#[derive(Debug, new)]
@@ -58,10 +62,12 @@ pub fn setup_axum_router(
5862
router_api: Option<Router>,
5963
backend_router: Arc<ArcSwapOption<LBBackendRouter>>,
6064
vector: Option<Arc<Vector>>,
65+
registry: &Registry,
6166
) -> Result<Router, Error> {
6267
let state = Arc::new(HandlerState::new(backend_router));
6368
let api_hostname = cli.api.api_hostname.clone().map(|x| x.to_string());
64-
let metrics_state = Arc::new(MetricsState::new(vector, cli.log.log_requests));
69+
let metrics = Metrics::new(registry);
70+
let metrics_state = Arc::new(MetricsState::new(vector, metrics, cli.log.log_requests));
6571

6672
Ok(Router::new()
6773
.route("/foobar_test_route", get(async || "foobar"))

0 commit comments

Comments
 (0)