1
0
mirror of https://github.com/actix/actix-extras.git synced 2025-02-23 10:53:02 +01:00

Make request ID extractable in request handlers (#3)

* Make request ID extractable in request handlers

* Remove unnecessary clone

* Implement `Display`, `Deref` and `Into` for RequestId

* Derive `Copy` for `RequestId` since `Uuid` is `Copy`

* Document `RequestId`

* Clarify, when the ID is logged

Co-authored-by: Valentin Brandl <mail+dev@vbrandl.net>
This commit is contained in:
Valentin Brandl 2020-10-31 16:32:45 +01:00 committed by GitHub
parent ccb5c43deb
commit cae596811d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -15,9 +15,9 @@
//! [`Logger`]: https://docs.rs/actix-web/3.0.2/actix_web/middleware/struct.Logger.html //! [`Logger`]: https://docs.rs/actix-web/3.0.2/actix_web/middleware/struct.Logger.html
//! [`log`]: https://docs.rs/log //! [`log`]: https://docs.rs/log
//! [`tracing`]: https://docs.rs/tracing //! [`tracing`]: https://docs.rs/tracing
use actix_web::dev::{Service, ServiceRequest, ServiceResponse, Transform}; use actix_web::dev::{Payload, Service, ServiceRequest, ServiceResponse, Transform};
use actix_web::Error; use actix_web::{Error, FromRequest, HttpMessage, HttpRequest};
use futures::future::{ok, Ready}; use futures::future::{ok, ready, Ready};
use futures::task::{Context, Poll}; use futures::task::{Context, Poll};
use std::future::Future; use std::future::Future;
use std::pin::Pin; use std::pin::Pin;
@ -112,6 +112,52 @@ pub struct TracingLoggerMiddleware<S> {
service: S, service: S,
} }
/// A unique identifier for each incomming request. This ID is added to the logger span, even if
/// the `RequestId` is never extracted.
///
/// Extracting a `RequestId` when the `TracingLogger` middleware is not registered, will result in
/// a internal server error.
///
/// # Usage
/// ```rust
/// use actix_web::get;
/// use tracing_actix_web::RequestId;
/// use uuid::Uuid;
///
/// #[get("/")]
/// async fn index(request_id: RequestId) -> String {
/// format!("{}", request_id)
/// }
///
/// #[get("/2")]
/// async fn index2(request_id: RequestId) -> String {
/// let uuid: Uuid = request_id.into();
/// format!("{}", uuid)
/// }
/// ```
#[derive(Clone, Copy)]
pub struct RequestId(Uuid);
impl std::ops::Deref for RequestId {
type Target = Uuid;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl std::convert::Into<Uuid> for RequestId {
fn into(self) -> Uuid {
self.0
}
}
impl std::fmt::Display for RequestId {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.0)
}
}
impl<S, B> Service for TracingLoggerMiddleware<S> impl<S, B> Service for TracingLoggerMiddleware<S>
where where
S: Service<Request = ServiceRequest, Response = ServiceResponse<B>, Error = Error>, S: Service<Request = ServiceRequest, Response = ServiceResponse<B>, Error = Error>,
@ -133,14 +179,16 @@ where
.get("User-Agent") .get("User-Agent")
.map(|h| h.to_str().unwrap_or("")) .map(|h| h.to_str().unwrap_or(""))
.unwrap_or(""); .unwrap_or("");
let request_id = RequestId(Uuid::new_v4());
let span = tracing::info_span!( let span = tracing::info_span!(
"Request", "Request",
request_path = %req.path(), request_path = %req.path(),
user_agent = %user_agent, user_agent = %user_agent,
client_ip_address = %req.connection_info().realip_remote_addr().unwrap_or(""), client_ip_address = %req.connection_info().realip_remote_addr().unwrap_or(""),
request_id = %Uuid::new_v4(), request_id = %request_id.0,
status_code = tracing::field::Empty, status_code = tracing::field::Empty,
); );
req.extensions_mut().insert(request_id);
let fut = self.service.call(req); let fut = self.service.call(req);
Box::pin( Box::pin(
async move { async move {
@ -156,3 +204,13 @@ where
) )
} }
} }
impl FromRequest for RequestId {
type Error = ();
type Future = Ready<Result<Self, Self::Error>>;
type Config = ();
fn from_request(req: &HttpRequest, _: &mut Payload) -> Self::Future {
ready(req.extensions().get::<RequestId>().copied().ok_or(()))
}
}