//! Actix-web middleware that creates a `tracing::Span` for each HTTP request. //! //! The span is set as the current context so all downstream async code is //! automatically instrumented. When the `tracing_opentelemetry` layer is //! active, spans are exported via OTLP. use actix_web::dev::{Service, ServiceRequest, ServiceResponse, Transform}; use std::sync::Arc; use std::task::{Context, Poll}; /// Actix-web middleware that creates a tracing span per request. pub struct TracingSpanMiddleware; impl TracingSpanMiddleware { pub fn new() -> Self { Self } } impl Default for TracingSpanMiddleware { fn default() -> Self { Self::new() } } impl Transform for TracingSpanMiddleware where S: Service, Error = actix_web::Error> + 'static, S::Future: 'static, B: 'static, { type Response = ServiceResponse; type Error = actix_web::Error; type Transform = TracingSpanMiddlewareService; type InitError = (); type Future = std::future::Ready>; fn new_transform(&self, service: S) -> Self::Future { std::future::ready(Ok(TracingSpanMiddlewareService { service: Arc::new(service), })) } } pub struct TracingSpanMiddlewareService { service: Arc, } impl Clone for TracingSpanMiddlewareService { fn clone(&self) -> Self { Self { service: self.service.clone(), } } } impl Service for TracingSpanMiddlewareService where S: Service, Error = actix_web::Error> + 'static, S::Future: 'static, B: 'static, { type Response = ServiceResponse; type Error = actix_web::Error; type Future = std::pin::Pin> + 'static>>; fn poll_ready(&self, cx: &mut Context<'_>) -> Poll> { self.service.poll_ready(cx) } fn call(&self, req: ServiceRequest) -> Self::Future { let method = req.method().to_string(); let path = req.path().to_string(); let service = self.service.clone(); Box::pin(async move { // Set _msg for VictoriaLogs before entering the span. // The JSON formatter reads this thread-local value. crate::msg_json_fmt::set_span_msg(format!("{} {}", method, path)); let span = tracing::info_span!("HTTP {method} {path}", method = %method, path = %path); let _guard = span.enter(); let res = service.call(req).await?; Ok(res) }) } }