1use leo_errors::Result;
18
19use colored::Colorize;
20use std::{fmt, sync::Once};
21use tracing::{event::Event, subscriber::Subscriber};
22use tracing_subscriber::{
23 FmtSubscriber,
24 fmt::{FmtContext, FormattedFields, format::*, time::*},
25 registry::LookupSpan,
26};
27
28static START: Once = Once::new();
29
30#[derive(Debug, Clone)]
31pub struct Format<F = Full, T = SystemTime> {
32 format: F,
33 #[allow(dead_code)] pub timer: T,
35 pub ansi: bool,
36 pub display_target: bool,
37 pub display_level: bool,
38 pub display_thread_id: bool,
39 pub display_thread_name: bool,
40}
41
42impl<F, T> Format<F, T> {
43 pub fn with_timer<T2>(self, timer: T2) -> Format<F, T2> {
55 Format {
56 format: self.format,
57 timer,
58 ansi: self.ansi,
59 display_target: self.display_target,
60 display_level: self.display_level,
61 display_thread_id: self.display_thread_id,
62 display_thread_name: self.display_thread_name,
63 }
64 }
65
66 pub fn without_time(self) -> Format<F, ()> {
68 Format {
69 format: self.format,
70 timer: (),
71 ansi: self.ansi,
72 display_target: self.display_target,
73 display_level: self.display_level,
74 display_thread_id: self.display_thread_id,
75 display_thread_name: self.display_thread_name,
76 }
77 }
78
79 pub fn with_ansi(self, ansi: bool) -> Format<F, T> {
81 Format { ansi, ..self }
82 }
83
84 pub fn with_target(self, display_target: bool) -> Format<F, T> {
86 Format { display_target, ..self }
87 }
88
89 pub fn with_level(self, display_level: bool) -> Format<F, T> {
91 Format { display_level, ..self }
92 }
93
94 pub fn with_thread_ids(self, display_thread_id: bool) -> Format<F, T> {
99 Format { display_thread_id, ..self }
100 }
101
102 pub fn with_thread_names(self, display_thread_name: bool) -> Format<F, T> {
107 Format { display_thread_name, ..self }
108 }
109}
110
111impl Default for Format<Full, SystemTime> {
112 fn default() -> Self {
113 Format {
114 format: Full,
115 timer: SystemTime,
116 ansi: true,
117 display_target: true,
118 display_level: true,
119 display_thread_id: false,
120 display_thread_name: false,
121 }
122 }
123}
124impl<S, N, T> FormatEvent<S, N> for Format<Full, T>
125where
126 S: Subscriber + for<'a> LookupSpan<'a>,
127 N: for<'a> FormatFields<'a> + 'static,
128 T: FormatTime,
129{
130 fn format_event(&self, context: &FmtContext<'_, S, N>, mut writer: Writer, event: &Event<'_>) -> fmt::Result {
131 let meta = event.metadata();
132
133 if self.display_level {
134 fn colored_string(level: &tracing::Level, message: &str) -> colored::ColoredString {
135 match *level {
136 tracing::Level::ERROR => message.bold().red(),
137 tracing::Level::WARN => message.bold().yellow(),
138 tracing::Level::INFO => message.bold().cyan(),
139 tracing::Level::DEBUG => message.bold().magenta(),
140 tracing::Level::TRACE => message.bold(),
141 }
142 }
143
144 let mut message = "".to_string();
145
146 match context.lookup_current() {
147 Some(span_ref) => {
148 let scope = span_ref.scope();
149
150 for span in scope {
151 message += span.metadata().name();
152
153 let ext = span.extensions();
154 let fields = &ext
155 .get::<FormattedFields<N>>()
156 .expect("Unable to find FormattedFields in extensions; this is a bug");
157 if !fields.is_empty() {
158 message = format!("{message} {{{fields}}}");
159 }
160 }
161 }
162 None => return Err(std::fmt::Error),
163 }
164
165 write!(&mut writer, "{:>10} ", colored_string(meta.level(), &message)).expect("Error writing event");
166 }
167
168 context.format_fields(writer.by_ref(), event)?;
169 writeln!(&mut writer)
170 }
171}
172
173pub fn init_logger(_app_name: &'static str, verbosity: usize) -> Result<()> {
175 #[cfg(target_family = "windows")]
177 ansi_term::enable_ansi_support().map_err(|_| leo_errors::CliError::failed_to_enable_ansi_support())?;
178
179 use tracing_subscriber::fmt::writer::MakeWriterExt;
180
181 let stderr = std::io::stderr.with_max_level(tracing::Level::WARN);
182 let mk_writer = stderr.or_else(std::io::stdout);
183
184 let subscriber = FmtSubscriber::builder()
185 .with_max_level(match verbosity {
188 0 => tracing::Level::WARN,
189 1 => tracing::Level::INFO,
190 2 => tracing::Level::DEBUG,
191 _ => tracing::Level::TRACE
192 })
193 .with_writer(mk_writer)
194 .without_time()
195 .with_target(false)
196 .event_format(Format::default())
197 .finish();
198
199 START.call_once(|| {
201 tracing::subscriber::set_global_default(subscriber).expect("setting default subscriber failed");
202 });
203 Ok(())
204}