Crate outlet

Crate outlet 

Source
Expand description

§Outlet

A high-performance Axum middleware for relaying axum requests & responses to a background thread for asynchronous processing, with full streaming support, and minimal performance overhead.

§Features

  • Stream-aware: Handles streaming request and response bodies without blocking
  • Configurable body capture: Control whether to capture request/response bodies
  • Background processing: All capture and processing happens asynchronously
  • Extensible: Custom handlers for processing captured data

§Quick Start

Here’s a practical example that tracks API usage metrics:

use axum::{routing::get, Router, Json};
use outlet::{RequestLoggerLayer, RequestLoggerConfig, RequestHandler, RequestData, ResponseData};
use tower::ServiceBuilder;
use std::sync::{Arc, Mutex};
use std::collections::HashMap;
use serde_json::json;

// Track API usage metrics
#[derive(Debug, Default)]
struct MetricsHandler {
    stats: Arc<Mutex<HashMap<String, u64>>>,
}

impl RequestHandler for MetricsHandler {
    async fn handle_request(&self, data: RequestData) {
        // Count requests by endpoint
        let endpoint = data.uri.path().to_string();
        let mut stats = self.stats.lock().unwrap();
        *stats.entry(endpoint).or_insert(0) += 1;
    }

    async fn handle_response(&self, _request_data: RequestData, response_data: ResponseData) {
        // Log slow requests for monitoring
        if response_data.duration.as_millis() > 1000 {
            println!("SLOW REQUEST: {} took {}ms",
                     response_data.status, response_data.duration.as_millis());
        }
    }
}

async fn hello() -> &'static str {
    "Hello, World!"
}

async fn stats(metrics: axum::extract::State<Arc<Mutex<HashMap<String, u64>>>>) -> Json<serde_json::Value> {
    let stats = metrics.lock().unwrap().clone();
    Json(json!({ "request_counts": stats }))
}

#[tokio::main]
async fn main() {
    let metrics = Arc::new(Mutex::new(HashMap::new()));
    let handler = MetricsHandler { stats: metrics.clone() };
    let layer = RequestLoggerLayer::new(RequestLoggerConfig::default(), handler);

    let app = Router::new()
        .route("/hello", get(hello))
        .route("/stats", get(stats))
        .with_state(metrics)
        .layer(ServiceBuilder::new().layer(layer));

    let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap();
    axum::serve(listener, app).await.unwrap();
}

§Custom Handlers

Implement the RequestHandler trait to create custom processing logic:

use outlet::{RequestHandler, RequestData, ResponseData};

#[derive(Debug)]
struct CustomHandler;

impl RequestHandler for CustomHandler {
    async fn handle_request(&self, data: RequestData) {
        println!("Request: {} {}", data.method, data.uri);
        // Custom processing logic here
    }

    async fn handle_response(&self, _request_data: RequestData, response_data: ResponseData) {
        println!("Response: {} ({}ms)", response_data.status, response_data.duration.as_millis());
        // Custom processing logic here  
    }
}

Re-exports§

pub use types::RequestData;
pub use types::ResponseData;
pub use logging_handler::LoggingHandler;

Modules§

body_wrapper
Body streaming and capture utilities.
logging_handler
Simple logging implementation for demonstration purposes.
types
Data types for captured HTTP request and response information.

Structs§

RequestLoggerConfig
Configuration for the request logging middleware.
RequestLoggerLayer
Tower layer for the request logging middleware.
RequestLoggerService
Tower service implementation for the request logging middleware.

Traits§

RequestHandler
Trait for handling captured request and response data.