tracing_actix_web2/
lib.rs

1use actix_web::http::header::{HeaderName, HeaderValue};
2use actix_web::{
3    dev::{Service, ServiceRequest, ServiceResponse, Transform},
4    Error,
5};
6use future::{ok, LocalBoxFuture, Ready};
7use futures::prelude::*;
8use std::{
9    cell::RefCell,
10    rc::Rc,
11    task::{Context, Poll},
12};
13use tracing::{span, trace, Instrument, Level};
14use uuid::Uuid;
15
16pub const REQUEST_ID_HEADER: &str = "x-request-id";
17
18pub struct Tracer;
19
20impl<S, B> Transform<S, ServiceRequest> for Tracer
21where
22    S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error> + 'static,
23    S::Future: 'static,
24    B: 'static,
25{
26    type Response = ServiceResponse<B>;
27    type Error = Error;
28    type InitError = ();
29    type Transform = TracerMiddleware<S>;
30    type Future = Ready<Result<Self::Transform, Self::InitError>>;
31
32    fn new_transform(&self, service: S) -> Self::Future {
33        ok(TracerMiddleware {
34            service: Rc::new(RefCell::new(service)),
35        })
36    }
37}
38
39pub struct TracerMiddleware<S> {
40    pub service: Rc<RefCell<S>>,
41}
42
43impl<S, B> Service<ServiceRequest> for TracerMiddleware<S>
44where
45    S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error> + 'static,
46    S::Future: 'static,
47    B: 'static,
48{
49    type Response = ServiceResponse<B>;
50    type Error = S::Error;
51    type Future = LocalBoxFuture<'static, Result<Self::Response, Self::Error>>;
52
53    fn poll_ready(&self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
54        self.service.poll_ready(cx)
55    }
56
57    fn call(&self, req: ServiceRequest) -> Self::Future {
58        let request_id = Uuid::new_v4().to_string();
59        let root_span = span!(Level::INFO, "HttpRequest", %request_id, method = req.method().as_str(), path = req.path(), query = req.query_string(), remote_ip = req.connection_info().peer_addr().unwrap_or("-"));
60
61        let fut = self.service.call(req);
62        Box::pin(
63            async move {
64                let mut res = fut.await?;
65
66                res.headers_mut().insert(
67                    HeaderName::from_static(REQUEST_ID_HEADER),
68                    HeaderValue::from_str(&request_id).unwrap(),
69                );
70                trace!("request");
71
72                Ok(res)
73            }
74            .instrument(root_span),
75        )
76    }
77}