use std::sync::{Arc, Mutex};
use serde_json::Value;
use super::OpsLog;
#[derive(Debug, Clone, PartialEq)]
pub struct RecordedCounter {
pub name: String,
pub labels: Vec<(String, String)>,
pub value: f64,
}
#[derive(Debug, Clone, PartialEq)]
pub struct RecordedGauge {
pub name: String,
pub labels: Vec<(String, String)>,
pub value: f64,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct RecordedEvent {
pub name: String,
pub payload: Value,
}
#[derive(Debug, Default)]
struct Inner {
counters: Vec<RecordedCounter>,
gauges: Vec<RecordedGauge>,
events: Vec<RecordedEvent>,
}
#[derive(Debug, Clone)]
pub struct RecordingOpsLog {
inner: Arc<Mutex<Inner>>,
}
impl RecordingOpsLog {
#[must_use]
pub fn new() -> Self {
Self {
inner: Arc::new(Mutex::new(Inner::default())),
}
}
pub fn clear(&self) {
let mut g = self.inner.lock().expect("recording ops log lock");
g.counters.clear();
g.gauges.clear();
g.events.clear();
}
#[must_use]
pub fn counters(&self) -> Vec<RecordedCounter> {
self.inner
.lock()
.expect("recording ops log lock")
.counters
.clone()
}
#[must_use]
pub fn gauges(&self) -> Vec<RecordedGauge> {
self.inner
.lock()
.expect("recording ops log lock")
.gauges
.clone()
}
#[must_use]
pub fn events(&self) -> Vec<RecordedEvent> {
self.inner
.lock()
.expect("recording ops log lock")
.events
.clone()
}
#[must_use]
pub fn recorded_counters_matching(
&self,
name: &str,
label_subset: &[(&str, &str)],
) -> Vec<RecordedCounter> {
self.counters()
.into_iter()
.filter(|c| c.name == name && labels_contain(&c.labels, label_subset))
.collect()
}
#[must_use]
pub fn recorded_events_for(&self, event_name: &str) -> Vec<RecordedEvent> {
self.events()
.into_iter()
.filter(|e| e.name == event_name)
.collect()
}
}
fn labels_contain(labels: &[(String, String)], subset: &[(&str, &str)]) -> bool {
subset.iter().all(|(k, v)| {
labels
.iter()
.any(|(lk, lv)| lk.as_str() == *k && lv.as_str() == *v)
})
}
impl OpsLog for RecordingOpsLog {
fn record_counter(&self, name: &str, labels: &[(&str, &str)], value: f64) {
let labels: Vec<(String, String)> = labels
.iter()
.map(|(k, v)| (k.to_string(), v.to_string()))
.collect();
self.inner
.lock()
.expect("recording ops log lock")
.counters
.push(RecordedCounter {
name: name.to_string(),
labels,
value,
});
}
fn record_gauge(&self, name: &str, labels: &[(&str, &str)], value: f64) {
let labels: Vec<(String, String)> = labels
.iter()
.map(|(k, v)| (k.to_string(), v.to_string()))
.collect();
self.inner
.lock()
.expect("recording ops log lock")
.gauges
.push(RecordedGauge {
name: name.to_string(),
labels,
value,
});
}
fn log_event(&self, name: &str, payload: &Value) {
self.inner
.lock()
.expect("recording ops log lock")
.events
.push(RecordedEvent {
name: name.to_string(),
payload: payload.clone(),
});
}
}
impl Default for RecordingOpsLog {
fn default() -> Self {
Self::new()
}
}