use axum::{
extract::Request,
http::{HeaderMap, HeaderValue},
middleware::Next,
response::Response,
};
use tracing::{info, instrument, Span};
use uuid::Uuid;
use minifly_logging::fields;
pub const REGION_HEADER: &str = "x-minifly-region";
pub const CORRELATION_ID_HEADER: &str = "x-minifly-correlation-id";
pub const DEFAULT_REGION: &str = "local";
#[instrument(
name = "region_middleware",
skip_all,
fields(
region = %DEFAULT_REGION,
correlation_id = tracing::field::Empty,
request_id = tracing::field::Empty,
http.method = %request.method(),
http.path = %request.uri().path(),
http.user_agent = tracing::field::Empty,
http.status = tracing::field::Empty,
duration_ms = tracing::field::Empty,
)
)]
pub async fn region_middleware(request: Request, next: Next) -> Response {
let correlation_id = minifly_logging::new_correlation_id();
let request_id = minifly_logging::new_request_id();
let region = DEFAULT_REGION.to_string();
Span::current().record(fields::CORRELATION_ID, &correlation_id);
Span::current().record(fields::REQUEST_ID, &request_id);
Span::current().record(fields::REGION, ®ion);
if let Some(user_agent) = request.headers().get("user-agent") {
if let Ok(ua_str) = user_agent.to_str() {
Span::current().record(fields::HTTP_USER_AGENT, ua_str);
}
}
info!(
operation = "http_request_start",
"Processing HTTP request"
);
let start_time = std::time::Instant::now();
let mut response = next.run(request).await;
let duration = start_time.elapsed();
Span::current().record(fields::HTTP_STATUS, response.status().as_u16());
Span::current().record(fields::DURATION_MS, duration.as_millis());
let headers = response.headers_mut();
add_region_headers(headers, ®ion, &correlation_id);
info!(
operation = "http_request_complete",
operation.status = "success",
"HTTP request completed successfully"
);
response
}
fn add_region_headers(headers: &mut HeaderMap, region: &str, correlation_id: &str) {
if let Ok(region_value) = HeaderValue::from_str(region) {
headers.insert(REGION_HEADER, region_value);
}
if let Ok(correlation_value) = HeaderValue::from_str(correlation_id) {
headers.insert(CORRELATION_ID_HEADER, correlation_value);
}
}
pub fn get_machine_region(machine_region: Option<&str>) -> String {
machine_region.unwrap_or(DEFAULT_REGION).to_string()
}
#[instrument(skip_all)]
pub fn log_machine_operation(operation: &str, machine_id: &str, app_name: &str, region: &str) {
info!(
operation = %operation,
machine_id = %machine_id,
app_name = %app_name,
region = %region,
"Machine operation"
);
}
#[macro_export]
macro_rules! api_log {
($level:ident, $($field:ident = $value:expr),* $(,)? ; $($arg:tt)*) => {
tracing::$level!(
region = %crate::middleware::region::DEFAULT_REGION,
$($field = $value,)*
$($arg)*
)
};
}