khora_telemetry/storage/
backend.rs1use khora_core::telemetry::{
18 metrics::MetricType, Metric, MetricId, MetricValue, MetricsError, MetricsResult,
19};
20use std::fmt::Debug;
21
22pub trait MetricsBackend: Send + Sync + Debug + 'static {
24 fn as_any(&self) -> &dyn std::any::Any;
26 fn put_metric(&self, metric: Metric) -> MetricsResult<()>;
28
29 fn get_metric(&self, id: &MetricId) -> MetricsResult<Metric>;
31
32 fn contains_metric(&self, id: &MetricId) -> bool;
34
35 fn remove_metric(&self, id: &MetricId) -> MetricsResult<()>;
37
38 fn list_metric_ids(&self) -> Vec<MetricId>;
40
41 fn list_all_metrics(&self) -> Vec<Metric>;
43
44 fn clear_all(&self) -> MetricsResult<()>;
46
47 fn metric_count(&self) -> usize;
49
50 fn increment_counter(&self, id: &MetricId, delta: u64) -> MetricsResult<u64> {
54 let mut metric = self.get_metric(id)?;
55
56 match metric.value {
57 MetricValue::Counter(ref mut value) => {
58 *value = value.saturating_add(delta);
59 metric.metadata.update_timestamp();
60 let result = *value;
61 self.put_metric(metric)?;
62 Ok(result)
63 }
64 _ => Err(MetricsError::TypeMismatch {
65 expected: MetricType::Counter,
66 found: metric.value.metric_type(),
67 }),
68 }
69 }
70
71 fn set_gauge(&self, id: &MetricId, value: f64) -> MetricsResult<()> {
73 let mut metric = self.get_metric(id)?;
74
75 match metric.value {
76 MetricValue::Gauge(ref mut gauge_value) => {
77 *gauge_value = value;
78 metric.metadata.update_timestamp();
79 self.put_metric(metric)?;
80 Ok(())
81 }
82 _ => Err(MetricsError::TypeMismatch {
83 expected: MetricType::Gauge,
84 found: metric.value.metric_type(),
85 }),
86 }
87 }
88
89 fn record_histogram_sample(&self, id: &MetricId, sample: f64) -> MetricsResult<()> {
91 let mut metric = self.get_metric(id)?;
92
93 match metric.value {
94 MetricValue::Histogram {
95 ref mut samples,
96 ref bucket_bounds,
97 ref mut bucket_counts,
98 } => {
99 samples.push(sample);
101
102 for (i, &bound) in bucket_bounds.iter().enumerate() {
104 if sample <= bound {
105 bucket_counts[i] += 1;
106 }
107 }
108
109 metric.metadata.update_timestamp();
110 self.put_metric(metric)?;
111 Ok(())
112 }
113 _ => Err(MetricsError::TypeMismatch {
114 expected: MetricType::Histogram,
115 found: metric.value.metric_type(),
116 }),
117 }
118 }
119}
120
121#[derive(Debug, Clone)]
123pub struct BackendStats {
124 pub total_metrics: usize,
126 pub counter_count: usize,
128 pub gauge_count: usize,
130 pub histogram_count: usize,
132 pub estimated_memory_bytes: usize,
134}
135
136#[cfg(test)]
137mod tests {
138 use super::*;
139 use khora_core::telemetry::metrics::{Metric, MetricId};
140
141 #[derive(Debug)]
143 struct MockBackend;
144
145 impl MetricsBackend for MockBackend {
146 fn as_any(&self) -> &dyn std::any::Any {
147 self
148 }
149
150 fn put_metric(&self, _metric: Metric) -> MetricsResult<()> {
151 Ok(())
152 }
153
154 fn get_metric(&self, id: &MetricId) -> MetricsResult<Metric> {
155 Err(MetricsError::MetricNotFound(id.clone()))
156 }
157
158 fn contains_metric(&self, _id: &MetricId) -> bool {
159 false
160 }
161
162 fn remove_metric(&self, id: &MetricId) -> MetricsResult<()> {
163 Err(MetricsError::MetricNotFound(id.clone()))
164 }
165
166 fn list_metric_ids(&self) -> Vec<MetricId> {
167 Vec::new()
168 }
169
170 fn list_all_metrics(&self) -> Vec<Metric> {
171 Vec::new()
172 }
173
174 fn clear_all(&self) -> MetricsResult<()> {
175 Ok(())
176 }
177
178 fn metric_count(&self) -> usize {
179 0
180 }
181 }
182
183 #[test]
184 fn test_backend_trait_compilation() {
185 let backend = MockBackend;
186 assert_eq!(backend.metric_count(), 0);
187 assert!(!backend.contains_metric(&MetricId::new("test", "metric")));
188 }
189}