use crate::{Env, config};
use anyhow::Context;
use std::sync::OnceLock;
use tracing_subscriber::{EnvFilter, Layer, filter::DynFilterFn, layer::Filter as TraceFilter};
static ENV_FILTER: OnceLock<Option<EnvFilter>> = OnceLock::new();
static CONFIG_FILTER: OnceLock<tracing::metadata::LevelFilter> = OnceLock::new();
pub(crate) struct TraceLayer {
pub layer: Box<dyn Layer<tracing_subscriber::Registry> + Send + Sync>,
pub guard: Box<dyn Guard>,
}
pub(crate) trait Guard {}
impl Guard for tracing_appender::non_blocking::WorkerGuard {}
pub(crate) fn logfile() -> anyhow::Result<TraceLayer> {
let file = std::fs::OpenOptions::new()
.append(true)
.create(true)
.open(&crate::config::get().log_path)
.with_context(|| {
format!(
"failed to open logfile at '{}'",
crate::config::get().log_path.display()
)
})?;
let (non_blocking, guard) = tracing_appender::non_blocking(file);
let filter = DynFilterFn::new(|metadata, cx| {
let env_filter = ENV_FILTER.get_or_init(|| {
if Env::LogLevel.is_set() {
Some(EnvFilter::from_env(Env::LogLevel.to_string()))
} else {
None
}
});
let config_filter = CONFIG_FILTER.get_or_init(|| *config::get().log_level);
env_filter
.as_ref()
.map(|filter| TraceFilter::enabled(filter, metadata, cx))
.unwrap_or_else(|| TraceFilter::enabled(config_filter, metadata, cx))
});
let layer = tracing_subscriber::fmt::Layer::new()
.with_writer(non_blocking)
.with_filter(filter);
Ok(TraceLayer {
layer: Box::new(layer),
guard: Box::new(guard),
})
}
#[cfg(feature = "debug-flame")]
pub fn flame_file() -> anyhow::Result<std::fs::File> {
let flame_file = "./cnf_traceinfo.folded";
std::fs::OpenOptions::new()
.write(true)
.create(true)
.open(flame_file)
.with_context(|| format!("failed to open flamegraph dump at '{}'", flame_file))
}