1use tracing_core::{Event, Subscriber};
2use tracing_log::AsLog;
3use tracing_subscriber::fmt::{FmtContext, format, FormatEvent, FormatFields, FormattedFields};
4use tracing_subscriber::layer::SubscriberExt;
5use tracing_subscriber::registry::LookupSpan;
6
7pub enum LogMode {
8 Original,
9 Simple,
10 General,
11 Full,
12 Custom,
13}
14
15pub fn init_log(log_mode: LogMode, log_level: tracing::Level) {
16 match log_mode {
17 LogMode::Original => {
18 init_log_original(log_level);
19 return;
20 }
21 LogMode::Simple => init_log_simple(log_level),
22 LogMode::General => init_log_general(log_level),
23 LogMode::Full => init_log_full(log_level),
24 LogMode::Custom => init_log_custom(log_level),
25 }
26
27 tracing_log::LogTracer::builder()
30 .with_max_level(tracing_core::LevelFilter::current().as_log())
31 .init().expect("Failed to set standard library logger");
32}
33
34fn init_log_original(log_level: tracing::Level) {
43 let _logger = tracing_subscriber::fmt()
45 .with_max_level(log_level)
46 .compact() .init();
49}
50
51fn init_log_simple(log_level: tracing::Level) {
52 let subscriber = tracing_subscriber::FmtSubscriber::builder()
53 .with_env_filter(tracing_subscriber::EnvFilter::from_default_env())
54 .with_max_level(log_level)
55 .with_writer(std::io::stdout)
56 .compact()
57 .finish();
58 tracing::subscriber::set_global_default(subscriber).expect("Could not set global default logger");
59}
60
61fn init_log_general(log_level: tracing::Level) {
73 let timer = tracing_subscriber::fmt::time::ChronoLocal::new("%Y-%m-%d %H:%M:%S%.3f %z".to_string());
75
76 let subscriber = tracing_subscriber::FmtSubscriber::builder()
77 .with_env_filter(tracing_subscriber::EnvFilter::from_default_env())
78 .with_max_level(log_level)
79 .with_writer(std::io::stdout)
80 .with_target(true)
81 .with_line_number(true)
82 .with_timer(timer)
83 .compact()
84 .finish();
85 tracing::subscriber::set_global_default(subscriber).expect("Could not set global default logger");
86}
87
88fn init_log_full(log_level: tracing::Level) {
89 let filter_layer = tracing_subscriber::EnvFilter::from_default_env().add_directive(log_level.into());
91
92 let timer = tracing_subscriber::fmt::time::ChronoLocal::new("%Y-%m-%d %H:%M:%S%.3f %z".to_string());
95
96 let fmt_layer = tracing_subscriber::fmt::layer()
98 .with_thread_names(true)
99 .with_thread_ids(true)
100 .with_timer(timer)
101 .pretty();
103
104 let collector = tracing_subscriber::registry()
106 .with(filter_layer)
107 .with(fmt_layer)
108 .with(tracing_error::ErrorLayer::default());
109
110 tracing::subscriber::set_global_default(collector).expect("Could not set global default logger");
112}
113
114fn init_log_custom(log_level: tracing::Level) {
115 let subscriber = tracing_subscriber::FmtSubscriber::builder()
116 .with_env_filter(tracing_subscriber::EnvFilter::from_default_env())
117 .with_max_level(log_level)
118 .with_writer(std::io::stdout)
119 .event_format(CustomFormatter)
127 .finish();
128 tracing::subscriber::set_global_default(subscriber).expect("Could not set global default logger");
129}
130
131struct CustomFormatter;
132
133impl<S, N> FormatEvent<S, N> for CustomFormatter
136 where
137 S: Subscriber + for<'a> LookupSpan<'a>,
138 N: for<'a> FormatFields<'a> + 'static,
139{
140 fn format_event(
141 &self,
142 ctx: &FmtContext<'_, S, N>,
143 mut writer: format::Writer<'_>,
144 event: &Event<'_>,
145 ) -> std::fmt::Result {
146 let metadata = event.metadata();
148 write!(&mut writer, "{} {}: ", metadata.level(), metadata.target())?;
149
150 let line = metadata.line().unwrap_or(0);
151 let full_path = metadata.file().unwrap_or("unknown");
152 let filename = full_path.split('/').last().unwrap_or(full_path);
153 let filename_display = if filename.len() > 20 {
154 &filename[0..20]
155 } else {
156 filename
157 };
158 write!(writer, "filename={filename_display}:{line} -> ")?;
159
160 if let Some(scope) = ctx.event_scope() {
162 for span in scope.from_root() {
163 write!(writer, "{}", span.name())?;
164
165 let ext = span.extensions();
171 let fields = &ext
172 .get::<FormattedFields<N>>()
173 .expect("will never be `None`");
174
175 if !fields.is_empty() {
177 write!(writer, "{{{}}}", fields)?;
178 }
179 write!(writer, ": ")?;
180 }
181 }
182
183 ctx.field_format().format_fields(writer.by_ref(), event)?;
185
186 writeln!(writer)
187 }
188}
189
190#[cfg(test)]
191mod tests {
192 use eyre::{Context, Report};
193
194 use crate::log::{init_log, LogMode};
195
196 fn my_err() -> Report {
197 let err = || -> eyre::Result<()> {
198 Err(eyre::eyre!("error: my error 1"))
199 }().context("my error 2").context("my error 3").unwrap_err();
200
201 err
202 }
203
204 fn display() {
205 tracing::trace!("[trace]-1 log info");
206 tracing::debug!("[debug]-2 log info");
207 tracing::info!("[info]-3 log info");
208 tracing::warn!("[warn]-4 log info");
209 tracing::error!("[error]-5 log info");
210 tracing::error!("[error]-5.1 {}", my_err());
211 tracing::error!("[error]-5.2 {:?}", my_err());
212 tracing::error!("[error]-5.3 {:#}", my_err());
213 tracing::error!("[error]-5.4 {:#?}", my_err());
214 }
215
216 #[test]
217 fn display_original() {
218 init_log(LogMode::Original, tracing::Level::TRACE);
219 display();
220 }
221
222 #[test]
223 fn display_simple() {
224 init_log(LogMode::Simple, tracing::Level::TRACE);
225 display();
226 }
227
228 #[test]
229 fn display_general() {
230 init_log(LogMode::General, tracing::Level::TRACE);
231 display();
232 }
233
234 #[test]
235 fn display_full() {
236 init_log(LogMode::Full, tracing::Level::TRACE);
237 display();
238 }
239
240 #[test]
241 fn display_custom() {
242 init_log(LogMode::Custom, tracing::Level::TRACE);
243 display();
244 }
245}