Skip to main content

hydro_lang/telemetry/
mod.rs

1//! # Telemetry
2use tracing::Subscriber;
3use tracing_subscriber::EnvFilter;
4use tracing_subscriber::fmt::{FormatEvent, FormatFields, FormattedFields};
5use tracing_subscriber::registry::LookupSpan;
6
7use crate::location::{LocationKey, LocationType};
8
9#[cfg(feature = "telemetry_emf")]
10pub mod emf;
11
12struct Formatter;
13
14impl<S, N> FormatEvent<S, N> for Formatter
15where
16    S: Subscriber + for<'a> LookupSpan<'a>,
17    N: for<'a> FormatFields<'a> + 'static,
18{
19    fn format_event(
20        &self,
21        ctx: &tracing_subscriber::fmt::FmtContext<'_, S, N>,
22        mut writer: tracing_subscriber::fmt::format::Writer<'_>,
23        event: &tracing::Event<'_>,
24    ) -> std::fmt::Result {
25        use colored::Colorize;
26
27        let metadata = event.metadata();
28
29        if writer.has_ansi_escapes() {
30            write!(
31                &mut writer,
32                "{} {} {}{} {} {}:{}: ",
33                chrono::Utc::now()
34                    .format("%Y-%m-%dT%H:%M:%S%.f%:z")
35                    .to_string()
36                    .magenta()
37                    .underline()
38                    .on_white(),
39                metadata.level().as_str().red(),
40                std::thread::current()
41                    .name()
42                    .unwrap_or("unnamed-thread")
43                    .blue(),
44                format!("({:?})", std::thread::current().id()).blue(),
45                // gettid::gettid(), TODO: can't get gettid to link properly.
46                metadata.target().green(),
47                metadata.file().unwrap_or("unknown-file").red(),
48                format!("{}", metadata.line().unwrap_or(0)).red(),
49            )?;
50        } else {
51            write!(
52                &mut writer,
53                "{} {} {}{:?} {} {}:{}: ",
54                chrono::Utc::now().format("%Y-%m-%dT%H:%M:%S%.f%:z"),
55                metadata.level().as_str().red(),
56                std::thread::current().name().unwrap_or("unnamed-thread"),
57                std::thread::current().id(),
58                // gettid::gettid(), TODO: can't get gettid to link properly.
59                metadata.target(),
60                metadata.file().unwrap_or("unknown-file"),
61                metadata.line().unwrap_or(0),
62            )?;
63        }
64
65        if let Some(scope) = ctx.event_scope() {
66            for span in scope.from_root() {
67                if writer.has_ansi_escapes() {
68                    write!(writer, "{}", span.name().purple())?;
69                } else {
70                    write!(writer, "{}", span.name())?;
71                }
72
73                let ext = span.extensions();
74                let fields = &ext.get::<FormattedFields<N>>().unwrap();
75
76                if !fields.is_empty() {
77                    if writer.has_ansi_escapes() {
78                        write!(writer, "{{{}}}", fields.cyan())?;
79                    } else {
80                        write!(writer, "{{{}}}", fields)?;
81                    }
82                }
83
84                write!(writer, ": ")?;
85            }
86        }
87
88        if writer.has_ansi_escapes() {
89            write!(writer, "{}: ", metadata.name().yellow().bold().underline())?;
90        } else {
91            write!(writer, "{}: ", metadata.name())?;
92        }
93
94        ctx.field_format().format_fields(writer.by_ref(), event)?;
95
96        writeln!(writer)
97    }
98}
99
100/// Initialize tracing using the above custom formatter with the default directive level of "ERROR", if RUST_LOG is not set.
101pub fn initialize_tracing() {
102    let rust_log = std::env::var("RUST_LOG").unwrap_or_else(|err| {
103        match err {
104            std::env::VarError::NotPresent => {
105                // RUST_LOG not set, the user wants the default.
106                "error".to_owned()
107            }
108            std::env::VarError::NotUnicode(v) => {
109                // Almost certainly there is a configuration issue.
110                eprintln!(
111                    "RUST_LOG is not unicode, defaulting to 'error' directive: {:?}",
112                    v
113                );
114                "error".to_owned()
115            }
116        }
117    });
118
119    let filter = EnvFilter::try_new(&rust_log).unwrap_or_else(|err| {
120        // Configuration error.
121        eprintln!("Failed to parse RUST_LOG: {}, err: {:?}", rust_log, err);
122        "error".to_owned().parse().unwrap()
123    });
124
125    initialize_tracing_with_filter(filter)
126}
127
128/// Initialize tracing using the above custom formatter, using the tracing directive.
129/// something like "{level},{abc}={level},{xyz}={level}" where {level} is one of "tracing,debug,info,warn,error"
130pub fn initialize_tracing_with_filter(filter: EnvFilter) {
131    use tracing::subscriber::set_global_default;
132    use tracing_subscriber::fmt::format::FmtSpan;
133    use tracing_subscriber::prelude::*;
134    use tracing_subscriber::{Layer, fmt, registry};
135
136    set_global_default(
137        registry().with(
138            fmt::layer()
139                .with_writer(std::io::stderr)
140                .with_span_events(FmtSpan::NEW | FmtSpan::CLOSE)
141                .event_format(Formatter)
142                .with_filter(filter.clone()),
143        ),
144    )
145    .unwrap();
146
147    #[expect(
148        non_snake_case,
149        reason = "this variable represents an env var which is in all caps"
150    )]
151    let RUST_LOG = std::env::var("RUST_LOG");
152
153    tracing::trace!(name: "Tracing Initialized", ?RUST_LOG, ?filter);
154}
155
156/// Used to add a sidecar to generated code.
157pub trait Sidecar {
158    /// Generates code to create a sidecar.
159    ///
160    /// The generated code should be an expression which evaluates as a `Future`.
161    fn to_expr(
162        &self,
163        flow_name: &str,
164        location_key: LocationKey,
165        location_type: LocationType,
166        location_name: &str,
167        dfir_ident: &syn::Ident,
168    ) -> syn::Expr;
169}