hydro_lang/
telemetry.rs

1//! # Telemetry
2use tracing::Subscriber;
3use tracing_subscriber::fmt::{FormatEvent, FormatFields, FormattedFields};
4use tracing_subscriber::registry::LookupSpan;
5
6#[expect(
7    missing_docs,
8    reason = "This is internal code. This struct needs to be pub for some reason for the Formatter impl to work in staged code?"
9)]
10pub struct Formatter;
11
12impl<S, N> FormatEvent<S, N> for Formatter
13where
14    S: Subscriber + for<'a> LookupSpan<'a>,
15    N: for<'a> FormatFields<'a> + 'static,
16{
17    fn format_event(
18        &self,
19        ctx: &tracing_subscriber::fmt::FmtContext<'_, S, N>,
20        mut writer: tracing_subscriber::fmt::format::Writer<'_>,
21        event: &tracing::Event<'_>,
22    ) -> std::fmt::Result {
23        use colored::Colorize;
24
25        let metadata = event.metadata();
26
27        write!(
28            &mut writer,
29            "{} {} {}{} {} {}:{}: ",
30            chrono::Utc::now()
31                .format("%Y-%m-%dT%H:%M:%S%.f%:z")
32                .to_string()
33                .magenta()
34                .underline()
35                .on_white(),
36            metadata.level().as_str().red(),
37            std::thread::current()
38                .name()
39                .unwrap_or("unnamed-thread")
40                .blue(),
41            format!("({:?})", std::thread::current().id()).blue(),
42            // gettid::gettid(), TODO: can't get gettid to link properly.
43            metadata.target().green(),
44            metadata.file().unwrap_or("unknown-file").red(),
45            format!("{}", metadata.line().unwrap_or(0)).red(),
46        )?;
47
48        if let Some(scope) = ctx.event_scope() {
49            for span in scope.from_root() {
50                write!(writer, "{}", span.name().purple())?;
51
52                let ext = span.extensions();
53                let fields = &ext.get::<FormattedFields<N>>().unwrap();
54
55                if !fields.is_empty() {
56                    write!(writer, "{{{}}}", fields.cyan())?;
57                }
58
59                write!(writer, ": ")?;
60            }
61        }
62
63        write!(writer, "{}: ", metadata.name().yellow().bold().underline())?;
64
65        ctx.field_format().format_fields(writer.by_ref(), event)?;
66
67        writeln!(writer)
68    }
69}
70
71/// Initialize tracing using the above custom formatter with the default "trace" directive, if RUST_LOG is not set.
72pub fn initialize_tracing() {
73    use tracing_subscriber::filter::EnvFilter;
74
75    initialize_tracing_with_directive(
76        EnvFilter::try_from_default_env()
77            .unwrap_or_else(|_| EnvFilter::new("trace"))
78            .to_string(),
79    );
80}
81
82/// Initialize tracing using the above custom formatter, using the tracing directive.
83/// something like "{level},{abc}={level},{xyz}={level}" where {level} is one of "tracing,debug,info,warn,error"
84pub fn initialize_tracing_with_directive(directive: impl AsRef<str>) {
85    use tracing::subscriber::set_global_default;
86    use tracing_subscriber::filter::EnvFilter;
87    use tracing_subscriber::fmt::format::FmtSpan;
88    use tracing_subscriber::prelude::*;
89    use tracing_subscriber::{Layer, fmt, registry};
90
91    let filter = EnvFilter::new(directive.as_ref());
92
93    set_global_default(
94        registry().with(
95            fmt::layer()
96                .with_span_events(FmtSpan::NEW | FmtSpan::CLOSE)
97                .event_format(Formatter)
98                .with_filter(filter),
99        ),
100    )
101    .unwrap();
102
103    tracing::trace!("Tracing Initialized");
104}