rust_webx_host/
request_tracing.rs1use rust_webx_core::error::Result;
4use rust_webx_core::http::IHttpContext;
5use rust_webx_core::middleware::IMiddleware;
6use std::ops::ControlFlow;
7use std::sync::atomic::{AtomicU64, Ordering};
8use std::sync::{LazyLock, Mutex};
9
10static COUNTER: AtomicU64 = AtomicU64::new(0);
11
12pub struct RequestTracing {
13 pub log_all: bool,
14}
15
16impl RequestTracing {
17 pub fn new() -> Self {
18 Self { log_all: true }
19 }
20 pub fn errors_only() -> Self {
21 Self { log_all: false }
22 }
23}
24
25impl Default for RequestTracing {
26 fn default() -> Self {
27 Self::new()
28 }
29}
30
31#[async_trait::async_trait]
32impl IMiddleware for RequestTracing {
33 async fn invoke(&self, ctx: &mut dyn IHttpContext) -> Result<ControlFlow<()>> {
34 let tid = next_trace_id();
35 ctx.response_mut().set_header("x-trace-id", &tid);
36 Ok(ControlFlow::Continue(()))
37 }
38
39 async fn after(&self, ctx: &mut dyn IHttpContext) -> Result<()> {
40 let status = ctx.response().status();
41 let is_err = status >= 400;
42
43 if self.log_all || is_err {
44 let count = COUNTER.fetch_add(1, Ordering::Relaxed);
45 let method = ctx.request().method().to_string();
46 let path = ctx.request().path().to_string();
47 let user = ctx.claims().map(|c| c.subject().to_string());
48
49 if is_err {
50 tracing::warn!(
51 count = count, method = %method, path = %path,
52 status = status, user = %user.as_deref().unwrap_or("anon"),
53 "request error"
54 );
55 } else {
56 tracing::info!(
57 count = count, method = %method, path = %path,
58 status = status, user = %user.as_deref().unwrap_or("anon"),
59 "request ok"
60 );
61 }
62 }
63 Ok(())
64 }
65}
66
67struct XorShift(std::sync::Mutex<u64>);
68static XORSHIFT: LazyLock<XorShift> = LazyLock::new(|| {
69 XorShift(Mutex::new(
70 std::time::SystemTime::now()
71 .duration_since(std::time::UNIX_EPOCH)
72 .map(|d| d.as_nanos() as u64)
73 .unwrap_or(1),
74 ))
75});
76
77
78static TRACE_BUF: LazyLock<Mutex<String>> = LazyLock::new(|| Mutex::new(String::with_capacity(16)));
79
80fn next_trace_id() -> String {
81 let id = {
82 let mut x = match XORSHIFT.0.lock() {
83 Ok(g) => g,
84 Err(_) => return format!("{:016x}", fast_rand()),
85 };
86 *x ^= *x << 13;
87 *x ^= *x >> 7;
88 *x ^= *x << 17;
89 *x
90 };
91 let mut buf = match TRACE_BUF.lock() {
92 Ok(g) => g,
93 Err(_) => return format!("{:016x}", id),
94 };
95 buf.clear();
96 use std::fmt::Write;
97 let _ = write!(&mut *buf, "{:016x}", id);
98 buf.clone()
99}
100
101fn fast_rand() -> u64 {
102 let t = std::time::SystemTime::now()
103 .duration_since(std::time::UNIX_EPOCH)
104 .map(|d| d.as_nanos() as u64)
105 .unwrap_or(42);
106 t.wrapping_mul(6364136223846793005).wrapping_add(1442695040888963407)
107}