use crate::cli::{ColorArg, LogFormatArg, LogLevelArg};
use crate::error::{AppError, AppResult};
use std::io::IsTerminal;
use tracing_subscriber::{fmt, prelude::*, EnvFilter};
pub fn init_tracing(
cli_log_level: LogLevelArg,
cli_log_format: LogFormatArg,
cli_color: ColorArg,
quiet: bool,
) -> AppResult<()> {
let filter = if let Ok(env_level) = std::env::var("YT_LOG_LEVEL") {
EnvFilter::try_new(env_level).unwrap_or_else(|_| EnvFilter::new("error"))
} else if let Ok(rust_log) = std::env::var("RUST_LOG") {
EnvFilter::try_new(rust_log).unwrap_or_else(|_| EnvFilter::new("error"))
} else if quiet {
EnvFilter::new("error")
} else {
EnvFilter::new(cli_log_level.as_str())
};
let filter = filter
.add_directive(
"chromiumoxide=error"
.parse()
.unwrap_or_else(|_| tracing_subscriber::filter::LevelFilter::ERROR.into()),
)
.add_directive(
"chromiumoxide_fetcher=error"
.parse()
.unwrap_or_else(|_| tracing_subscriber::filter::LevelFilter::ERROR.into()),
);
let registry = tracing_subscriber::registry().with(filter);
let use_json = matches!(std::env::var("YT_LOG_FORMAT").ok().as_deref(), Some("json"))
|| matches!(cli_log_format, LogFormatArg::Json);
if use_json {
let layer = fmt::layer()
.json()
.with_writer(std::io::stderr)
.with_target(false)
.with_current_span(false)
.with_ansi(false);
registry
.with(layer)
.try_init()
.map_err(|e| AppError::Internal(format!("tracing init failed: {e}")))?;
} else {
let ansi = match cli_color {
ColorArg::Never => false,
ColorArg::Always => true,
ColorArg::Auto => std::io::stderr().is_terminal(),
};
let layer = fmt::layer()
.with_writer(std::io::stderr)
.with_target(false)
.with_ansi(ansi);
registry
.with(layer)
.try_init()
.map_err(|e| AppError::Internal(format!("tracing init failed: {e}")))?;
}
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
use tracing_subscriber::filter::LevelFilter;
#[test]
fn chromiumoxide_directive_pins_to_error() {
let base = EnvFilter::new("info");
let with_chrome = base
.add_directive("chromiumoxide=error".parse().expect("valid directive"))
.add_directive(
"chromiumoxide_fetcher=error"
.parse()
.expect("valid directive"),
);
assert_eq!(
with_chrome.max_level_hint(),
Some(LevelFilter::INFO),
"global level retained"
);
let rendered = with_chrome.to_string();
assert!(
rendered.contains("chromiumoxide=error"),
"filter string must contain chromiumoxide=error, got: {rendered}"
);
}
#[test]
fn chromiumoxide_directive_respects_explicit_override() {
let operator_wants = EnvFilter::try_new("chromiumoxide=warn").expect("valid");
let combined = EnvFilter::new("info")
.add_directive(operator_wants.to_string().parse().expect("valid directive"));
let rendered = combined.to_string();
assert!(
rendered.contains("chromiumoxide=warn"),
"explicit chromiumoxide=warn must survive merge, got: {rendered}"
);
}
}