use tracing::Subscriber;
use tracing_subscriber::EnvFilter;
use tracing_subscriber::fmt::{FormatEvent, FormatFields, FormattedFields};
use tracing_subscriber::registry::LookupSpan;
use crate::location::{LocationKey, LocationType};
pub mod emf;
struct Formatter;
impl<S, N> FormatEvent<S, N> for Formatter
where
S: Subscriber + for<'a> LookupSpan<'a>,
N: for<'a> FormatFields<'a> + 'static,
{
fn format_event(
&self,
ctx: &tracing_subscriber::fmt::FmtContext<'_, S, N>,
mut writer: tracing_subscriber::fmt::format::Writer<'_>,
event: &tracing::Event<'_>,
) -> std::fmt::Result {
use colored::Colorize;
let metadata = event.metadata();
if writer.has_ansi_escapes() {
write!(
&mut writer,
"{} {} {}{} {} {}:{}: ",
chrono::Utc::now()
.format("%Y-%m-%dT%H:%M:%S%.f%:z")
.to_string()
.magenta()
.underline()
.on_white(),
metadata.level().as_str().red(),
std::thread::current()
.name()
.unwrap_or("unnamed-thread")
.blue(),
format!("({:?})", std::thread::current().id()).blue(),
metadata.target().green(),
metadata.file().unwrap_or("unknown-file").red(),
format!("{}", metadata.line().unwrap_or(0)).red(),
)?;
} else {
write!(
&mut writer,
"{} {} {}{:?} {} {}:{}: ",
chrono::Utc::now().format("%Y-%m-%dT%H:%M:%S%.f%:z"),
metadata.level().as_str().red(),
std::thread::current().name().unwrap_or("unnamed-thread"),
std::thread::current().id(),
metadata.target(),
metadata.file().unwrap_or("unknown-file"),
metadata.line().unwrap_or(0),
)?;
}
if let Some(scope) = ctx.event_scope() {
for span in scope.from_root() {
if writer.has_ansi_escapes() {
write!(writer, "{}", span.name().purple())?;
} else {
write!(writer, "{}", span.name())?;
}
let ext = span.extensions();
let fields = &ext.get::<FormattedFields<N>>().unwrap();
if !fields.is_empty() {
if writer.has_ansi_escapes() {
write!(writer, "{{{}}}", fields.cyan())?;
} else {
write!(writer, "{{{}}}", fields)?;
}
}
write!(writer, ": ")?;
}
}
if writer.has_ansi_escapes() {
write!(writer, "{}: ", metadata.name().yellow().bold().underline())?;
} else {
write!(writer, "{}: ", metadata.name())?;
}
ctx.field_format().format_fields(writer.by_ref(), event)?;
writeln!(writer)
}
}
pub fn initialize_tracing() {
let rust_log = std::env::var("RUST_LOG").unwrap_or_else(|err| {
match err {
std::env::VarError::NotPresent => {
"error".to_owned()
}
std::env::VarError::NotUnicode(v) => {
eprintln!(
"RUST_LOG is not unicode, defaulting to 'error' directive: {:?}",
v
);
"error".to_owned()
}
}
});
let filter = EnvFilter::try_new(&rust_log).unwrap_or_else(|err| {
// Configuration error.
eprintln!("Failed to parse RUST_LOG: {}, err: {:?}", rust_log, err);
"error".to_owned().parse().unwrap()
});
initialize_tracing_with_filter(filter)
}
/// Initialize tracing using the above custom formatter, using the tracing directive.
/// something like "{level},{abc}={level},{xyz}={level}" where {level} is one of "tracing,debug,info,warn,error"
pub fn initialize_tracing_with_filter(filter: EnvFilter) {
use tracing::subscriber::set_global_default;
use tracing_subscriber::fmt::format::FmtSpan;
use tracing_subscriber::prelude::*;
use tracing_subscriber::{Layer, fmt, registry};
set_global_default(
registry().with(
fmt::layer()
.with_writer(std::io::stderr)
.with_span_events(FmtSpan::NEW | FmtSpan::CLOSE)
.event_format(Formatter)
.with_filter(filter.clone()),
),
)
.unwrap();
#[expect(
non_snake_case,
reason = "this variable represents an env var which is in all caps"
)]
let RUST_LOG = std::env::var("RUST_LOG");
tracing::trace!(name: "Tracing Initialized", ?RUST_LOG, ?filter);
}
/// Used to add a sidecar to generated code.
pub trait Sidecar {
/// Generates code to create a sidecar.
///
/// The generated code should be an expression which evaluates as a `Future`.
fn to_expr(
&self,
flow_name: &str,
location_key: LocationKey,
location_type: LocationType,
location_name: &str,
dfir_ident: &syn::Ident,
) -> syn::Expr;
}