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§
- Request
Logger Config - Configuration for the request logging middleware.
- Request
Logger Layer - Tower layer for the request logging middleware.
- Request
Logger Service - Tower service implementation for the request logging middleware.
Traits§
- Request
Handler - Trait for handling captured request and response data.