Skip to main content

rust_ef/observability/
query_guard.rs

1use std::time::Duration;
2#[cfg(feature = "tracing")]
3use std::time::Instant;
4
5/// Guards a query execution span, emitting timing events on drop.
6///
7/// When `tracing` is enabled, logs query start (`DEBUG`) and completion
8/// (`DEBUG`, or `WARN` if slow). When disabled, it is a ZST no-op.
9#[cfg(feature = "tracing")]
10pub struct QueryGuard {
11    sql: String,
12    start: Instant,
13    threshold: Option<Duration>,
14}
15
16#[cfg(feature = "tracing")]
17impl QueryGuard {
18    pub fn new(sql: &str, threshold: Option<Duration>) -> Self {
19        tracing::debug!(target: "rust_ef::query", sql = %sql, "query started");
20        Self {
21            sql: sql.to_string(),
22            start: Instant::now(),
23            threshold,
24        }
25    }
26}
27
28#[cfg(feature = "tracing")]
29impl Drop for QueryGuard {
30    fn drop(&mut self) {
31        let elapsed = self.start.elapsed();
32        let elapsed_ms = elapsed.as_millis() as u64;
33        if let Some(threshold) = self.threshold {
34            if elapsed >= threshold {
35                tracing::warn!(
36                    target: "rust_ef::query",
37                    sql = %self.sql,
38                    elapsed_ms,
39                    threshold_ms = threshold.as_millis() as u64,
40                    "slow query detected"
41                );
42                return;
43            }
44        }
45        tracing::debug!(
46            target: "rust_ef::query",
47            sql = %self.sql,
48            elapsed_ms,
49            "query completed"
50        );
51    }
52}
53
54/// No-op stub when tracing is disabled — zero-sized, eliminated by compiler.
55#[cfg(not(feature = "tracing"))]
56pub struct QueryGuard;
57
58#[cfg(not(feature = "tracing"))]
59impl QueryGuard {
60    pub fn new(_sql: &str, _threshold: Option<Duration>) -> Self {
61        Self
62    }
63}