Last active
August 27, 2021 04:50
-
-
Save dimfeld/189053f1307682524739df8387636daa to your computer and use it in GitHub Desktop.
Simple actix-web middleware for custom KV logging with slog
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, FutureResult}; | |
use futures::{Future, Poll}; | |
use slog::info; | |
// 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 InitError = (); | |
type Transform = LoggingMiddleware<S>; | |
type Future = FutureResult<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 = Box<Future<Item = Self::Response, Error = Self::Error>>; | |
fn poll_ready(&mut self) -> Poll<(), Self::Error> { | |
self.service.poll_ready() | |
} | |
fn call(&mut self, req: ServiceRequest) -> Self::Future { | |
let start_time = chrono::Utc::now(); | |
let logger = self.logger.clone(); | |
Box::new(self.service.call(req).and_then(move |res| { | |
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) | |
})) | |
} | |
} |
Just a note, this doesn't work with actix-web >= 4.0.0-beta.8
.
@DrBluefall yeah not too surprised there. The middleware traits have changed a bit since I originally wrote this. FWIW nowadays I prefer the tracing crate along with tracing-actix-web for my logging needs.
Not too much a fan of tracing, generally prefer slog. Especially when using with journald
.
I'm sure I could hack something up based on this, though. Maybe.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
thanks for sharing