tracing_actix_web/
request_id.rs

1use actix_web::{dev::Payload, HttpMessage};
2use actix_web::{FromRequest, HttpRequest, ResponseError};
3use std::future::{ready, Ready};
4use uuid::Uuid;
5
6/// A unique identifier generated for each incoming request.
7///
8/// Extracting a `RequestId` when the `TracingLogger` middleware is not registered will result in
9/// an internal server error.
10///
11/// # Usage
12/// ```rust
13/// use actix_web::get;
14/// use tracing_actix_web::RequestId;
15/// use uuid::Uuid;
16///
17/// #[get("/")]
18/// async fn index(request_id: RequestId) -> String {
19///   format!("{}", request_id)
20/// }
21///
22/// #[get("/2")]
23/// async fn index2(request_id: RequestId) -> String {
24///  let uuid: Uuid = request_id.into();
25///   format!("{}", uuid)
26/// }
27/// ```
28///
29/// Optionally, using the `uuid_v7` feature flag will allow [`RequestId`] to use UUID v7 instead of the currently used UUID v4.
30#[derive(Clone, Copy, Debug)]
31pub struct RequestId(Uuid);
32
33impl RequestId {
34    pub(crate) fn generate() -> Self {
35        #[cfg(not(feature = "uuid_v7"))]
36        {
37            Self(Uuid::new_v4())
38        }
39        #[cfg(feature = "uuid_v7")]
40        {
41            Self(Uuid::now_v7())
42        }
43    }
44}
45
46impl std::ops::Deref for RequestId {
47    type Target = Uuid;
48
49    fn deref(&self) -> &Self::Target {
50        &self.0
51    }
52}
53
54impl From<RequestId> for Uuid {
55    fn from(r: RequestId) -> Self {
56        r.0
57    }
58}
59
60impl std::fmt::Display for RequestId {
61    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
62        write!(f, "{}", self.0)
63    }
64}
65
66impl FromRequest for RequestId {
67    type Error = RequestIdExtractionError;
68    type Future = Ready<Result<Self, Self::Error>>;
69
70    fn from_request(req: &HttpRequest, _: &mut Payload) -> Self::Future {
71        ready(
72            req.extensions()
73                .get::<RequestId>()
74                .copied()
75                .ok_or(RequestIdExtractionError { _priv: () }),
76        )
77    }
78}
79
80#[derive(Debug)]
81/// Error returned by the [`RequestId`] extractor when it fails to retrieve
82/// the current request id from request-local storage.
83///
84/// It only happens if you try to extract the current request id without having
85/// registered [`TracingLogger`] as a middleware for your application.
86///
87/// [`TracingLogger`]: crate::TracingLogger
88pub struct RequestIdExtractionError {
89    // It turns out that a unit struct has a public constructor!
90    // Therefore adding fields to it (either public or private) later on
91    // is an API breaking change.
92    // Therefore we are adding a dummy private field that the compiler is going
93    // to optimise away to make sure users cannot construct this error
94    // manually in their own code.
95    _priv: (),
96}
97
98impl ResponseError for RequestIdExtractionError {}
99
100impl std::fmt::Display for RequestIdExtractionError {
101    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
102        write!(
103            f,
104            "Failed to retrieve request id from request-local storage."
105        )
106    }
107}
108
109impl std::error::Error for RequestIdExtractionError {}