myutil/
log.rs

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    // 设置标准库 `log` 记录器,以便 `tracing` 可以接收 `log` 事件
28    // tracing_log::LogTracer::init().expect("Failed to set standard library logger");
29    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
34/// # runtime error:
35/// ```
36/// tracing_subscriber::fmt().init();
37/// tracing_log::LogTracer::init().expect("panic message");
38/// // tracing_subscriber::fmt().init() 内部已包含 tracing_log::LogTracer::init(),无需再次启动
39/// // 二者同时使用有冲突(使用tracing::subscriber::set_global_default()则没有问题),运行时报错如下:
40/// // Message:  Unable to install global subscriber: SetLoggerError(())
41/// ```
42fn init_log_original(log_level: tracing::Level) {
43    // tracing_subscriber::fmt::init(); //default Level::INFO
44    let _logger = tracing_subscriber::fmt()
45        .with_max_level(log_level)
46        .compact() //紧凑模式
47        // .pretty() //美观模式
48        .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
61/// # tracing: local time print `<unknown time>`
62///
63/// tracing_subscriber 版本 0.3.* 中使用`time`输出自定义时间时错误打印`<unknown time>`,使用`chrono`则无此问题。
64///
65/// [subscriber: don't bail when timestamp formatting fails #1689](https://github.com/tokio-rs/tracing/pull/1689)
66///
67/// [tracing_subscriber : The log CAN NOT display the time correctly in the LINUX with tracing_subscriber::fmt().with_timer(LocalTime::rfc_3339()) #2715](https://github.com/tokio-rs/tracing/issues/2715)
68///
69/// [tracing_subscriber::fmt::time::LocalTime not working when multiple threads #2004](https://github.com/tokio-rs/tracing/issues/2004)
70///
71/// [unable to get LocalTime on OpenBSD #2764](https://github.com/tokio-rs/tracing/issues/2764)
72fn init_log_general(log_level: tracing::Level) {
73    // let timer = tracing_subscriber::fmt::time::ChronoLocal::default();
74    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    // 创建一个Tracing的事件过滤器
90    let filter_layer = tracing_subscriber::EnvFilter::from_default_env().add_directive(log_level.into());
91
92    // 创建一个自定义的时间戳格式器
93    // let timer = tracing_subscriber::fmt::time::ChronoLocal::default();
94    let timer = tracing_subscriber::fmt::time::ChronoLocal::new("%Y-%m-%d %H:%M:%S%.3f %z".to_string());
95
96    // 创建一个Tracing的格式化器,并设置时间戳格式器
97    let fmt_layer = tracing_subscriber::fmt::layer()
98        .with_thread_names(true)
99        .with_thread_ids(true)
100        .with_timer(timer)
101        // .without_time() //不显示时间
102        .pretty();
103
104    // 创建一个Tracing订阅器,并将格式化器和事件过滤器添加到其中
105    let collector = tracing_subscriber::registry()
106        .with(filter_layer)
107        .with(fmt_layer)
108        .with(tracing_error::ErrorLayer::default());
109
110    // 使用Tracing订阅器
111    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        // .with_target(true)
120        // .with_file(true)
121        // .with_line_number(true)
122        // .with_thread_names(true)
123        // .with_thread_ids(true)
124        // .compact()
125        // .pretty()
126        .event_format(CustomFormatter)
127        .finish();
128    tracing::subscriber::set_global_default(subscriber).expect("Could not set global default logger");
129}
130
131struct CustomFormatter;
132
133/// 自定义 tracing 日志输出格式:
134/// https://docs.rs/tracing-subscriber/latest/tracing_subscriber/fmt/trait.FormatEvent.html
135impl<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        // Format values from the event's's metadata:
147        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        // Format all the spans in the event's span context.
161        if let Some(scope) = ctx.event_scope() {
162            for span in scope.from_root() {
163                write!(writer, "{}", span.name())?;
164
165                // `FormattedFields` is a formatted representation of the span's
166                // fields, which is stored in its extensions by the `fmt` layer's
167                // `new_span` method. The fields will have been formatted
168                // by the same field formatter that's provided to the event
169                // formatter in the `FmtContext`.
170                let ext = span.extensions();
171                let fields = &ext
172                    .get::<FormattedFields<N>>()
173                    .expect("will never be `None`");
174
175                // Skip formatting the fields if the span had no fields.
176                if !fields.is_empty() {
177                    write!(writer, "{{{}}}", fields)?;
178                }
179                write!(writer, ": ")?;
180            }
181        }
182
183        // Write fields on the event
184        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}