at-jet 0.7.2

High-performance HTTP + Protobuf API framework for mobile services
Documentation
//! Smart tracing span maker for HTTP requests
//!
//! Provides [`SmartSpanMaker`] that automatically downgrades noisy
//! infrastructure endpoints (`/health`, `/metrics`) to `TRACE` level,
//! keeping regular API traffic at `INFO` level.
//!
//! Records `http.status_code` on the span when the response is produced,
//! enabling status-based filtering in Jaeger and other tracing backends.
//!
//! # Example
//!
//! ```ignore
//! use at_jet::middleware::smart_trace_layer;
//!
//! let app = Router::new()
//!     .route("/api/users", get(handler))
//!     .layer(smart_trace_layer());
//! ```

use std::time::Duration;
use tower_http::trace::TraceLayer;

/// A span maker that assigns log levels based on the request path.
///
/// - Paths ending with `/health` or `/metrics` → `TRACE` level (quiet)
/// - All other paths → `INFO` level (visible in normal logging)
///
/// This prevents health checks and metrics scrapes from flooding logs
/// while keeping business-relevant traffic visible.
///
/// The span includes an `http.status_code` field (initially empty) that
/// is populated by [`SmartOnResponse`] when the response is sent.
#[derive(Clone, Copy, Debug, Default)]
pub struct SmartSpanMaker;

impl<B> tower_http::trace::MakeSpan<B> for SmartSpanMaker {
  fn make_span(&mut self, req: &http::Request<B>) -> tracing::Span {
    let path = req.uri().path();
    if path.ends_with("/health") || path.ends_with("/metrics") || path == "/health" || path == "/metrics" {
      tracing::trace_span!(
        "http.request",
        method = %req.method(),
        uri = %path,
        request_id = tracing::field::Empty,
        http.status_code = tracing::field::Empty,
      )
    } else {
      tracing::info_span!(
        "http.request",
        method = %req.method(),
        uri = %path,
        request_id = tracing::field::Empty,
        http.status_code = tracing::field::Empty,
      )
    }
  }
}

/// Records `http.status_code` on the tracing span when a response is produced.
#[derive(Clone, Copy, Debug, Default)]
pub struct SmartOnResponse;

impl<B> tower_http::trace::OnResponse<B> for SmartOnResponse {
  fn on_response(self, response: &http::Response<B>, _latency: Duration, span: &tracing::Span) {
    span.record("http.status_code", response.status().as_u16());
  }
}

/// Create a [`TraceLayer`] with [`SmartSpanMaker`] and [`SmartOnResponse`] pre-configured.
///
/// This is the recommended way to add HTTP tracing to your server.
/// Health check and metrics endpoints will be logged at `TRACE` level,
/// while all other requests use `INFO` level. The response status code
/// is recorded as `http.status_code` on each span.
///
/// # Example
///
/// ```ignore
/// use at_jet::middleware::smart_trace_layer;
///
/// let app = Router::new()
///     .route("/api/users", get(handler))
///     .layer(smart_trace_layer());
/// ```
pub fn smart_trace_layer() -> TraceLayer<
  tower_http::classify::SharedClassifier<tower_http::classify::ServerErrorsAsFailures>,
  SmartSpanMaker,
  tower_http::trace::DefaultOnRequest,
  SmartOnResponse,
> {
  TraceLayer::new_for_http()
    .make_span_with(SmartSpanMaker)
    .on_response(SmartOnResponse)
}