svc-utils 0.8.0

Bunch of reusable utilities
Documentation
use std::time::Duration;

use http::{Method, Request, Response};
use hyper::{body::HttpBody, Body};
use tower::Layer;
use tower_http::classify::{ServerErrorsAsFailures, SharedClassifier};
use tower_http::trace::TraceLayer;
use tower_http::trace::{DefaultOnRequest, MakeSpan, OnResponse};
use tracing::{
    error,
    field::{self, Empty},
    info, Span,
};

#[derive(Default, Clone)]
pub struct LogLayer;

impl LogLayer {
    pub fn new() -> Self {
        Self
    }
}

impl<S> Layer<S> for LogLayer {
    type Service = <TraceLayer<
        SharedClassifier<ServerErrorsAsFailures>,
        SpanMaker,
        DefaultOnRequest,
        OnResp,
    > as Layer<S>>::Service;

    fn layer(&self, service: S) -> Self::Service {
        let layer = TraceLayer::new_for_http()
            .make_span_with(SpanMaker)
            .on_response(OnResp);

        layer.layer(service)
    }
}

#[derive(Debug, Clone)]
pub struct SpanMaker;

impl MakeSpan<Body> for SpanMaker {
    fn make_span(&mut self, request: &Request<Body>) -> Span {
        let span = tracing::error_span!(
            "http-api-request",
            status_code = Empty,
            path = request.uri().path(),
            query = request.uri().query(),
            method = %request.method(),
            account_id = Empty,
            body_size = Empty,
            kind = Empty,
            detail = Empty,
        );

        if request.method() != Method::GET && request.method() != Method::OPTIONS {
            span.record(
                "body_size",
                &field::debug(request.body().size_hint().upper()),
            );
        }

        span
    }
}

#[derive(Debug, Clone)]
pub struct OnResp;

impl<B> OnResponse<B> for OnResp {
    fn on_response(self, response: &Response<B>, latency: Duration, span: &Span) {
        span.record("status_code", &field::debug(response.status()));
        if response.status().is_client_error() || response.status().is_server_error() {
            error!("response generated in {:?}", latency)
        } else {
            info!("response generated in {:?}", latency)
        }
    }
}