use crate::anomaly::sink::AnomalySink;
mod dedupe;
mod min_severity;
mod rate_limit;
mod sample;
mod tee;
pub use dedupe::DedupeAnomalies;
pub use min_severity::MinSeverity;
pub use rate_limit::RateLimitAnomalies;
pub use sample::Sample;
pub use tee::Tee;
pub trait Layer: Send + 'static {
fn wrap(self: Box<Self>, inner: Box<dyn AnomalySink>) -> Box<dyn AnomalySink>;
}
pub trait LayerSpec: Send + Sync + 'static {
fn instantiate(&self) -> Box<dyn Layer>;
}
impl<L> LayerSpec for L
where
L: Layer + Clone + Sync,
{
fn instantiate(&self) -> Box<dyn Layer> {
Box::new(self.clone())
}
}
pub struct LayerFactory<F>(pub F);
impl<F> LayerSpec for LayerFactory<F>
where
F: Fn() -> Box<dyn Layer> + Send + Sync + 'static,
{
fn instantiate(&self) -> Box<dyn Layer> {
(self.0)()
}
}
#[cfg(test)]
mod tests {
use std::borrow::Cow;
use std::sync::{Arc, Mutex};
use std::time::Duration;
use flowscope::Timestamp;
use super::*;
use crate::anomaly::Severity;
use crate::anomaly::sink::AnomalySink;
#[derive(Default)]
struct CaptureSink {
calls: Arc<Mutex<Vec<(&'static str, Severity)>>>,
}
impl CaptureSink {
fn calls(&self) -> Arc<Mutex<Vec<(&'static str, Severity)>>> {
Arc::clone(&self.calls)
}
}
impl AnomalySink for CaptureSink {
fn write(
&mut self,
kind: &'static str,
severity: Severity,
_ts: Timestamp,
_key: Option<&dyn crate::anomaly::Key>,
_observations: &[(&'static str, Cow<'_, str>)],
_metrics: &[(&'static str, f64)],
) {
self.calls.lock().unwrap().push((kind, severity));
}
}
fn apply(layers: Vec<Box<dyn Layer>>, base: Box<dyn AnomalySink>) -> Box<dyn AnomalySink> {
let mut s = base;
for layer in layers.into_iter().rev() {
s = layer.wrap(s);
}
s
}
#[test]
fn layer_order_outermost_first() {
let base = CaptureSink::default();
let calls = base.calls();
let layers: Vec<Box<dyn Layer>> = vec![
Box::new(MinSeverity::at_least(Severity::Warning)),
Box::new(DedupeAnomalies::within(Duration::from_secs(60))),
];
let mut sink = apply(layers, Box::new(base));
sink.begin("LowSev", Severity::Info, Timestamp::new(0, 0))
.emit();
sink.begin("MidSev", Severity::Warning, Timestamp::new(0, 0))
.emit();
sink.begin("MidSev", Severity::Warning, Timestamp::new(0, 0))
.emit();
sink.begin("HighSev", Severity::Error, Timestamp::new(0, 0))
.emit();
let calls = calls.lock().unwrap();
assert_eq!(
*calls,
vec![("MidSev", Severity::Warning), ("HighSev", Severity::Error)]
);
}
}