Skip to main content

sentinel_driver/
observability.rs

1use std::sync::Arc;
2use std::time::Duration;
3
4/// Metrics for a completed query execution.
5#[derive(Debug, Clone)]
6pub struct QueryMetrics {
7    /// The SQL statement that was executed.
8    pub sql: String,
9    /// Time taken to execute the query.
10    pub elapsed: Duration,
11    /// Number of rows affected (for INSERT/UPDATE/DELETE) or returned.
12    pub rows_affected: u64,
13    /// Whether the statement was served from the prepared statement cache.
14    pub cache_hit: bool,
15}
16
17/// Callback invoked after every query completion.
18pub type QueryMetricsCallback = Arc<dyn Fn(&QueryMetrics) + Send + Sync>;
19
20/// Configuration for query observability.
21#[derive(Clone, Default)]
22pub struct ObservabilityConfig {
23    /// Queries exceeding this duration are logged at WARN level.
24    /// `None` disables slow query logging.
25    pub slow_query_threshold: Option<Duration>,
26    /// Optional callback invoked after every query with timing metrics.
27    pub on_query: Option<QueryMetricsCallback>,
28}
29
30impl std::fmt::Debug for ObservabilityConfig {
31    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
32        f.debug_struct("ObservabilityConfig")
33            .field("slow_query_threshold", &self.slow_query_threshold)
34            .field("on_query", &self.on_query.as_ref().map(|_| ".."))
35            .finish()
36    }
37}
38
39/// Log a slow query warning via tracing.
40pub fn log_slow_query(sql: &str, elapsed: Duration, threshold: Duration) {
41    if elapsed > threshold {
42        let truncated = if sql.len() > 200 { &sql[..200] } else { sql };
43        tracing::warn!(
44            sql = %truncated,
45            elapsed_ms = %elapsed.as_millis(),
46            threshold_ms = %threshold.as_millis(),
47            "slow query detected"
48        );
49    }
50}
51
52/// Emit a tracing span for a query execution.
53pub fn query_span(sql: &str) -> tracing::Span {
54    let truncated = if sql.len() > 100 { &sql[..100] } else { sql };
55    tracing::info_span!("pg.query", db.statement = %truncated)
56}