use std::env;
use tracing::Level;
use tracing_subscriber::EnvFilter;
use tracing_subscriber::filter::LevelFilter;
use tracing_subscriber::layer::SubscriberExt;
use tracing_subscriber::reload;
use tracing_subscriber::util::SubscriberInitExt;
pub const ENV_VERBOSE: &str = "GIT_REMOTE_OBJECT_STORE_VERBOSE";
pub type ReloadHandle = reload::Handle<EnvFilter, tracing_subscriber::Registry>;
pub fn init() -> Result<ReloadHandle, InitError> {
let initial = build_initial_filter();
let (filter, handle) = reload::Layer::new(initial);
tracing_subscriber::registry()
.with(filter)
.with(
tracing_subscriber::fmt::layer()
.with_writer(std::io::stderr)
.with_target(false),
)
.try_init()
.map_err(|source| InitError {
source: source.into(),
})?;
Ok(handle)
}
pub fn raise_to_info(handle: &ReloadHandle) -> Result<(), InitError> {
handle
.modify(|filter| *filter = info_filter())
.map_err(|source| InitError {
source: Box::new(source),
})
}
#[derive(Debug, thiserror::Error)]
#[error("failed to initialise tracing subscriber: {source}")]
pub struct InitError {
#[source]
source: Box<dyn std::error::Error + Send + Sync + 'static>,
}
fn build_initial_filter() -> EnvFilter {
if env_verbose_at_least(2) {
info_filter()
} else {
EnvFilter::default().add_directive(LevelFilter::ERROR.into())
}
}
fn info_filter() -> EnvFilter {
EnvFilter::default().add_directive(Level::INFO.into())
}
fn env_verbose_at_least(threshold: u32) -> bool {
read_verbose_env() >= threshold
}
fn read_verbose_env() -> u32 {
env::var(ENV_VERBOSE)
.ok()
.and_then(|v| parse_verbose(&v))
.unwrap_or(0)
}
fn parse_verbose(value: &str) -> Option<u32> {
value.trim().parse::<u32>().ok()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn parse_verbose_accepts_decimal() {
assert_eq!(parse_verbose("2"), Some(2));
assert_eq!(parse_verbose(" 3 "), Some(3));
assert_eq!(parse_verbose("0"), Some(0));
}
#[test]
fn parse_verbose_rejects_non_decimal() {
assert_eq!(parse_verbose("foo"), None);
assert_eq!(parse_verbose(""), None);
assert_eq!(parse_verbose("-1"), None);
}
#[test]
fn read_verbose_env_ignores_rust_log() {
let _rust_log = crate::test_util::EnvGuard::set("RUST_LOG", "trace");
let verbose = crate::test_util::EnvGuard::unset(ENV_VERBOSE);
assert_eq!(
read_verbose_env(),
0,
"RUST_LOG must not influence verbosity; only {ENV_VERBOSE} does",
);
assert!(
!env_verbose_at_least(2),
"default floor is below info even when RUST_LOG=trace",
);
verbose.set_to("2");
assert!(env_verbose_at_least(2));
}
}