fix: make logger work while loading config file (#581)

This commit is contained in:
SteveLauC
2023-10-17 11:19:47 +08:00
committed by GitHub
parent 6b871e7949
commit a8c29fd1a2
5 changed files with 122 additions and 59 deletions

View File

@@ -17,17 +17,19 @@ use regex::Regex;
use regex_split::RegexSplit;
use serde::Deserialize;
use strum::{EnumIter, EnumString, EnumVariantNames, IntoEnumIterator};
use tracing::debug;
use which_crate::which;
use super::utils::{editor, hostname};
use crate::command::CommandExt;
use crate::sudo::SudoKind;
use crate::utils::string_prepend_str;
use super::utils::{editor, hostname};
use tracing::{debug, error};
pub static EXAMPLE_CONFIG: &str = include_str!("../config.example.toml");
/// Topgrade's default log level.
pub const DEFAULT_LOG_LEVEL: &str = "warn";
#[allow(unused_macros)]
macro_rules! str_value {
($section:ident, $value:ident) => {
@@ -607,11 +609,11 @@ impl ConfigFile {
*/
for include in dir_include {
let include_contents = fs::read_to_string(&include).map_err(|e| {
tracing::error!("Unable to read {}", include.display());
error!("Unable to read {}", include.display());
e
})?;
let include_contents_parsed = toml::from_str(include_contents.as_str()).map_err(|e| {
tracing::error!("Failed to deserialize {}", include.display());
error!("Failed to deserialize {}", include.display());
e
})?;
@@ -628,7 +630,7 @@ impl ConfigFile {
}
let mut contents_non_split = fs::read_to_string(&config_path).map_err(|e| {
tracing::error!("Unable to read {}", config_path.display());
error!("Unable to read {}", config_path.display());
e
})?;
@@ -641,7 +643,7 @@ impl ConfigFile {
for contents in contents_split {
let config_file_include_only: ConfigFileIncludeOnly = toml::from_str(contents).map_err(|e| {
tracing::error!("Failed to deserialize an include section of {}", config_path.display());
error!("Failed to deserialize an include section of {}", config_path.display());
e
})?;
@@ -654,28 +656,24 @@ impl ConfigFile {
let include_contents = match fs::read_to_string(&include_path) {
Ok(c) => c,
Err(e) => {
tracing::error!("Unable to read {}: {}", include_path.display(), e);
error!("Unable to read {}: {}", include_path.display(), e);
continue;
}
};
match toml::from_str::<Self>(&include_contents) {
Ok(include_parsed) => result.merge(include_parsed),
Err(e) => {
tracing::error!("Failed to deserialize {}: {}", include_path.display(), e);
error!("Failed to deserialize {}: {}", include_path.display(), e);
continue;
}
};
debug!("Configuration include found: {}", include_path.display());
}
} else {
debug!("No include paths found in {}", config_path.display());
}
}
match toml::from_str::<Self>(contents) {
Ok(contents) => result.merge(contents),
Err(e) => tracing::error!("Failed to deserialize {}: {}", config_path.display(), e),
Err(e) => error!("Failed to deserialize {}: {}", config_path.display(), e),
}
}
@@ -713,8 +711,6 @@ impl ConfigFile {
}
}
debug!("Loaded configuration: {:?}", result);
Ok(result)
}
@@ -832,7 +828,7 @@ pub struct CommandLineArgs {
/// Tracing filter directives.
///
/// See: https://docs.rs/tracing-subscriber/latest/tracing_subscriber/struct.EnvFilter.html
#[clap(long, default_value = "warn")]
#[clap(long, default_value = DEFAULT_LOG_LEVEL)]
pub log_filter: String,
/// Print completion script for the given shell and exit
@@ -860,6 +856,27 @@ impl CommandLineArgs {
pub fn env_variables(&self) -> &Vec<String> {
&self.env
}
/// In Topgrade, filter directives come from 3 places:
/// 1. CLI option `--log-filter`
/// 2. Config file
/// 3. `debug` if the `--verbose` option is present
///
/// Before loading the configuration file, we need our logger to work, so this
/// function will return directives coming from part 1 and 2.
///
///
/// When the configuration file is loaded, `Config::tracing_filter_directives()`
/// will return all the 3 parts.
pub fn tracing_filter_directives(&self) -> String {
let mut ret = self.log_filter.clone();
if self.verbose {
ret.push(',');
ret.push_str("debug");
}
ret
}
}
/// Represents the application configuration
@@ -867,6 +884,7 @@ impl CommandLineArgs {
/// The struct holds the loaded configuration file, as well as the arguments parsed from the command line.
/// Its provided methods decide the appropriate options based on combining the configuration file and the
/// command line arguments.
#[derive(Debug)]
pub struct Config {
opt: CommandLineArgs,
config_file: ConfigFile,
@@ -883,7 +901,7 @@ impl Config {
ConfigFile::read(opt.config.clone()).unwrap_or_else(|e| {
// Inform the user about errors when loading the configuration,
// but fallback to the default config to at least attempt to do something
tracing::error!("failed to load configuration: {}", e);
error!("failed to load configuration: {}", e);
ConfigFile::default()
})
} else {
@@ -1442,6 +1460,14 @@ impl Config {
self.opt.verbose
}
/// After loading the config file, filter directives consist of 3 parts:
///
/// 1. directives from the configuration file
/// 2. directives from the CLI options `--log-filter`
/// 3. `debug`, which would be enabled if the `--verbose` option is present
///
/// Previous directive will be overwritten if a directive with the same target
/// appear later.
pub fn tracing_filter_directives(&self) -> String {
let mut ret = String::new();
if let Some(directives) = self.config_file.misc.as_ref().and_then(|m| m.log_filters.as_ref()) {