pub const ENV_DIG_LOG: &str = "DIG_LOG";
pub const ENV_RUST_LOG: &str = "RUST_LOG";
pub const DEFAULT_DIRECTIVE: &str = "info,hyper=warn,rustls=warn,h2=warn,tower=warn";
pub fn resolve_filter(
persisted: Option<&str>,
dig_log: Option<&str>,
rust_log: Option<&str>,
) -> String {
[persisted, dig_log, rust_log]
.into_iter()
.flatten()
.map(str::trim)
.find(|value| !value.is_empty())
.unwrap_or(DEFAULT_DIRECTIVE)
.to_string()
}
fn directive_is_valid(directive: &str) -> bool {
tracing_subscriber::EnvFilter::try_new(directive).is_ok()
}
pub fn resolve_valid_filter(
persisted: Option<&str>,
dig_log: Option<&str>,
rust_log: Option<&str>,
) -> String {
[persisted, dig_log, rust_log]
.into_iter()
.flatten()
.map(str::trim)
.find(|value| !value.is_empty() && directive_is_valid(value))
.unwrap_or(DEFAULT_DIRECTIVE)
.to_string()
}
pub fn resolve_filter_from_env(persisted: Option<&str>) -> String {
let dig_log = std::env::var(ENV_DIG_LOG).ok();
let rust_log = std::env::var(ENV_RUST_LOG).ok();
resolve_valid_filter(persisted, dig_log.as_deref(), rust_log.as_deref())
}
pub const LEVEL_FILE: &str = "level";
pub fn read_persisted_level(dir: &std::path::Path) -> Option<String> {
std::fs::read_to_string(dir.join(LEVEL_FILE))
.ok()
.map(|value| value.trim().to_string())
.filter(|value| !value.is_empty())
}
pub fn write_persisted_level(dir: &std::path::Path, directive: &str) -> std::io::Result<()> {
let directive = directive.trim();
if !directive_is_valid(directive) {
return Err(std::io::Error::new(
std::io::ErrorKind::InvalidInput,
format!("invalid log filter directive: {directive:?}"),
));
}
std::fs::create_dir_all(dir)?;
std::fs::write(dir.join(LEVEL_FILE), directive)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn persisted_beats_everything() {
assert_eq!(
resolve_filter(Some("debug"), Some("trace"), Some("warn")),
"debug"
);
}
#[test]
fn dig_log_beats_rust_log() {
assert_eq!(resolve_filter(None, Some("trace"), Some("warn")), "trace");
}
#[test]
fn rust_log_used_when_only_source() {
assert_eq!(resolve_filter(None, None, Some("error")), "error");
}
#[test]
fn default_when_all_absent_or_blank() {
assert_eq!(resolve_filter(None, None, None), DEFAULT_DIRECTIVE);
assert_eq!(
resolve_filter(Some(" "), Some(""), None),
DEFAULT_DIRECTIVE
);
}
#[test]
fn garbage_persisted_falls_back_to_default() {
assert_eq!(
resolve_valid_filter(Some("target=boguslevel"), None, None),
DEFAULT_DIRECTIVE
);
}
#[test]
fn garbage_persisted_falls_through_to_valid_env() {
assert_eq!(
resolve_valid_filter(Some("info,bad=notalevel"), None, Some("debug")),
"debug"
);
}
#[test]
fn valid_persisted_still_wins() {
assert_eq!(
resolve_valid_filter(Some("debug,h2=warn"), Some("trace"), None),
"debug,h2=warn"
);
}
#[test]
fn from_env_with_garbage_persisted_is_valid() {
let resolved = resolve_filter_from_env(Some("app=verboze"));
assert!(
tracing_subscriber::EnvFilter::try_new(&resolved).is_ok(),
"resolved directive must parse: {resolved}"
);
}
#[test]
fn write_rejects_invalid_directive() {
let dir = tempfile::tempdir().unwrap();
assert!(write_persisted_level(dir.path(), "target=boguslevel").is_err());
assert_eq!(
read_persisted_level(dir.path()),
None,
"nothing should have been persisted"
);
}
#[test]
fn persisted_level_round_trips_and_blank_is_none() {
let dir = tempfile::tempdir().unwrap();
assert_eq!(read_persisted_level(dir.path()), None);
write_persisted_level(dir.path(), " debug,h2=warn ").unwrap();
assert_eq!(
read_persisted_level(dir.path()),
Some("debug,h2=warn".to_string())
);
write_persisted_level(dir.path(), " ").unwrap();
assert_eq!(
read_persisted_level(dir.path()),
None,
"blank persisted level reads as unset"
);
}
}