use std::borrow::Cow;
use flowscope::Timestamp;
use crate::anomaly::Severity;
use crate::anomaly::key::Key;
use crate::anomaly::sink::AnomalySink;
#[derive(Clone, Debug)]
pub struct MetricsSink {
counter_name: &'static str,
histogram_name: &'static str,
}
impl Default for MetricsSink {
fn default() -> Self {
Self {
counter_name: "netring_anomaly_total",
histogram_name: "netring_anomaly_metric",
}
}
}
impl MetricsSink {
pub fn with_counter_name(mut self, name: &'static str) -> Self {
self.counter_name = name;
self
}
pub fn with_histogram_name(mut self, name: &'static str) -> Self {
self.histogram_name = name;
self
}
pub fn counter_name(&self) -> &'static str {
self.counter_name
}
pub fn histogram_name(&self) -> &'static str {
self.histogram_name
}
}
impl AnomalySink for MetricsSink {
fn write(
&mut self,
kind: &'static str,
severity: Severity,
_ts: Timestamp,
_key: Option<&dyn Key>,
_observations: &[(&'static str, Cow<'_, str>)],
metrics: &[(&'static str, f64)],
) {
::metrics::counter!(
self.counter_name,
"kind" => kind,
"severity" => severity.as_str(),
)
.increment(1);
for (label, value) in metrics {
::metrics::histogram!(
self.histogram_name,
"metric" => *label,
)
.record(*value);
}
}
fn flush(&mut self) -> Result<(), std::io::Error> {
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::anomaly::sink::AnomalySinkExt;
#[test]
fn default_names_match_plan() {
let s = MetricsSink::default();
assert_eq!(s.counter_name(), "netring_anomaly_total");
assert_eq!(s.histogram_name(), "netring_anomaly_metric");
}
#[test]
fn name_overrides_take_effect() {
let s = MetricsSink::default()
.with_counter_name("my_anomalies")
.with_histogram_name("my_anomaly_metric");
assert_eq!(s.counter_name(), "my_anomalies");
assert_eq!(s.histogram_name(), "my_anomaly_metric");
}
#[test]
fn write_through_anomalywriter_does_not_panic() {
let mut sink = MetricsSink::default();
sink.begin("TestKind", Severity::Warning, Timestamp::new(0, 0))
.with_metric("latency_ms", 42.5)
.with_metric("size_bytes", 1024.0)
.with("note", "synthetic")
.emit();
}
}