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 {}