Part 4. Add a Middleware

Next, let’s look at how to add middleware to Volo.

For example, if we need a middleware that prints out the received requests, the returned responses and the elapsed time, we could write a Service in

pub struct LogService<S>(S);

impl<Cx, Req, S> volo::Service<Cx, Req> for LogService<S>
    Req: std::fmt::Debug + Send + 'static,
    S: Send + 'static + volo::Service<Cx, Req> + Sync,
    S::Response: std::fmt::Debug,
    S::Error: std::fmt::Debug,
    Cx: Send + 'static,
    async fn call(&self, cx: &mut Cx, req: Req) -> Result<S::Response, S::Error> {
        let now = std::time::Instant::now();
        tracing::debug!("Received request {:?}", &req);
        let resp =, req).await;
        tracing::debug!("Sent response {:?}", &resp);
        tracing::info!("Request took {}ms", now.elapsed().as_millis());

Then we wrap a Layer around the Service:

pub struct LogLayer;

impl<S> volo::Layer<S> for LogLayer {
    type Service = LogService<S>;

    fn layer(self, inner: S) -> Self::Service {

Finally, we add this Layer to client and server:

use volo_example::LogLayer;

static ref CLIENT: volo_gen::volo::example::ItemServiceClient = {
    let addr: SocketAddr = "[::1]:8080".parse().unwrap();


At this point, at the INFO log level, it prints out how long the request took; At the DEBUG logging level, it also types the details of the request and response.

Last modified September 22, 2023 : chore: fix link error (#785) (51eb134)