sentinel_driver/
observability.rs1use std::sync::Arc;
2use std::time::Duration;
3
4#[derive(Debug, Clone)]
6pub struct QueryMetrics {
7 pub sql: String,
9 pub elapsed: Duration,
11 pub rows_affected: u64,
13 pub cache_hit: bool,
15}
16
17pub type QueryMetricsCallback = Arc<dyn Fn(&QueryMetrics) + Send + Sync>;
19
20#[derive(Clone, Default)]
22pub struct ObservabilityConfig {
23 pub slow_query_threshold: Option<Duration>,
26 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
39pub 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
52pub 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}