diff options
Diffstat (limited to 'lib')
-rw-r--r-- | lib/warden-core/build.rs | 16 | ||||
-rw-r--r-- | lib/warden-middleware/Cargo.toml | 18 | ||||
-rw-r--r-- | lib/warden-middleware/src/lib.rs | 20 | ||||
-rw-r--r-- | lib/warden-middleware/src/metrics.rs | 33 | ||||
-rw-r--r-- | lib/warden-middleware/src/trace_layer.rs | 24 |
5 files changed, 104 insertions, 7 deletions
diff --git a/lib/warden-core/build.rs b/lib/warden-core/build.rs index 9088fd7..5f1e898 100644 --- a/lib/warden-core/build.rs +++ b/lib/warden-core/build.rs @@ -18,12 +18,9 @@ impl Entity { vec!["proto/warden_message.proto"] } - #[cfg(feature = "configuration")] fn configuration_protos() -> Vec<&'static str> { - vec![ - "proto/configuration/routing.proto", - ] + vec!["proto/configuration/routing.proto"] } #[cfg(feature = "pseudonyms")] @@ -72,7 +69,6 @@ fn main() -> Result<(), Box<dyn std::error::Error>> { #[cfg(any(feature = "message", feature = "pseudonyms", feature = "configuration"))] build_proto(&protos)?; - Ok(()) } @@ -100,7 +96,10 @@ fn build_proto(protos: &[&str]) -> Result<(), Box<dyn std::error::Error>> { Ok(()) } -#[cfg(all(feature = "serde", any(feature = "pseudonyms", feature = "message", feature = "configuration")))] +#[cfg(all( + feature = "serde", + any(feature = "pseudonyms", feature = "message", feature = "configuration") +))] fn add_serde(config: tonic_prost_build::Builder) -> tonic_prost_build::Builder { let config = config.type_attribute( ".", @@ -116,7 +115,10 @@ fn add_serde(config: tonic_prost_build::Builder) -> tonic_prost_build::Builder { config } -#[cfg(all(feature = "openapi", any(feature = "message", feature = "pseudonyms", feature = "configuration")))] +#[cfg(all( + feature = "openapi", + any(feature = "message", feature = "pseudonyms", feature = "configuration") +))] fn add_openapi(config: tonic_prost_build::Builder) -> tonic_prost_build::Builder { config.type_attribute(".", "#[derive(utoipa::ToSchema)]") } diff --git a/lib/warden-middleware/Cargo.toml b/lib/warden-middleware/Cargo.toml new file mode 100644 index 0000000..b23e61e --- /dev/null +++ b/lib/warden-middleware/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "warden-middleware" +version = "0.1.0" +edition = "2024" +license.workspace = true +homepage.workspace = true +documentation.workspace = true +description.workspace = true +publish = false + +[dependencies] +axum.workspace = true +metrics.workspace = true +metrics-exporter-prometheus.workspace = true +tower-http = { workspace = true, features = [ + "request-id", +] } +tracing.workspace = true diff --git a/lib/warden-middleware/src/lib.rs b/lib/warden-middleware/src/lib.rs new file mode 100644 index 0000000..6e3a0f4 --- /dev/null +++ b/lib/warden-middleware/src/lib.rs @@ -0,0 +1,20 @@ +mod metrics; +mod trace_layer; + +use metrics::*; +use trace_layer::*; + +use axum::{Router, http::HeaderName, middleware}; +use tower_http::request_id::{MakeRequestUuid, PropagateRequestIdLayer, SetRequestIdLayer}; + +const REQUEST_ID_HEADER: &str = "x-request-id"; + +pub fn apply<T: Clone + Send + Sync + 'static>(router: Router<T>) -> Router<T> { + let x_request_id = HeaderName::from_static(REQUEST_ID_HEADER); + + let router = router.layer(PropagateRequestIdLayer::new(x_request_id.clone())); + + apply_trace_context_middleware(router) + .layer(SetRequestIdLayer::new(x_request_id, MakeRequestUuid)) + .route_layer(middleware::from_fn(apply_metrics_middleware)) +} diff --git a/lib/warden-middleware/src/metrics.rs b/lib/warden-middleware/src/metrics.rs new file mode 100644 index 0000000..8644160 --- /dev/null +++ b/lib/warden-middleware/src/metrics.rs @@ -0,0 +1,33 @@ +use std::time::Instant; + +use axum::{ + extract::{MatchedPath, Request}, + middleware::Next, + response::IntoResponse, +}; + +pub async fn apply_metrics_middleware(req: Request, next: Next) -> impl IntoResponse { + let start = Instant::now(); + let path = if let Some(matched_path) = req.extensions().get::<MatchedPath>() { + matched_path.as_str().to_owned() + } else { + req.uri().path().to_owned() + }; + let method = req.method().clone(); + + let response = next.run(req).await; + + let latency = start.elapsed().as_secs_f64(); + let status = response.status().as_u16().to_string(); + + let labels = [ + ("method", method.to_string()), + ("path", path), + ("status", status), + ]; + + metrics::counter!("http_requests_total", &labels).increment(1); + metrics::histogram!("http_requests_duration_seconds", &labels).record(latency); + + response +} diff --git a/lib/warden-middleware/src/trace_layer.rs b/lib/warden-middleware/src/trace_layer.rs new file mode 100644 index 0000000..5173e8d --- /dev/null +++ b/lib/warden-middleware/src/trace_layer.rs @@ -0,0 +1,24 @@ +use axum::{Router, http::Request}; +use tower_http::trace::TraceLayer; +use tracing::info_span; + +use super::REQUEST_ID_HEADER; + +pub fn apply_trace_context_middleware<T: Clone + Send + Sync + 'static>( + router: Router<T>, +) -> Router<T> { + router.layer( + TraceLayer::new_for_http().make_span_with(|request: &Request<_>| { + let request_id = request + .headers() + .get(REQUEST_ID_HEADER) + .expect("should have been applied already"); + + info_span!( + "http_request", + request_id = ?request_id, + headers = ?request.headers() + ) + }), + ) +} |