use tracing_appender::non_blocking::WorkerGuard;
use tracing_appender::rolling::{RollingFileAppender, Rotation};
use tracing_subscriber::{fmt, layer::SubscriberExt};
use tracing_subscriber::fmt::time::OffsetTime;
use tracing_subscriber::util::SubscriberInitExt;
use crate::config::Format;
use crate::contracts::{Application, Service};
use crate::foundation::application::config;
pub struct Logger(WorkerGuard);
impl Service for Logger {
fn register<A: Application + ?Sized>() -> Self
where
Self: Sized,
{
let logger = config().unwrap().get::<crate::config::Logger>("logger").unwrap();
let local_offset_time = OffsetTime::local_rfc_3339().expect("could not get local offset");
let (non_blocking_appender, worker_guard) = match logger.channel.as_str() {
"file" => tracing_appender::non_blocking(file_layer(&logger)),
"stdout" => tracing_appender::non_blocking(std::io::stdout()),
_ => panic!("invalid log channel"),
};
let fmt_layer = fmt::layer()
.with_level(true)
.with_ansi(logger.display_ansi)
.with_file(logger.display_file)
.with_line_number(logger.display_line_number)
.with_thread_ids(logger.display_thread_ids)
.with_thread_names(logger.display_thread_names)
.with_target(logger.display_target)
.with_timer(local_offset_time)
.with_writer(non_blocking_appender);
let subscriber = tracing_subscriber::registry();
match logger.format {
Format::Compact => subscriber.with(fmt_layer.compact()).init(),
Format::Pretty => subscriber.with(fmt_layer.pretty()).init(),
_ => subscriber.with(fmt_layer).init(),
}
Self(worker_guard)
}
}
fn file_layer(config: &crate::config::Logger) -> RollingFileAppender {
let log_rolling_period = std::time::Duration::from_secs(3600 * 24 * 90); let log_rotation = "daily";
let rolling_period_minutes = log_rolling_period.as_secs().div_ceil(60);
let (rotation, max_log_files) = match log_rotation {
"minutely" => (Rotation::MINUTELY, rolling_period_minutes),
"hourly" => (Rotation::HOURLY, rolling_period_minutes.div_ceil(60)),
"daily" => (Rotation::DAILY, rolling_period_minutes.div_ceil(60 * 24)),
_ => (Rotation::NEVER, 1),
};
RollingFileAppender::builder()
.rotation(rotation)
.filename_prefix(&config.file_prefix)
.filename_suffix("log")
.max_log_files(max_log_files.try_into().unwrap_or(1))
.build(&config.file_path)
.expect("fail to initialize the rolling file appender")
}