use std::ops::BitAnd;
use std::{
fmt::{Display, Formatter},
mem::forget,
sync::OnceLock,
};
use tracing::{
instrument::WithSubscriber, level_filters::LevelFilter, subscriber::Interest, Metadata,
};
use tracing_appender::{non_blocking::NonBlocking, rolling::Rotation};
use tracing_subscriber::{
filter::Filtered,
fmt::{
self,
format::{DefaultFields, Format},
Layer as FmtLayer,
},
layer::{Context, Filter, Layered, SubscriberExt},
reload::{Handle, Layer as ReloadLayer},
util::SubscriberInitExt,
Layer, Registry,
};
use cuprate_helper::fs::logs_path;
use crate::config::Config;
static FILE_WRITER_FILTER_HANDLE: OnceLock<Handle<CupratedTracingFilter, Registry>> =
OnceLock::new();
#[expect(clippy::type_complexity)] static STDOUT_FILTER_HANDLE: OnceLock<
Handle<
CupratedTracingFilter,
Layered<
Filtered<
FmtLayer<Registry, DefaultFields, Format, NonBlocking>,
ReloadLayer<CupratedTracingFilter, Registry>,
Registry,
>,
Registry,
Registry,
>,
>,
> = OnceLock::new();
#[derive(Debug)]
pub struct CupratedTracingFilter {
pub level: LevelFilter,
}
impl Display for CupratedTracingFilter {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Filter")
.field("minimum_level", &self.level.to_string())
.finish()
}
}
impl<S> Filter<S> for CupratedTracingFilter {
fn enabled(&self, meta: &Metadata<'_>, cx: &Context<'_, S>) -> bool {
Filter::<S>::enabled(&self.level, meta, cx)
}
fn callsite_enabled(&self, meta: &'static Metadata<'static>) -> Interest {
Filter::<S>::callsite_enabled(&self.level, meta)
}
fn max_level_hint(&self) -> Option<LevelFilter> {
Some(self.level)
}
}
pub fn init_logging(config: &Config) {
let (stdout_filter, stdout_handle) = ReloadLayer::new(CupratedTracingFilter {
level: config.tracing.stdout.level,
});
STDOUT_FILTER_HANDLE.set(stdout_handle).unwrap();
let stdout_layer = FmtLayer::default()
.with_target(false)
.with_filter(stdout_filter);
let appender_config = &config.tracing.file;
let (appender, guard) = tracing_appender::non_blocking(
tracing_appender::rolling::Builder::new()
.rotation(Rotation::DAILY)
.max_log_files(appender_config.max_log_files)
.build(logs_path(&config.fs.data_directory, config.network()))
.unwrap(),
);
forget(guard);
let (appender_filter, appender_handle) = ReloadLayer::new(CupratedTracingFilter {
level: appender_config.level,
});
FILE_WRITER_FILTER_HANDLE.set(appender_handle).unwrap();
let appender_layer = fmt::layer()
.with_target(false)
.with_ansi(false)
.with_writer(appender)
.with_filter(appender_filter);
tracing_subscriber::registry()
.with(appender_layer)
.with(stdout_layer)
.init();
}
pub fn modify_stdout_output(f: impl FnOnce(&mut CupratedTracingFilter)) {
STDOUT_FILTER_HANDLE.get().unwrap().modify(f).unwrap();
}
pub fn modify_file_output(f: impl FnOnce(&mut CupratedTracingFilter)) {
FILE_WRITER_FILTER_HANDLE.get().unwrap().modify(f).unwrap();
}