photon_telemetry/
recording.rs1use std::sync::{Arc, Mutex};
4
5use serde_json::Value;
6
7use super::OpsLog;
8
9#[derive(Debug, Clone, PartialEq)]
11pub struct RecordedCounter {
12 pub name: String,
14 pub labels: Vec<(String, String)>,
16 pub value: f64,
18}
19
20#[derive(Debug, Clone, PartialEq)]
22pub struct RecordedGauge {
23 pub name: String,
25 pub labels: Vec<(String, String)>,
27 pub value: f64,
29}
30
31#[derive(Debug, Clone, PartialEq, Eq)]
33pub struct RecordedEvent {
34 pub name: String,
36 pub payload: Value,
38}
39
40#[derive(Debug, Default)]
41struct Inner {
42 counters: Vec<RecordedCounter>,
43 gauges: Vec<RecordedGauge>,
44 events: Vec<RecordedEvent>,
45}
46
47#[derive(Debug, Clone)]
49pub struct RecordingOpsLog {
50 inner: Arc<Mutex<Inner>>,
51}
52
53impl RecordingOpsLog {
54 #[must_use]
56 pub fn new() -> Self {
57 Self {
58 inner: Arc::new(Mutex::new(Inner::default())),
59 }
60 }
61
62 pub fn clear(&self) {
68 let mut g = self.inner.lock().expect("recording ops log lock");
69 g.counters.clear();
70 g.gauges.clear();
71 g.events.clear();
72 }
73
74 #[must_use]
80 pub fn counters(&self) -> Vec<RecordedCounter> {
81 self.inner
82 .lock()
83 .expect("recording ops log lock")
84 .counters
85 .clone()
86 }
87
88 #[must_use]
94 pub fn gauges(&self) -> Vec<RecordedGauge> {
95 self.inner
96 .lock()
97 .expect("recording ops log lock")
98 .gauges
99 .clone()
100 }
101
102 #[must_use]
108 pub fn events(&self) -> Vec<RecordedEvent> {
109 self.inner
110 .lock()
111 .expect("recording ops log lock")
112 .events
113 .clone()
114 }
115
116 #[must_use]
118 pub fn recorded_counters_matching(
119 &self,
120 name: &str,
121 label_subset: &[(&str, &str)],
122 ) -> Vec<RecordedCounter> {
123 self.counters()
124 .into_iter()
125 .filter(|c| c.name == name && labels_contain(&c.labels, label_subset))
126 .collect()
127 }
128
129 #[must_use]
131 pub fn recorded_events_for(&self, event_name: &str) -> Vec<RecordedEvent> {
132 self.events()
133 .into_iter()
134 .filter(|e| e.name == event_name)
135 .collect()
136 }
137}
138
139fn labels_contain(labels: &[(String, String)], subset: &[(&str, &str)]) -> bool {
140 subset.iter().all(|(k, v)| {
141 labels
142 .iter()
143 .any(|(lk, lv)| lk.as_str() == *k && lv.as_str() == *v)
144 })
145}
146
147impl OpsLog for RecordingOpsLog {
148 fn record_counter(&self, name: &str, labels: &[(&str, &str)], value: f64) {
149 let labels: Vec<(String, String)> = labels
150 .iter()
151 .map(|(k, v)| (k.to_string(), v.to_string()))
152 .collect();
153 self.inner
154 .lock()
155 .expect("recording ops log lock")
156 .counters
157 .push(RecordedCounter {
158 name: name.to_string(),
159 labels,
160 value,
161 });
162 }
163
164 fn record_gauge(&self, name: &str, labels: &[(&str, &str)], value: f64) {
165 let labels: Vec<(String, String)> = labels
166 .iter()
167 .map(|(k, v)| (k.to_string(), v.to_string()))
168 .collect();
169 self.inner
170 .lock()
171 .expect("recording ops log lock")
172 .gauges
173 .push(RecordedGauge {
174 name: name.to_string(),
175 labels,
176 value,
177 });
178 }
179
180 fn log_event(&self, name: &str, payload: &Value) {
181 self.inner
182 .lock()
183 .expect("recording ops log lock")
184 .events
185 .push(RecordedEvent {
186 name: name.to_string(),
187 payload: payload.clone(),
188 });
189 }
190}
191
192impl Default for RecordingOpsLog {
193 fn default() -> Self {
194 Self::new()
195 }
196}