Skip to content

Instantly share code, notes, and snippets.

@mesuutt
Last active February 14, 2021 21:57
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mesuutt/a6669343e4177424668e73c277bc83c7 to your computer and use it in GitHub Desktop.
Save mesuutt/a6669343e4177424668e73c277bc83c7 to your computer and use it in GitHub Desktop.
actix-web(3) logging middleware with slog (2.7)
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