use std::sync::{
OnceLock,
atomic::{AtomicBool, Ordering},
};
pub struct TracePoint {
pub name: &'static str,
pub module: &'static str,
enabled: AtomicBool,
}
impl TracePoint {
#[must_use]
pub const fn new(name: &'static str, module: &'static str) -> Self {
Self {
name,
module,
enabled: AtomicBool::new(false),
}
}
#[must_use]
pub fn is_enabled(&self) -> bool {
self.enabled.load(Ordering::Relaxed)
}
pub fn enable(&self) {
self.enabled.store(true, Ordering::Relaxed);
}
pub fn disable(&self) {
self.enabled.store(false, Ordering::Relaxed);
}
}
#[derive(Debug, Clone)]
pub struct TraceEvent {
pub timestamp_ns: u64,
pub tracepoint: &'static str,
pub module: &'static str,
pub message: String,
}
pub trait TraceSink: Send + Sync {
fn emit(&self, event: TraceEvent);
}
static TRACE_SINK: OnceLock<Box<dyn TraceSink>> = OnceLock::new();
pub fn set_trace_sink(sink: Box<dyn TraceSink>) {
let _ = TRACE_SINK.set(sink);
}
#[allow(clippy::cast_possible_truncation)] pub fn emit_trace(tracepoint: &'static str, module: &'static str, message: String) {
if let Some(sink) = TRACE_SINK.get() {
sink.emit(TraceEvent {
timestamp_ns: std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.map_or(0, |d| d.as_nanos() as u64),
tracepoint,
module,
message,
});
}
}
#[macro_export]
macro_rules! define_tracepoint {
($name:ident, $module:expr) => {
static $name: $crate::debug::TracePoint =
$crate::debug::TracePoint::new(stringify!($name), $module);
};
}
#[macro_export]
macro_rules! trace_event {
($tp:expr, $($arg:tt)*) => {
if $tp.is_enabled() {
$crate::debug::emit_trace($tp.name, $tp.module, format!($($arg)*));
}
};
}