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