fix: keep file logger disabled by default (#2189)

This commit is contained in:
KKRainbow
2026-04-30 21:42:30 +08:00
committed by GitHub
parent 81d169abfc
commit aae30894dd
+193 -11
View File
@@ -58,6 +58,21 @@ fn parse_env_filter(default_level: Option<LevelFilter>) -> Result<EnvFilter, any
.with_context(|| "failed to create env filter") .with_context(|| "failed to create env filter")
} }
fn parse_static_filter(level: LevelFilter) -> Result<EnvFilter, anyhow::Error> {
EnvFilter::builder()
.with_default_directive(level.into())
.parse("")
.with_context(|| "failed to create static filter")
}
fn parse_file_filter(level: LevelFilter) -> Result<EnvFilter, anyhow::Error> {
if matches!(level, LevelFilter::OFF) {
parse_static_filter(level)
} else {
parse_env_filter(Some(level))
}
}
fn is_log(meta: &Metadata) -> bool { fn is_log(meta: &Metadata) -> bool {
meta.target() == LOG_TARGET || meta.target().starts_with(&format!("{LOG_TARGET}::")) meta.target() == LOG_TARGET || meta.target().starts_with(&format!("{LOG_TARGET}::"))
} }
@@ -165,14 +180,17 @@ fn file_layers(
) -> anyhow::Result<(Vec<BoxLayer>, Option<NewFilterSender>)> { ) -> anyhow::Result<(Vec<BoxLayer>, Option<NewFilterSender>)> {
let mut layers = Vec::new(); let mut layers = Vec::new();
let level = config.level.map(|s| s.parse().unwrap()); let level = config
.level
.map(|s| s.parse().unwrap())
.unwrap_or(LevelFilter::OFF);
if matches!(level, Some(LevelFilter::OFF)) && !reload { if matches!(level, LevelFilter::OFF) && !reload {
return Ok((layers, None)); return Ok((layers, None));
} }
let (file_filter, file_filter_reloader) = let (file_filter, file_filter_reloader) =
tracing_subscriber::reload::Layer::<_, Registry>::new(parse_env_filter(level)?); tracing_subscriber::reload::Layer::<_, Registry>::new(parse_file_filter(level)?);
let layer = |wrapper| { let layer = |wrapper| {
layer() layer()
@@ -218,9 +236,7 @@ fn file_layers(
// 初始化全局状态 // 初始化全局状态
let _ = LOGGER_LEVEL_SENDER.set(std::sync::Mutex::new(tx.clone())); let _ = LOGGER_LEVEL_SENDER.set(std::sync::Mutex::new(tx.clone()));
if let Some(level) = level { let _ = CURRENT_LOG_LEVEL.set(std::sync::Mutex::new(level.to_string()));
let _ = CURRENT_LOG_LEVEL.set(std::sync::Mutex::new(level.to_string()));
}
std::thread::spawn(move || { std::thread::spawn(move || {
while let Ok(lf) = rx.recv() { while let Ok(lf) = rx.recv() {
@@ -232,11 +248,7 @@ fn file_layers(
} }
}; };
let mut new_filter = match EnvFilter::builder() let mut new_filter = match parse_file_filter(parsed_level) {
.with_default_directive(parsed_level.into())
.from_env()
.with_context(|| "failed to create file filter")
{
Ok(filter) => Some(filter), Ok(filter) => Some(filter),
Err(e) => { Err(e) => {
error!("Failed to build new log filter for {:?}: {:?}", lf, e); error!("Failed to build new log filter for {:?}: {:?}", lf, e);
@@ -268,6 +280,36 @@ mod tests {
use super::*; use super::*;
use crate::common::config::FileLoggerConfig; use crate::common::config::FileLoggerConfig;
const RUST_LOG: &str = "RUST_LOG";
struct EnvVarGuard {
key: &'static str,
previous: Option<std::ffi::OsString>,
}
impl EnvVarGuard {
fn set(key: &'static str, value: &str) -> Self {
let previous = std::env::var_os(key);
unsafe { std::env::set_var(key, value) };
Self { key, previous }
}
fn unset(key: &'static str) -> Self {
let previous = std::env::var_os(key);
unsafe { std::env::remove_var(key) };
Self { key, previous }
}
}
impl Drop for EnvVarGuard {
fn drop(&mut self) {
match &self.previous {
Some(value) => unsafe { std::env::set_var(self.key, value) },
None => unsafe { std::env::remove_var(self.key) },
}
}
}
#[ctor::ctor] #[ctor::ctor]
fn init() { fn init() {
let _ = Registry::default() let _ = Registry::default()
@@ -276,7 +318,147 @@ mod tests {
} }
#[test] #[test]
fn default_file_logger_level_is_off_without_reload() {
let (layers, sender) = file_layers(FileLoggerConfig::default(), false).unwrap();
assert!(layers.is_empty());
assert!(sender.is_none());
}
#[test]
#[serial_test::serial]
fn default_file_logger_level_filters_info_with_reload() {
let _guard = EnvVarGuard::set(RUST_LOG, "info");
let temp_dir = tempfile::tempdir().unwrap();
let log_file_name = "default-off-test.log".to_string();
let log_path = temp_dir.path().join(&log_file_name);
let cfg = FileLoggerConfig {
file: Some(log_file_name),
dir: Some(temp_dir.path().to_string_lossy().to_string()),
..Default::default()
};
let (layers, _sender) = file_layers(cfg, true).unwrap();
let marker = "default-file-logger-off-marker";
let subscriber = Registry::default().with(layers);
tracing::subscriber::with_default(subscriber, || {
tracing::info!(target: LOG_TARGET, "{}", marker);
std::thread::sleep(std::time::Duration::from_millis(300));
});
let content = std::fs::read_to_string(&log_path).unwrap_or_default();
assert!(
!content.contains(marker),
"default file logger level should filter info logs"
);
}
#[test]
#[serial_test::serial]
fn file_logger_level_uses_env_filter_when_enabled() {
let _guard = EnvVarGuard::set(RUST_LOG, "debug");
let temp_dir = tempfile::tempdir().unwrap();
let log_file_name = "env-filter-test.log".to_string();
let log_path = temp_dir.path().join(&log_file_name);
let cfg = FileLoggerConfig {
level: Some(LevelFilter::INFO.to_string()),
file: Some(log_file_name),
dir: Some(temp_dir.path().to_string_lossy().to_string()),
..Default::default()
};
let (layers, _sender) = file_layers(cfg, true).unwrap();
let marker = "file-logger-env-filter-marker";
let subscriber = Registry::default().with(layers);
tracing::subscriber::with_default(subscriber, || {
tracing::debug!(target: LOG_TARGET, "{}", marker);
std::thread::sleep(std::time::Duration::from_millis(300));
});
let content = std::fs::read_to_string(&log_path).unwrap_or_default();
assert!(
content.contains(marker),
"enabled file logger should use RUST_LOG directives"
);
}
#[test]
#[serial_test::serial]
fn file_logger_reload_uses_env_filter_when_enabled() {
let _guard = EnvVarGuard::set(RUST_LOG, "debug");
let temp_dir = tempfile::tempdir().unwrap();
let log_file_name = "reload-env-filter-test.log".to_string();
let log_path = temp_dir.path().join(&log_file_name);
let cfg = FileLoggerConfig {
file: Some(log_file_name),
dir: Some(temp_dir.path().to_string_lossy().to_string()),
..Default::default()
};
let (layers, sender) = file_layers(cfg, true).unwrap();
let sender = sender.expect("reload=true should return a sender");
let marker = "file-logger-reload-env-filter-marker";
let subscriber = Registry::default().with(layers);
tracing::subscriber::with_default(subscriber, || {
sender.send(LevelFilter::INFO.to_string()).unwrap();
std::thread::sleep(std::time::Duration::from_millis(300));
tracing::debug!(target: LOG_TARGET, "{}", marker);
std::thread::sleep(std::time::Duration::from_millis(300));
});
let content = std::fs::read_to_string(&log_path).unwrap_or_default();
assert!(
content.contains(marker),
"file logger enabled by reload should use RUST_LOG directives"
);
}
#[test]
#[serial_test::serial]
fn file_logger_reload_off_ignores_env_filter() {
let _guard = EnvVarGuard::set(RUST_LOG, "info");
let temp_dir = tempfile::tempdir().unwrap();
let log_file_name = "reload-off-test.log".to_string();
let log_path = temp_dir.path().join(&log_file_name);
let cfg = FileLoggerConfig {
level: Some(LevelFilter::INFO.to_string()),
file: Some(log_file_name),
dir: Some(temp_dir.path().to_string_lossy().to_string()),
..Default::default()
};
let (layers, sender) = file_layers(cfg, true).unwrap();
let sender = sender.expect("reload=true should return a sender");
let marker = "file-logger-reload-off-marker";
let subscriber = Registry::default().with(layers);
tracing::subscriber::with_default(subscriber, || {
sender.send(LevelFilter::OFF.to_string()).unwrap();
std::thread::sleep(std::time::Duration::from_millis(300));
tracing::info!(target: LOG_TARGET, "{}", marker);
std::thread::sleep(std::time::Duration::from_millis(300));
});
let content = std::fs::read_to_string(&log_path).unwrap_or_default();
assert!(
!content.contains(marker),
"disabled file logger should ignore RUST_LOG directives"
);
}
#[test]
#[serial_test::serial]
fn test_logger_reload() { fn test_logger_reload() {
let _guard = EnvVarGuard::unset(RUST_LOG);
let temp_dir = tempfile::tempdir().unwrap(); let temp_dir = tempfile::tempdir().unwrap();
let log_file_name = "reload-test.log".to_string(); let log_file_name = "reload-test.log".to_string();
let log_path = temp_dir.path().join(&log_file_name); let log_path = temp_dir.path().join(&log_file_name);