use std::io;
use std::fmt::Write;
use tracing::{Event, Subscriber};
use tracing::field::{Field, Visit};
use tracing_appender::non_blocking::WorkerGuard;
use tracing_subscriber::layer::Context;
use tracing_subscriber::{fmt, layer::SubscriberExt, EnvFilter, Layer, Registry};
pub struct StringVisitor<'a> {
pub string: &'a mut String,
}
impl<'a> Visit for StringVisitor<'a> {
fn record_debug(&mut self, _field: &Field, value: &dyn std::fmt::Debug) {
let _ = write!(self.string, "{:?}", value);
}
}
pub struct CustomLayer {
pub callback: fn(&Event<'_>),
}
impl CustomLayer {
pub fn new(f: fn(_event: &Event<'_>)) -> Self {
Self { callback: f }
}
}
impl Default for CustomLayer {
fn default() -> Self {
CustomLayer::new(|_| {})
}
}
impl<S> Layer<S> for CustomLayer
where
S: Subscriber,
{
fn on_event(&self, _event: &Event<'_>, _ctx: Context<'_, S>) {
(self.callback)(_event);
}
}
pub fn init_tracing(
max_log_files: Option<u32>,
custom_hook: Option<fn(&Event<'_>)>,
) -> (WorkerGuard, WorkerGuard) {
let env_filter = EnvFilter::try_from_default_env()
.or_else(|_| EnvFilter::try_new("info"))
.unwrap();
let (console_writer, console_guard) = tracing_appender::non_blocking(io::stdout());
let file_appender = tracing_appender::rolling::RollingFileAppender::builder()
.rotation(tracing_appender::rolling::Rotation::DAILY) .filename_prefix("app") .filename_suffix("log") .max_log_files(max_log_files.unwrap_or(2) as usize) .build("logs") .expect("initializing rolling file appender failed");
let (file_writer, file_guard) = tracing_appender::non_blocking(file_appender);
let console_layer = fmt::layer()
.with_timer(fmt::time::ChronoLocal::rfc_3339())
.with_target(false)
.with_writer(console_writer)
.with_ansi(atty::is(atty::Stream::Stdout)) .with_file(true) .with_line_number(true) .with_thread_names(false)
.compact();
let file_layer = fmt::layer()
.with_timer(fmt::time::ChronoLocal::rfc_3339())
.with_target(false)
.with_writer(file_writer)
.with_ansi(false)
.with_file(true)
.with_line_number(true)
.with_thread_names(false)
.compact();
let custom_layer = if let Some(fn_hook) = custom_hook {
CustomLayer::new(fn_hook)
} else {
CustomLayer::default()
};
let subscriber = Registry::default()
.with(env_filter)
.with(console_layer)
.with(file_layer)
.with(custom_layer);
tracing::subscriber::set_global_default(subscriber)
.expect("Failed to set global tracing subscriber");
(console_guard, file_guard)
}