Last active
February 14, 2021 21:57
-
-
Save mesuutt/a6669343e4177424668e73c277bc83c7 to your computer and use it in GitHub Desktop.
actix-web(3) logging middleware with slog (2.7)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
use actix_service::{Service, Transform}; | |
use actix_web::{dev::ServiceRequest, dev::ServiceResponse, Error}; | |
use futures::future::{ok, Ready}; | |
use futures::{Future}; | |
use slog::info; | |
use futures::task::Poll; | |
use std::task::Context; | |
use std::pin::Pin; | |
// There are two step in middleware processing. | |
// 1. Middleware initialization, middleware factory get called with | |
// next service in chain as parameter. | |
// 2. Middleware's call method get called with normal request. | |
pub struct Logging { | |
logger: slog::Logger, | |
} | |
impl Logging { | |
pub fn new(logger: slog::Logger) -> Logging { | |
Logging { logger } | |
} | |
} | |
// Middleware factory is `Transform` trait from actix-service crate | |
// `S` - type of the next service | |
// `B` - type of response's body | |
impl<S, B> Transform<S> for Logging | |
where | |
S: Service<Request=ServiceRequest, Response=ServiceResponse<B>, Error=Error>, | |
S::Future: 'static, | |
B: 'static, | |
{ | |
type Request = ServiceRequest; | |
type Response = ServiceResponse<B>; | |
type Error = Error; | |
type Transform = LoggingMiddleware<S>; | |
type InitError = (); | |
type Future = Ready<Result<Self::Transform, Self::InitError>>; | |
fn new_transform(&self, service: S) -> Self::Future { | |
ok(LoggingMiddleware { | |
service, | |
logger: self.logger.clone(), | |
}) | |
} | |
} | |
pub struct LoggingMiddleware<S> { | |
service: S, | |
logger: slog::Logger, | |
} | |
impl<S, B> Service for LoggingMiddleware<S> | |
where | |
S: Service<Request=ServiceRequest, Response=ServiceResponse<B>, Error=Error>, | |
S::Future: 'static, | |
B: 'static, | |
{ | |
type Request = ServiceRequest; | |
type Response = ServiceResponse<B>; | |
type Error = Error; | |
type Future = Pin<Box<dyn Future<Output=Result<Self::Response, Self::Error>>>>; | |
fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> { | |
self.service.poll_ready(cx) | |
} | |
fn call(&mut self, req: ServiceRequest) -> Self::Future { | |
let fut = self.service.call(req); | |
let logger = self.logger.clone(); | |
Box::pin(async move { | |
let start_time = chrono::Utc::now(); | |
let res = fut.await?; | |
let req = res.request(); | |
let end_time = chrono::Utc::now(); | |
let duration = end_time - start_time; | |
info!(logger, "handled request"; | |
"responseTime" => duration.num_milliseconds(), | |
"url" => %req.uri(), | |
"route" => req.path(), | |
"method" => %req.method(), | |
"statusCode" => res.status().as_u16() | |
); | |
Ok(res) | |
}) | |
} | |
} | |
// App::new().wrap(Logging::new(root_logger.clone())) | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment