pub mod event;
pub mod live_log;
pub mod stats;
pub use event::{QueryEvent, TelemetrySink};
pub use live_log::LiveLog;
pub use stats::{Stats, StatsSnapshot};
use tracing::info;
use tracing_subscriber::{EnvFilter, fmt, layer::SubscriberExt, registry, util::SubscriberInitExt};
use crate::config::Config;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum InitOutcome {
Initialised,
AlreadyInitialised,
}
pub struct Telemetry;
impl Telemetry {
pub fn build_filter(directive: Option<&str>) -> EnvFilter {
directive
.and_then(|d| EnvFilter::try_new(d).ok())
.unwrap_or_else(|| EnvFilter::new("info"))
}
pub fn init() -> InitOutcome {
let directive = std::env::var("RUST_LOG").ok();
let filter = Self::build_filter(directive.as_deref());
let result = registry()
.with(filter)
.with(fmt::layer().with_writer(std::io::stdout))
.try_init();
match result {
Ok(()) => InitOutcome::Initialised,
Err(_) => InitOutcome::AlreadyInitialised,
}
}
pub fn log_startup(cfg: &Config) {
info!(
name = env!("CARGO_PKG_NAME"),
version = env!("CARGO_PKG_VERSION"),
dns_addrs = ?cfg.dns_addrs,
admin_addr = %cfg.admin_addr,
db_path = ?cfg.db_path,
"starting sagittarius",
);
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn init_is_idempotent_no_panic() {
let _first = Telemetry::init();
let second = Telemetry::init();
assert_eq!(second, InitOutcome::AlreadyInitialised);
}
#[test]
fn build_filter_none_yields_info_default() {
let filter = Telemetry::build_filter(None);
assert_eq!(filter.to_string(), "info");
}
#[test]
fn build_filter_explicit_directive_parses() {
let filter = Telemetry::build_filter(Some("sagittarius=debug"));
assert_eq!(filter.to_string(), "sagittarius=debug");
}
#[test]
fn build_filter_invalid_directive_falls_back_to_info() {
let filter = Telemetry::build_filter(Some("this=is=not=valid====="));
assert_eq!(filter.to_string(), "info");
}
#[test]
fn build_filter_multiple_directives() {
let filter = Telemetry::build_filter(Some("warn,sagittarius=debug"));
let s = filter.to_string();
assert!(
s.contains("sagittarius=debug"),
"expected directive in: {s}"
);
}
#[test]
fn log_startup_does_not_panic() {
use crate::config::SessionCookieSecurePolicy;
use std::net::SocketAddr;
use std::path::PathBuf;
let _ = Telemetry::init();
let cfg = Config {
dns_addrs: vec!["0.0.0.0:53".parse::<SocketAddr>().unwrap()],
admin_addr: "127.0.0.1:8080".parse::<SocketAddr>().unwrap(),
db_path: PathBuf::from("sagittarius.db"),
session_cookie_secure: SessionCookieSecurePolicy::Auto,
};
Telemetry::log_startup(&cfg);
}
}