minifly_api/middleware/
region.rs1use axum::{
8 extract::Request,
9 http::{HeaderMap, HeaderValue},
10 middleware::Next,
11 response::Response,
12};
13use tracing::{info, instrument, Span};
14use uuid::Uuid;
15use minifly_logging::fields;
16
17pub const REGION_HEADER: &str = "x-minifly-region";
19
20pub const CORRELATION_ID_HEADER: &str = "x-minifly-correlation-id";
22
23pub const DEFAULT_REGION: &str = "local";
25
26#[instrument(
34 name = "region_middleware",
35 skip_all,
36 fields(
37 region = %DEFAULT_REGION,
38 correlation_id = tracing::field::Empty,
39 request_id = tracing::field::Empty,
40 http.method = %request.method(),
41 http.path = %request.uri().path(),
42 http.user_agent = tracing::field::Empty,
43 http.status = tracing::field::Empty,
44 duration_ms = tracing::field::Empty,
45 )
46)]
47pub async fn region_middleware(request: Request, next: Next) -> Response {
48 let correlation_id = minifly_logging::new_correlation_id();
49 let request_id = minifly_logging::new_request_id();
50 let region = DEFAULT_REGION.to_string();
51
52 Span::current().record(fields::CORRELATION_ID, &correlation_id);
54 Span::current().record(fields::REQUEST_ID, &request_id);
55 Span::current().record(fields::REGION, ®ion);
56
57 if let Some(user_agent) = request.headers().get("user-agent") {
59 if let Ok(ua_str) = user_agent.to_str() {
60 Span::current().record(fields::HTTP_USER_AGENT, ua_str);
61 }
62 }
63
64 info!(
65 operation = "http_request_start",
66 "Processing HTTP request"
67 );
68
69 let start_time = std::time::Instant::now();
70
71 let mut response = next.run(request).await;
73
74 let duration = start_time.elapsed();
75
76 Span::current().record(fields::HTTP_STATUS, response.status().as_u16());
78 Span::current().record(fields::DURATION_MS, duration.as_millis());
79
80 let headers = response.headers_mut();
82 add_region_headers(headers, ®ion, &correlation_id);
83
84 info!(
85 operation = "http_request_complete",
86 operation.status = "success",
87 "HTTP request completed successfully"
88 );
89
90 response
91}
92
93fn add_region_headers(headers: &mut HeaderMap, region: &str, correlation_id: &str) {
100 if let Ok(region_value) = HeaderValue::from_str(region) {
101 headers.insert(REGION_HEADER, region_value);
102 }
103
104 if let Ok(correlation_value) = HeaderValue::from_str(correlation_id) {
105 headers.insert(CORRELATION_ID_HEADER, correlation_value);
106 }
107}
108
109pub fn get_machine_region(machine_region: Option<&str>) -> String {
117 machine_region.unwrap_or(DEFAULT_REGION).to_string()
118}
119
120#[instrument(skip_all)]
128pub fn log_machine_operation(operation: &str, machine_id: &str, app_name: &str, region: &str) {
129 info!(
130 operation = %operation,
131 machine_id = %machine_id,
132 app_name = %app_name,
133 region = %region,
134 "Machine operation"
135 );
136}
137
138#[macro_export]
143macro_rules! api_log {
144 ($level:ident, $($field:ident = $value:expr),* $(,)? ; $($arg:tt)*) => {
145 tracing::$level!(
146 region = %crate::middleware::region::DEFAULT_REGION,
147 $($field = $value,)*
148 $($arg)*
149 )
150 };
151}