use leo_errors::Result;
use colored::Colorize;
use std::{fmt, sync::Once};
use tracing::{event::Event, subscriber::Subscriber};
use tracing_subscriber::{
EnvFilter,
FmtSubscriber,
fmt::{FmtContext, FormattedFields, format::*, time::*},
registry::LookupSpan,
};
static START: Once = Once::new();
#[derive(Debug, Clone)]
pub struct Format<F = Full, T = SystemTime> {
format: F,
#[allow(dead_code)] pub timer: T,
pub ansi: bool,
pub display_target: bool,
pub display_level: bool,
pub display_thread_id: bool,
pub display_thread_name: bool,
}
impl<F, T> Format<F, T> {
pub fn with_timer<T2>(self, timer: T2) -> Format<F, T2> {
Format {
format: self.format,
timer,
ansi: self.ansi,
display_target: self.display_target,
display_level: self.display_level,
display_thread_id: self.display_thread_id,
display_thread_name: self.display_thread_name,
}
}
pub fn without_time(self) -> Format<F, ()> {
Format {
format: self.format,
timer: (),
ansi: self.ansi,
display_target: self.display_target,
display_level: self.display_level,
display_thread_id: self.display_thread_id,
display_thread_name: self.display_thread_name,
}
}
pub fn with_ansi(self, ansi: bool) -> Format<F, T> {
Format { ansi, ..self }
}
pub fn with_target(self, display_target: bool) -> Format<F, T> {
Format { display_target, ..self }
}
pub fn with_level(self, display_level: bool) -> Format<F, T> {
Format { display_level, ..self }
}
pub fn with_thread_ids(self, display_thread_id: bool) -> Format<F, T> {
Format { display_thread_id, ..self }
}
pub fn with_thread_names(self, display_thread_name: bool) -> Format<F, T> {
Format { display_thread_name, ..self }
}
}
impl Default for Format<Full, SystemTime> {
fn default() -> Self {
Format {
format: Full,
timer: SystemTime,
ansi: true,
display_target: true,
display_level: true,
display_thread_id: false,
display_thread_name: false,
}
}
}
impl<S, N, T> FormatEvent<S, N> for Format<Full, T>
where
S: Subscriber + for<'a> LookupSpan<'a>,
N: for<'a> FormatFields<'a> + 'static,
T: FormatTime,
{
fn format_event(&self, context: &FmtContext<'_, S, N>, mut writer: Writer, event: &Event<'_>) -> fmt::Result {
let meta = event.metadata();
if self.display_level {
fn colored_string(level: &tracing::Level, message: &str) -> colored::ColoredString {
match *level {
tracing::Level::ERROR => message.bold().red(),
tracing::Level::WARN => message.bold().yellow(),
tracing::Level::INFO => message.bold().cyan(),
tracing::Level::DEBUG => message.bold().magenta(),
tracing::Level::TRACE => message.bold(),
}
}
let mut message = "".to_string();
match context.lookup_current() {
Some(span_ref) => {
let scope = span_ref.scope();
for span in scope {
message += span.metadata().name();
let ext = span.extensions();
let fields = &ext
.get::<FormattedFields<N>>()
.expect("Unable to find FormattedFields in extensions; this is a bug");
if !fields.is_empty() {
message = format!("{message} {{{fields}}}");
}
}
}
None => return Err(std::fmt::Error),
}
write!(&mut writer, "{:>10} ", colored_string(meta.level(), &message)).expect("Error writing event");
}
context.format_fields(writer.by_ref(), event)?;
writeln!(&mut writer)
}
}
pub fn init_logger(_app_name: &'static str, verbosity: usize) -> Result<()> {
#[cfg(target_family = "windows")]
ansi_term::enable_ansi_support().map_err(|_| crate::errors::failed_to_enable_ansi_support())?;
use tracing_subscriber::fmt::writer::MakeWriterExt;
let stderr = std::io::stderr.with_max_level(tracing::Level::WARN);
let mk_writer = stderr.or_else(std::io::stdout);
let base = match verbosity {
0 => "warn",
1 => "info",
2 => "debug",
_ => "trace",
};
let filter =
if verbosity >= 2 { EnvFilter::new(base) } else { EnvFilter::new(format!("{base},snarkvm_ledger=warn")) };
let subscriber = FmtSubscriber::builder()
.with_env_filter(filter)
.with_writer(mk_writer)
.without_time()
.with_target(false)
.event_format(Format::default())
.finish();
START.call_once(|| {
tracing::subscriber::set_global_default(subscriber).expect("setting default subscriber failed");
});
Ok(())
}