1use std::env::{self, VarError};
37use std::fmt::{self, Display};
38use std::io::{self, IsTerminal};
39
40use tracing_core::{Event, Subscriber};
41use tracing_subscriber::filter::{Directive, EnvFilter, LevelFilter};
42use tracing_subscriber::fmt::FmtContext;
43use tracing_subscriber::fmt::format::{self, FormatEvent, FormatFields};
44use tracing_subscriber::layer::SubscriberExt;
45
46pub struct LoggerConfig {
49 pub filter: Result<String, VarError>,
50 pub color_logs: Result<String, VarError>,
51 pub verbose_entry_exit: Result<String, VarError>,
52 pub verbose_thread_ids: Result<String, VarError>,
53 pub backtrace: Result<String, VarError>,
54 pub wraptree: Result<String, VarError>,
55 pub lines: Result<String, VarError>,
56}
57
58impl LoggerConfig {
59 pub fn from_env(env: &str) -> Self {
60 LoggerConfig {
61 filter: env::var(env),
62 color_logs: env::var(format!("{env}_COLOR")),
63 verbose_entry_exit: env::var(format!("{env}_ENTRY_EXIT")),
64 verbose_thread_ids: env::var(format!("{env}_THREAD_IDS")),
65 backtrace: env::var(format!("{env}_BACKTRACE")),
66 wraptree: env::var(format!("{env}_WRAPTREE")),
67 lines: env::var(format!("{env}_LINES")),
68 }
69 }
70}
71
72pub fn init_logger(cfg: LoggerConfig) -> Result<(), Error> {
74 let filter = match cfg.filter {
75 Ok(env) => EnvFilter::new(env),
76 _ => EnvFilter::default().add_directive(Directive::from(LevelFilter::WARN)),
77 };
78
79 let color_logs = match cfg.color_logs {
80 Ok(value) => match value.as_ref() {
81 "always" => true,
82 "never" => false,
83 "auto" => stderr_isatty(),
84 _ => return Err(Error::InvalidColorValue(value)),
85 },
86 Err(VarError::NotPresent) => stderr_isatty(),
87 Err(VarError::NotUnicode(_value)) => return Err(Error::NonUnicodeColorValue),
88 };
89
90 let verbose_entry_exit = match cfg.verbose_entry_exit {
91 Ok(v) => &v != "0",
92 Err(_) => false,
93 };
94
95 let verbose_thread_ids = match cfg.verbose_thread_ids {
96 Ok(v) => &v == "1",
97 Err(_) => false,
98 };
99
100 let lines = match cfg.lines {
101 Ok(v) => &v == "1",
102 Err(_) => false,
103 };
104
105 let mut layer = tracing_tree::HierarchicalLayer::default()
106 .with_writer(io::stderr)
107 .with_ansi(color_logs)
108 .with_targets(true)
109 .with_verbose_exit(verbose_entry_exit)
110 .with_verbose_entry(verbose_entry_exit)
111 .with_indent_amount(2)
112 .with_indent_lines(lines)
113 .with_thread_ids(verbose_thread_ids)
114 .with_thread_names(verbose_thread_ids);
115
116 match cfg.wraptree {
117 Ok(v) => match v.parse::<usize>() {
118 Ok(v) => {
119 layer = layer.with_wraparound(v);
120 }
121 Err(_) => return Err(Error::InvalidWraptree(v)),
122 },
123 Err(_) => {} }
125
126 let subscriber = tracing_subscriber::Registry::default().with(filter).with(layer);
127 match cfg.backtrace {
128 Ok(backtrace_target) => {
129 let fmt_layer = tracing_subscriber::fmt::layer()
130 .with_writer(io::stderr)
131 .without_time()
132 .event_format(BacktraceFormatter { backtrace_target });
133 let subscriber = subscriber.with(fmt_layer);
134 tracing::subscriber::set_global_default(subscriber).unwrap();
135 }
136 Err(_) => {
137 tracing::subscriber::set_global_default(subscriber).unwrap();
138 }
139 };
140
141 Ok(())
142}
143
144struct BacktraceFormatter {
145 backtrace_target: String,
146}
147
148impl<S, N> FormatEvent<S, N> for BacktraceFormatter
149where
150 S: Subscriber + for<'a> tracing_subscriber::registry::LookupSpan<'a>,
151 N: for<'a> FormatFields<'a> + 'static,
152{
153 fn format_event(
154 &self,
155 _ctx: &FmtContext<'_, S, N>,
156 mut writer: format::Writer<'_>,
157 event: &Event<'_>,
158 ) -> fmt::Result {
159 let target = event.metadata().target();
160 if !target.contains(&self.backtrace_target) {
161 return Ok(());
162 }
163 let backtrace = std::backtrace::Backtrace::force_capture();
166 writeln!(writer, "stack backtrace: \n{backtrace:?}")
167 }
168}
169
170pub fn stdout_isatty() -> bool {
171 io::stdout().is_terminal()
172}
173
174pub fn stderr_isatty() -> bool {
175 io::stderr().is_terminal()
176}
177
178#[derive(Debug)]
179pub enum Error {
180 InvalidColorValue(String),
181 NonUnicodeColorValue,
182 InvalidWraptree(String),
183}
184
185impl std::error::Error for Error {}
186
187impl Display for Error {
188 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
189 match self {
190 Error::InvalidColorValue(value) => write!(
191 formatter,
192 "invalid log color value '{value}': expected one of always, never, or auto",
193 ),
194 Error::NonUnicodeColorValue => write!(
195 formatter,
196 "non-Unicode log color value: expected one of always, never, or auto",
197 ),
198 Error::InvalidWraptree(value) => write!(
199 formatter,
200 "invalid log WRAPTREE value '{value}': expected a non-negative integer",
201 ),
202 }
203 }
204}