use std::path::PathBuf;
use std::sync::OnceLock;
use tracing_appender::non_blocking::WorkerGuard;
use tracing_subscriber::prelude::*;
struct Guards {
_file: WorkerGuard,
#[cfg(feature = "profiling")]
_chrome: tracing_chrome::FlushGuard,
#[cfg(feature = "flamegraph")]
_flame: tracing_flame::FlushGuard<std::io::BufWriter<std::fs::File>>,
}
static GUARDS: OnceLock<Guards> = OnceLock::new();
pub fn log_dir() -> PathBuf {
dirs::home_dir()
.unwrap_or_else(|| PathBuf::from("/tmp"))
.join(".cache/zshrs")
}
pub fn log_path() -> PathBuf {
log_dir().join("zshrs.log")
}
pub fn init() {
GUARDS.get_or_init(|| {
let dir = log_dir();
let _ = std::fs::create_dir_all(&dir);
let pid = std::process::id();
let file_appender = tracing_appender::rolling::never(&dir, "zshrs.log");
let (non_blocking, file_guard) = tracing_appender::non_blocking(file_appender);
let env_filter = std::env::var("ZSHRS_LOG").unwrap_or_else(|_| "info".to_string());
let file_layer = tracing_subscriber::fmt::layer()
.with_writer(non_blocking)
.with_ansi(false)
.with_target(true)
.with_thread_names(true)
.compact();
#[cfg(feature = "profiling")]
let (chrome_layer, chrome_guard) = {
let trace_path = dir.join(format!("trace-{}.json", pid));
let (layer, guard) = tracing_chrome::ChromeLayerBuilder::new()
.file(trace_path)
.include_args(true)
.build();
(Some(layer), guard)
};
#[cfg(not(feature = "profiling"))]
let chrome_layer: Option<tracing_subscriber::layer::Identity> = None;
#[cfg(feature = "flamegraph")]
let (flame_layer, flame_guard) = {
let flame_path = dir.join(format!("flame-{}.folded", pid));
let file = std::fs::File::create(&flame_path)
.expect("cannot create flamegraph output file");
let writer = std::io::BufWriter::new(file);
let (layer, guard) = tracing_flame::FlameLayer::with_writer(writer).build();
(Some(layer), guard)
};
#[cfg(not(feature = "flamegraph"))]
let flame_layer: Option<tracing_subscriber::layer::Identity> = None;
#[cfg(feature = "prometheus")]
{
let builder = metrics_exporter_prometheus::PrometheusBuilder::new();
if let Err(e) = builder
.with_http_listener(([127, 0, 0, 1], 9090))
.install()
{
eprintln!("zshrs: failed to start prometheus exporter: {}", e);
}
}
let subscriber = tracing_subscriber::registry()
.with(tracing_subscriber::EnvFilter::new(&env_filter))
.with(file_layer)
.with(chrome_layer)
.with(flame_layer);
let _ = tracing::subscriber::set_global_default(subscriber);
Guards {
_file: file_guard,
#[cfg(feature = "profiling")]
_chrome: chrome_guard,
#[cfg(feature = "flamegraph")]
_flame: flame_guard,
}
});
}
pub fn profiling_enabled() -> bool {
cfg!(feature = "profiling")
}
pub fn flamegraph_enabled() -> bool {
cfg!(feature = "flamegraph")
}
pub fn prometheus_enabled() -> bool {
cfg!(feature = "prometheus")
}