use tracing_subscriber::{
filter, fmt,
prelude::*,
reload::{self},
};
pub use tracing::Level;
fn init_log(
global: bool,
file: String,
time_format: String,
show_line_number: bool,
) -> (
Box<dyn Fn(Level) + Send + Sync + 'static>,
tracing_appender::non_blocking::WorkerGuard,
) {
let (layer, guard) = init_layer(file, time_format, show_line_number);
let (layer, _reload_layer_handle) = reload::Layer::new(layer);
let filter = filter::LevelFilter::WARN;
let (filter, reload_filter) = reload::Layer::new(filter);
let reg = tracing_subscriber::registry().with(filter).with(layer);
if global {
reg.init()
}
return (
Box::new(move |level: Level| {
let _ = reload_filter.modify(|filter| *filter = level.into());
}),
guard,
);
}
fn init_layer_writer(
file: &str,
) -> (
bool,
tracing_appender::non_blocking::NonBlocking,
tracing_appender::non_blocking::WorkerGuard,
) {
let stdout = file == "";
let (writer, guard) = if stdout {
let (writer, guard) = tracing_appender::non_blocking(std::io::stdout());
(writer, guard)
} else {
let (dir, file_name) = match file.rfind('/') {
Some(idx) => {
let (dir, file) = file.split_at(idx);
let file_name = &file[1..];
(dir, file_name)
}
None => ("", file),
};
let file_appender = tracing_appender::rolling::never(dir, file_name);
let (writer, guard) = tracing_appender::non_blocking(file_appender);
(writer, guard)
};
(stdout, writer, guard)
}
fn init_layer(
file: String,
time_format: String,
show_line_number: bool,
) -> (
fmt::Layer<
tracing_subscriber::layer::Layered<
reload::Layer<filter::LevelFilter, tracing_subscriber::Registry>,
tracing_subscriber::Registry,
>,
fmt::format::DefaultFields,
fmt::format::Format<
fmt::format::Full,
fmt::time::UtcTime<Vec<time::format_description::BorrowedFormatItem<'static>>>,
>,
tracing_appender::non_blocking::NonBlocking,
>,
tracing_appender::non_blocking::WorkerGuard,
) {
let format_str: &'static str = Box::leak(time_format.into_boxed_str());
let format = time::format_description::parse(format_str)
.expect("Invalid time format")
.to_owned();
let timer = tracing_subscriber::fmt::time::UtcTime::new(format);
let (stdout, writer, guard) = init_layer_writer(file.as_str());
let layer = fmt::Layer::default()
.with_timer(timer)
.with_file(show_line_number)
.with_line_number(show_line_number)
.with_writer(writer)
.with_ansi(stdout);
return (layer, guard);
}
extern crate time;
extern crate tracing;
extern crate tracing_subscriber;
use std::sync::OnceLock;
static LOG: OnceLock<(
Box<dyn Fn(Level) + Send + Sync + 'static>,
tracing_appender::non_blocking::WorkerGuard,
)> = OnceLock::new();
pub struct Options {
pub level: Level,
pub set_file: String,
pub time_format: String,
pub show_line_number: bool,
}
impl Default for Options {
fn default() -> Self {
Self {
level: Level::INFO,
set_file: "".to_string(),
time_format: "[year]-[month]-[day] [hour repr:24]:[minute]:[second]".to_string(),
show_line_number: false,
}
}
}
pub fn init<T: Into<Option<Options>>>(options: T) {
let mut options = options.into();
if options.is_none() {
options = Some(Options::default());
}
let options = options.unwrap();
let set_file = options.set_file;
let (reload_handle, _) = LOG.get_or_init(|| {
init_log(
true,
set_file,
options.time_format,
options.show_line_number,
)
});
let level = options.level;
reload_handle(level.into());
}