From ca66419b11cb2888b660b76f8abcc8d339f3d63f Mon Sep 17 00:00:00 2001 From: leynos Date: Mon, 1 Jun 2026 04:45:59 +0200 Subject: [PATCH] Instrument config path resolution (#292) Add structured tracing around explicit configuration path selection, environment variable lookup, diagnostic layer collection, and explicit configuration load failures. Record selector and path fields so users can diagnose precedence and load failures without changing configuration behaviour. --- src/cli/config_merge.rs | 58 ++++++++++++++++++++++++++++++----------- 1 file changed, 43 insertions(+), 15 deletions(-) diff --git a/src/cli/config_merge.rs b/src/cli/config_merge.rs index 37ed776d..fb080fd7 100644 --- a/src/cli/config_merge.rs +++ b/src/cli/config_merge.rs @@ -16,6 +16,7 @@ use std::borrow::Cow; use std::io; use std::path::{Path, PathBuf}; use std::sync::Arc; +use tracing::{debug, debug_span, trace, warn}; use crate::localization::{self, keys}; @@ -45,9 +46,11 @@ fn env_config_path(var_os: F, var_name: &str) -> Option where F: Fn(&str) -> Option, { - var_os(var_name) + let path = var_os(var_name) .filter(|value| !value.is_empty()) - .map(PathBuf::from) + .map(PathBuf::from); + trace!(var_name, found = path.is_some(), path = ?path, "read config path variable"); + path } #[expect( @@ -58,11 +61,21 @@ fn resolve_config_path(cli: &Cli, var_os: F) -> Option where F: Fn(&str) -> Option, { - cli.config - .as_ref() - .cloned() - .or_else(|| env_config_path(&var_os, CONFIG_ENV_VAR)) - .or_else(|| env_config_path(var_os, CONFIG_ENV_VAR_LEGACY)) + let (selector, resolved_path) = cli.config.as_ref().cloned().map_or_else( + || { + env_config_path(&var_os, CONFIG_ENV_VAR).map_or_else( + || { + env_config_path(var_os, CONFIG_ENV_VAR_LEGACY) + .map_or(("none", None), |path| (CONFIG_ENV_VAR_LEGACY, Some(path))) + }, + |path| (CONFIG_ENV_VAR, Some(path)), + ) + }, + |path| ("cli_flag", Some(path)), + ); + + debug!(selector, path = ?resolved_path, "resolved config path"); + resolved_path } fn load_layers_from_path(path: &Path) -> OrthoResult>> { @@ -72,14 +85,21 @@ fn load_layers_from_path(path: &Path) -> OrthoResult>> { .into_iter() .map(|(value, layer_path)| MergeLayer::file(Cow::Owned(value), Some(layer_path))) .collect()), - Ok(None) => Err(Arc::new(OrthoError::File { - path: path.to_path_buf(), - source: Box::new(io::Error::new( - io::ErrorKind::NotFound, - "explicit configuration file not found", - )), - })), - Err(err) => Err(err), + Ok(None) => { + let error = Arc::new(OrthoError::File { + path: path.to_path_buf(), + source: Box::new(io::Error::new( + io::ErrorKind::NotFound, + "explicit configuration file not found", + )), + }); + warn!(path = ?path, error = %error, "explicit config load failed"); + Err(error) + } + Err(error) => { + warn!(path = ?path, error = %error, "explicit config load failed"); + Err(error) + } } } @@ -148,10 +168,14 @@ fn diag_json_from_layer(value: &serde_json::Value) -> Option { /// load failures so startup diagnostic-mode selection reports the same /// configuration errors as the full merge path. fn collect_diag_file_layers(cli: &Cli) -> OrthoResult>> { + let _span = debug_span!("collect_diag_file_layers").entered(); + if let Some(path) = resolve_config_path(cli, |name| std::env::var_os(name)) { + debug!(path = ?path, "using explicit config path"); return load_layers_from_path(&path); } + debug!("using config discovery"); let discovery = config_discovery(cli.directory.as_deref()); let file_layers = discovery.compose_layers().value; let project_file = project_scope_file_str(cli.directory.as_deref()); @@ -160,8 +184,10 @@ fn collect_diag_file_layers(cli: &Cli) -> OrthoResult>> .is_some_and(|p| project_file.as_deref() == Some(p.as_str())) }); if first_pass_found_project { + debug!(project_file = ?project_file, "discovery included project-scope layers"); Ok(file_layers) } else { + debug!(project_file = ?project_file, "appending project-scope layers"); project_scope_layers(cli.directory.as_deref()) .map(|project_layers| file_layers.into_iter().chain(project_layers).collect()) } @@ -243,10 +269,12 @@ fn push_file_layers( cli: &Cli, ) { if let Some(path) = resolve_config_path(cli, |name| std::env::var_os(name)) { + debug!(path = ?path, "using explicit config path"); push_layers_result(composer, errors, load_layers_from_path(&path)); return; } + debug!("using config discovery"); let discovery = config_discovery(cli.directory.as_deref()); let mut file_layers = discovery.compose_layers(); errors.append(&mut file_layers.required_errors);