kindly_guard_server/telemetry/
standard.rs1use super::{
17 async_trait, Result, TelemetryConfig, TelemetryMetric, TelemetryProvider,
18 TelemetryProviderFactory, TelemetrySpan,
19};
20use std::sync::{Arc, Mutex};
21use tracing::{debug, info};
22
23pub struct StandardTelemetryProvider {
25 config: TelemetryConfig,
26 spans: Arc<Mutex<Vec<TelemetrySpan>>>,
27 metrics: Arc<Mutex<Vec<TelemetryMetric>>>,
28 events: Arc<Mutex<Vec<(String, Vec<(String, String)>)>>>,
29}
30
31impl StandardTelemetryProvider {
32 pub fn new(config: TelemetryConfig) -> Self {
33 Self {
34 config,
35 spans: Arc::new(Mutex::new(Vec::new())),
36 metrics: Arc::new(Mutex::new(Vec::new())),
37 events: Arc::new(Mutex::new(Vec::new())),
38 }
39 }
40
41 pub fn get_metrics(&self) -> Vec<TelemetryMetric> {
43 self.metrics.lock().unwrap().clone()
44 }
45}
46
47#[async_trait]
48impl TelemetryProvider for StandardTelemetryProvider {
49 fn start_span(&self, name: &str) -> TelemetrySpan {
50 let span = TelemetrySpan {
51 name: name.to_string(),
52 start_time: std::time::Instant::now(),
53 attributes: vec![
54 ("service.name".to_string(), self.config.service_name.clone()),
55 (
56 "service.version".to_string(),
57 self.config.service_version.clone(),
58 ),
59 ],
60 };
61
62 if self.config.tracing_enabled {
63 debug!("Starting span: {}", name);
64 }
65
66 span
67 }
68
69 fn end_span(&self, span: TelemetrySpan) {
70 if !self.config.tracing_enabled {
71 return;
72 }
73
74 let duration = span.start_time.elapsed();
75 debug!("Ending span: {} (duration: {:?})", span.name, duration);
76
77 if let Ok(mut spans) = self.spans.lock() {
78 spans.push(span);
79
80 if spans.len() > 1000 {
82 spans.drain(0..100);
83 }
84 }
85 }
86
87 fn record_metric(&self, metric: TelemetryMetric) {
88 if !self.config.metrics_enabled {
89 return;
90 }
91
92 debug!("Recording metric: {} = {:?}", metric.name, metric.value);
93
94 if let Ok(mut metrics) = self.metrics.lock() {
95 metrics.push(metric);
96
97 if metrics.len() > 10000 {
99 metrics.drain(0..1000);
100 }
101 }
102 }
103
104 fn add_event(&self, name: &str, attributes: Vec<(&str, &str)>) {
105 if !self.config.tracing_enabled {
106 return;
107 }
108
109 debug!("Adding event: {}", name);
110
111 let attrs: Vec<(String, String)> = attributes
112 .into_iter()
113 .map(|(k, v)| (k.to_string(), v.to_string()))
114 .collect();
115
116 if let Ok(mut events) = self.events.lock() {
117 events.push((name.to_string(), attrs));
118
119 if events.len() > 5000 {
121 events.drain(0..500);
122 }
123 }
124 }
125
126 fn set_status(&self, span: &TelemetrySpan, is_error: bool, message: Option<&str>) {
127 if !self.config.tracing_enabled {
128 return;
129 }
130
131 debug!(
132 "Setting span status for {}: error={}, message={:?}",
133 span.name, is_error, message
134 );
135 }
136
137 async fn flush(&self) -> Result<()> {
138 info!("Flushing telemetry data");
139
140 if let Some(endpoint) = &self.config.export_endpoint {
142 debug!("Would export to: {}", endpoint);
143 }
144
145 let spans_count = self.spans.lock().map(|s| s.len()).unwrap_or(0);
147 let metrics_count = self.metrics.lock().map(|m| m.len()).unwrap_or(0);
148 let events_count = self.events.lock().map(|e| e.len()).unwrap_or(0);
149
150 info!(
151 "Telemetry summary: {} spans, {} metrics, {} events",
152 spans_count, metrics_count, events_count
153 );
154
155 Ok(())
156 }
157
158 async fn shutdown(&self) -> Result<()> {
159 info!("Shutting down telemetry provider");
160 self.flush().await?;
161
162 if let Ok(mut spans) = self.spans.lock() {
164 spans.clear();
165 }
166 if let Ok(mut metrics) = self.metrics.lock() {
167 metrics.clear();
168 }
169 if let Ok(mut events) = self.events.lock() {
170 events.clear();
171 }
172
173 Ok(())
174 }
175}
176
177pub struct StandardTelemetryFactory;
179
180impl TelemetryProviderFactory for StandardTelemetryFactory {
181 fn create(&self, config: &TelemetryConfig) -> Result<Arc<dyn TelemetryProvider>> {
182 Ok(Arc::new(StandardTelemetryProvider::new(config.clone())))
183 }
184}
185
186#[cfg(test)]
187mod tests {
188 use super::*;
189
190 #[tokio::test]
191 async fn test_standard_telemetry() {
192 let config = TelemetryConfig::default();
193 let provider = StandardTelemetryProvider::new(config);
194
195 let span = provider.start_span("test.operation");
197 std::thread::sleep(std::time::Duration::from_millis(10));
198 provider.end_span(span);
199
200 provider.record_metric(TelemetryMetric {
202 name: "test.counter".to_string(),
203 value: crate::telemetry::MetricValue::Counter(42),
204 labels: vec![],
205 });
206
207 provider.add_event("test.event", vec![("key", "value")]);
209
210 let metrics = provider.get_metrics();
212 assert_eq!(metrics.len(), 1);
213 assert_eq!(metrics[0].name, "test.counter");
214
215 provider.flush().await.unwrap();
217 }
218}