use crate::protocol::types::{DetailLevel, Severity};
use serde_json::{Map, Value};
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct Namespace {
pub prefix: Vec<String>,
pub inner: Vec<String>,
}
impl Namespace {
pub fn new(inner: Vec<String>) -> Self {
Self {
prefix: vec![],
inner,
}
}
pub fn complete(&self) -> Vec<String> {
let mut v = self.prefix.clone();
v.extend_from_slice(&self.inner);
v
}
pub fn to_text(&self) -> String {
self.complete().join(".")
}
}
impl std::fmt::Display for Namespace {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.to_text())
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum Privacy {
#[default]
Public,
Confidential,
}
#[derive(Debug, Clone)]
pub enum Metric {
IntM(String, i64),
DoubleM(String, f64),
CounterM(String, Option<i64>),
PrometheusM(String, Vec<(String, String)>),
}
impl Metric {
pub fn name(&self) -> &str {
match self {
Metric::IntM(n, _) => n,
Metric::DoubleM(n, _) => n,
Metric::CounterM(n, _) => n,
Metric::PrometheusM(n, _) => n,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct SeverityF(pub Option<Severity>);
impl SeverityF {
pub const SILENCE: Self = Self(None);
pub fn passes(&self, sev: Severity) -> bool {
match self.0 {
None => false,
Some(threshold) => sev >= threshold,
}
}
}
impl Default for SeverityF {
fn default() -> Self {
Self(Some(Severity::Warning))
}
}
pub trait MetaTrace {
fn namespace(&self) -> Namespace;
fn severity(&self) -> Option<Severity>;
fn privacy(&self) -> Privacy {
Privacy::Public
}
fn detail(&self) -> Option<DetailLevel> {
Some(DetailLevel::DNormal)
}
fn all_namespaces() -> Vec<Namespace>
where
Self: Sized,
{
vec![]
}
}
pub trait LogFormatting {
fn for_machine(&self, detail: DetailLevel) -> Map<String, Value>;
fn for_human(&self) -> String {
String::new()
}
fn as_metrics(&self) -> Vec<Metric> {
vec![]
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn silence_blocks_every_severity() {
for sev in [
Severity::Debug,
Severity::Info,
Severity::Notice,
Severity::Warning,
Severity::Error,
Severity::Critical,
Severity::Alert,
Severity::Emergency,
] {
assert!(
!SeverityF::SILENCE.passes(sev),
"{:?} should be blocked by Silence",
sev
);
}
}
#[test]
fn warning_threshold_blocks_below_passes_at_and_above() {
let f = SeverityF(Some(Severity::Warning));
assert!(!f.passes(Severity::Debug));
assert!(!f.passes(Severity::Info));
assert!(!f.passes(Severity::Notice));
assert!(f.passes(Severity::Warning));
assert!(f.passes(Severity::Error));
assert!(f.passes(Severity::Critical));
assert!(f.passes(Severity::Alert));
assert!(f.passes(Severity::Emergency));
}
#[test]
fn default_is_warning() {
assert_eq!(SeverityF::default(), SeverityF(Some(Severity::Warning)));
}
#[test]
fn debug_threshold_passes_all() {
let f = SeverityF(Some(Severity::Debug));
for sev in [
Severity::Debug,
Severity::Info,
Severity::Notice,
Severity::Warning,
Severity::Error,
Severity::Critical,
Severity::Alert,
Severity::Emergency,
] {
assert!(f.passes(sev), "{:?} should pass Debug threshold", sev);
}
}
#[test]
fn emergency_threshold_only_passes_emergency() {
let f = SeverityF(Some(Severity::Emergency));
for sev in [
Severity::Debug,
Severity::Info,
Severity::Notice,
Severity::Warning,
Severity::Error,
Severity::Critical,
Severity::Alert,
] {
assert!(
!f.passes(sev),
"{:?} should be blocked by Emergency threshold",
sev
);
}
assert!(f.passes(Severity::Emergency));
}
}