Expand description
§outlet-postgres
PostgreSQL logging handler for the outlet HTTP request/response middleware.
This crate implements the RequestHandler
trait from outlet to log HTTP
requests and responses to PostgreSQL with JSONB serialization for bodies.
§Features
- PostgreSQL Integration: Uses sqlx for async PostgreSQL operations
- JSONB Bodies: Serializes request/response bodies to JSONB fields
- Type-safe Querying: Query logged data with typed request/response bodies
- Correlation: Links requests and responses via correlation IDs
- Error Handling: Graceful error handling with logging
§Quick Start
use outlet::{RequestLoggerLayer, RequestLoggerConfig};
use outlet_postgres::{PostgresHandler, repository::RequestFilter};
use axum::{routing::get, Router};
use tower::ServiceBuilder;
use serde::{Deserialize, Serialize};
// Define your custom request and response body types
#[derive(Clone, Debug, Deserialize, Serialize)]
struct ApiRequest {
user_id: u64,
action: String,
}
#[derive(Clone, Debug, Deserialize, Serialize)]
struct ApiResponse {
success: bool,
message: String,
}
async fn hello() -> &'static str {
"Hello, World!"
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let database_url = "postgresql://user:password@localhost/dbname";
// Create handler with separate request and response types
let handler = PostgresHandler::<ApiRequest, ApiResponse>::new(database_url).await?;
// Or use default Value types for flexible JSON storage
// let handler = PostgresHandler::new(database_url).await?;
let layer = RequestLoggerLayer::new(RequestLoggerConfig::default(), handler.clone());
// Get a repository for querying logged requests and responses
let repository = handler.repository();
// Query logged data with filters
let filter = RequestFilter {
method: Some("POST".to_string()),
status_code_min: Some(400),
limit: Some(10),
..Default::default()
};
let results = repository.query(filter).await?;
for pair in results {
println!("Request: {} {}", pair.request.method, pair.request.uri);
match pair.request.body {
Some(Ok(request_body)) => println!(" Parsed request: {:?}", request_body),
Some(Err(raw_bytes)) => println!(" Raw request bytes: {} bytes", raw_bytes.len()),
None => println!(" No request body"),
}
if let Some(response) = pair.response {
println!("Response: {} ({}ms)", response.status_code, response.duration_ms);
match response.body {
Some(Ok(response_body)) => println!(" Parsed response: {:?}", response_body),
Some(Err(raw_bytes)) => println!(" Raw response bytes: {} bytes", raw_bytes.len()),
None => println!(" No response body"),
}
}
}
let app = Router::new()
.route("/hello", get(hello))
.layer(ServiceBuilder::new().layer(layer));
let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await?;
axum::serve(listener, app).await?;
Ok(())
}
Re-exports§
pub use error::PostgresHandlerError;
pub use repository::HttpRequest;
pub use repository::HttpResponse;
pub use repository::RequestFilter;
pub use repository::RequestRepository;
pub use repository::RequestResponsePair;
Modules§
- error
- Error types for outlet-postgres.
- repository
Structs§
- Path
Filter - Filter configuration for determining which requests to log.
- Postgres
Handler - PostgreSQL handler for outlet middleware.
Functions§
- migrator
- Get the migrator for running outlet-postgres database migrations.