From d1012b8cf5cab9a8d44c8883c4039c31f9cb0fa1 Mon Sep 17 00:00:00 2001 From: lym953 <10097700+lym953@users.noreply.github.com> Date: Wed, 7 Oct 2026 15:29:13 -0400 Subject: [PATCH 1/2] feat(config): accept filter directives in DD_LOG_LEVEL A value such as `info,cold_start_duration=debug` is a RUST_LOG-style directive list, which the Lambda extension and datadog-serverless-compat pass to their log filter. Take the level from its entry with no target, or info if there is none, and do not log "Invalid log level" for it. Co-Authored-By: Claude Opus 5.5 --- .../src/deserializers/log_level.rs | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/crates/datadog-agent-config/src/deserializers/log_level.rs b/crates/datadog-agent-config/src/deserializers/log_level.rs index 7443f3ca..28963eb4 100644 --- a/crates/datadog-agent-config/src/deserializers/log_level.rs +++ b/crates/datadog-agent-config/src/deserializers/log_level.rs @@ -72,6 +72,12 @@ impl<'de> Deserialize<'de> for LogLevel { if let Value::String(s) = value { match LogLevel::from_str(&s) { Ok(level) => Ok(level), + // A `RUST_LOG`-style directive list, such as `info,cold_start_duration=debug`. + // Its level is the last entry with no target, or info if it has none. + Err(_) if s.contains(['=', ',']) => Ok(s + .rsplit(',') + .find_map(|entry| LogLevel::from_str(entry.trim()).ok()) + .unwrap_or(LogLevel::Info)), Err(e) => { error!("{}", e); Ok(LogLevel::Warn) @@ -83,3 +89,34 @@ impl<'de> Deserialize<'de> for LogLevel { } } } + +#[cfg(test)] +mod tests { + use super::LogLevel; + use serde_json::json; + + fn parse(value: &str) -> LogLevel { + serde_json::from_value(json!(value)).expect("log level always deserializes") + } + + #[test] + fn parses_plain_level() { + assert_eq!(parse("DEBUG"), LogLevel::Debug); + } + + #[test] + fn takes_level_from_directives() { + assert_eq!(parse("debug,cold_start_duration=off"), LogLevel::Debug); + assert_eq!(parse("cold_start_duration=debug, warn"), LogLevel::Warn); + } + + #[test] + fn defaults_directives_without_level_to_info() { + assert_eq!(parse("cold_start_duration=debug"), LogLevel::Info); + } + + #[test] + fn falls_back_to_warn_for_invalid_level() { + assert_eq!(parse("verbose"), LogLevel::Warn); + } +} From 254e9bd7a259eef08067f1ea565199fadb78b325 Mon Sep 17 00:00:00 2001 From: lym953 <10097700+lym953@users.noreply.github.com> Date: Wed, 7 Oct 2026 15:52:27 -0400 Subject: [PATCH 2/2] fix(config): fall back to warn for directives without a level Matches the fallback for an invalid value, so the crate uses one default. Co-Authored-By: Claude Opus 5.5 --- .../datadog-agent-config/src/deserializers/log_level.rs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/crates/datadog-agent-config/src/deserializers/log_level.rs b/crates/datadog-agent-config/src/deserializers/log_level.rs index 28963eb4..67a2c03d 100644 --- a/crates/datadog-agent-config/src/deserializers/log_level.rs +++ b/crates/datadog-agent-config/src/deserializers/log_level.rs @@ -73,11 +73,12 @@ impl<'de> Deserialize<'de> for LogLevel { match LogLevel::from_str(&s) { Ok(level) => Ok(level), // A `RUST_LOG`-style directive list, such as `info,cold_start_duration=debug`. - // Its level is the last entry with no target, or info if it has none. + // Its level is the last entry with no target, or warn if it has none, the same as + // for an invalid value. Err(_) if s.contains(['=', ',']) => Ok(s .rsplit(',') .find_map(|entry| LogLevel::from_str(entry.trim()).ok()) - .unwrap_or(LogLevel::Info)), + .unwrap_or(LogLevel::Warn)), Err(e) => { error!("{}", e); Ok(LogLevel::Warn) @@ -111,8 +112,8 @@ mod tests { } #[test] - fn defaults_directives_without_level_to_info() { - assert_eq!(parse("cold_start_duration=debug"), LogLevel::Info); + fn defaults_directives_without_level_to_warn() { + assert_eq!(parse("cold_start_duration=debug"), LogLevel::Warn); } #[test]