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