inapt 0.3.2

A minimal Debian/Ubuntu APT repository proxy written in Rust. Exposes a valid APT repo structure over HTTP, sourcing .deb packages from GitHub Releases.
Documentation
use tower_http::{
    classify::{ServerErrorsAsFailures, SharedClassifier},
    trace::{
        DefaultOnBodyChunk, DefaultOnEos, MakeSpan, OnEos, OnFailure, OnRequest, OnResponse,
        TraceLayer,
    },
};

pub fn layer() -> TraceLayer<
    SharedClassifier<ServerErrorsAsFailures>,
    SpanCreator,
    EventBuilder,
    EventBuilder,
    DefaultOnBodyChunk,
    DefaultOnEos,
    EventBuilder,
> {
    TraceLayer::new_for_http()
        .make_span_with(SpanCreator)
        .on_request(EventBuilder)
        .on_response(EventBuilder)
        .on_failure(EventBuilder)
}

#[derive(Clone, Copy, Debug)]
pub(crate) struct SpanCreator;

impl<B> MakeSpan<B> for SpanCreator {
    fn make_span(&mut self, req: &http::Request<B>) -> tracing::Span {
        let uri = req.uri();
        let span_name = format!("{} {uri}", req.method());
        let span = tracing::info_span!(
            parent: None,
            "http.server.request",
            "error.type" = tracing::field::Empty,
            "exception.message" = tracing::field::Empty,
            "exception.stacktrace" = tracing::field::Empty,
            "http.request.header" = ?req.headers(),
            "http.request.method" = %req.method(),
            "http.response.status_code" = tracing::field::Empty,
            "network.protocol.version" = ?req.version(),
            "otel.kind" = "server",
            "otel.name" = span_name,
            "otel.status_code" = tracing::field::Empty,
            "otel.status_description" = tracing::field::Empty,
            "resource.name" = span_name,
            "server.address" = tracing::field::Empty,
            "server.port" = tracing::field::Empty,
            "span.name" = span_name,
            "span.kind" = "server",
            "span.type" = "web",
            "url.full" = %uri,
            "url.path" = uri.path(),
            "url.query" = tracing::field::Empty,
            "url.scheme" = tracing::field::Empty,
        );
        if let Some(query) = uri.query() {
            span.record("url.query", query);
        }
        if let Some(scheme) = uri.scheme_str() {
            span.record("url.scheme", scheme);
        }
        if let Some(host) = uri.host() {
            span.record("server.address", host);
        }
        if let Some(port) = uri.port_u16() {
            span.record("server.port", port);
        }
        span
    }
}

#[derive(Clone, Copy, Debug)]
pub(crate) struct EventBuilder;

impl<B> OnRequest<B> for EventBuilder {
    fn on_request(&mut self, _req: &axum::extract::Request<B>, _span: &tracing::Span) {
        tracing::info!("request started");
    }
}

impl<B> OnResponse<B> for EventBuilder {
    fn on_response(
        self,
        res: &http::Response<B>,
        latency: std::time::Duration,
        span: &tracing::Span,
    ) {
        span.record("http.response.status_code", res.status().as_str());

        tracing::info!(latency_ns = latency.as_nanos(), "request processed");
    }
}

impl<F> OnFailure<F> for EventBuilder
where
    F: std::fmt::Display,
{
    fn on_failure(
        &mut self,
        failure_classification: F,
        latency: std::time::Duration,
        span: &tracing::Span,
    ) {
        span.record("error.type", "server");
        span.record("exception.message", failure_classification.to_string());
        span.record("otel.status_code", "error");
        span.record(
            "otel.status_description",
            failure_classification.to_string(),
        );

        tracing::info!(
            error = %failure_classification,
            latency_ns = latency.as_nanos(),
            "response failed",
        );
    }
}

impl OnEos for EventBuilder {
    fn on_eos(
        self,
        _trailers: Option<&http::HeaderMap>,
        stream_duration: std::time::Duration,
        _span: &tracing::Span,
    ) {
        tracing::debug!(
            stream_duration_ns = stream_duration.as_nanos(),
            "end of stream",
        );
    }
}