Feature: Make Config Serializable
Hi... It would be awfully convenient to be able to serialize the config to file.
Rationale: In most cases, the default logging is fine, which is set by code. Sometimes more is needed, but instead of sending the customer a 'log4rs.yml' file. I would like to be able to do my_executable --log_path './logs' --init_log Then have the customer alter the default...
It should be fairly straight forward, just a matching 'save_config' to the 'init_file'
@JRAndreassen I'm not sure I really understand the question, I'm going to send you an email.
Hi Estk, Thanks for responding...
I try to keep the number of files I send to the users as small as possible. Optimally jsut the exe. Duing the init process, I call the 'init_logging_with_path' which looks for the config file or the default log4rs config is created in code (see below).
In order to be able alter the config it would be nice to be able to serialize the default configuration to a file. Which the user could then modify to turn on logging as needed.
I understand this might not be as easy as it sounds, but it would be awfully convinient
pub const LOG_PATTERN_STR_DEBUG : &'static str = "{d(%Y%m%d %T.%f %Z)(utc)}|{({l}):5.5}|P:{({P}):>6}|T:{({I}):>6}|{({M}):15}| {m}{n}";
pub const LOG_PATTERN_STR_CONSOLE : &'static str = "{d(%Y%m%d %T.%f %Z)(utc)}|{({l}):5.5}|P:{({P}):>6}|T:{({I}):>6}| {m}{n}";
pub fn get_default_config_rez(consolemode: bool, path: &PathBuf, level: Option<LevelFilter>)
-> Result<(), crate::errors::SabakiBaseError>
{
let curr_exe = current_exe::get_current_exe();
let lpath = String::from(path.to_string_lossy());
let logpath: String =
if !lpath.is_empty() && !lpath.ends_with("/")
{ let mut x = lpath.to_owned();
x.push('/');
x
}
else {lpath.to_owned()};
let logpath_prefix = format!("{}{}", logpath, curr_exe);
let level = level.unwrap_or(LevelFilter::Info);
// Logging to log file.
let conf =
if consolemode {
let logfile = FileAppender::builder()
// Pattern: https://docs.rs/log4rs/*/log4rs/encode/pattern/index.html
.encoder(Box::new(PatternEncoder::new(LOG_PATTERN_STR_DEBUG)))
.build(format!("{}.log", logpath_prefix))
?;
// Build a stderr logger.
let std_err = ConsoleAppender::builder().target(Target::Stderr)
.encoder(Box::new(PatternEncoder::new(LOG_PATTERN_STR_CONSOLE)))
.build();
Config::builder()
.appender(Appender::builder().build("logfile", Box::new(logfile)))
.appender(
Appender::builder()
.filter(Box::new(ThresholdFilter::new(LevelFilter::Warn)))
.build("stderr", Box::new(std_err)),
)
.build(
Root::builder()
.appender("logfile")
.appender("stderr")
.build(level),
)?
} else {
let window_size = 20; // log0, log1, log2
let fixedrollerpath = format!("{}.log", logpath_prefix);
let fixed_window_roller =
FixedWindowRoller::builder()
.build(&format!("{}{}_info{{}}.log", logpath, curr_exe),window_size).unwrap();
let size_limit = 10240000; // 5KB as max log file size to roll
let size_trigger = SizeTrigger::new(size_limit);
let compound_policy = CompoundPolicy::new(Box::new(size_trigger),Box::new(fixed_window_roller));
Config::builder()
.appender(
Appender::builder()
.filter(Box::new(ThresholdFilter::new(LevelFilter::Info)))
.build(
"logfile",
Box::new(
RollingFileAppender::builder()
.encoder(Box::new(PatternEncoder::new(LOG_PATTERN_STR_DEBUG)))
.build(fixedrollerpath, Box::new(compound_policy))?,
),
),
)
.build(
Root::builder()
.appender("logfile")
.build(LevelFilter::Info),
)?
};
let _handle = log4rs::init_config(conf);
Ok(())
}
pub fn init_logging_with_path(consolemode: bool, path: Option<PathBuf>, _config: Option<String>, level: Option<&str>)
{
let (lpath, llevel) = get_logging_params(level, path);
let mut lpath =
if let Some(tmppath) = lpath {
tmppath
} else {
PathBuf::from(self::LOG_DEFAULT_PATH)
};
//let conf_file = config.unwrap_or(format!("{}{}", lpath, "log4rs.yaml"));
let mut conf_file: PathBuf = lpath.clone();
if consolemode {
conf_file.push("log4rs.console.yaml");
} else {
conf_file.push("log4rs.yaml");
}
let inited : bool =
if file_utils::path_exists(&conf_file.as_path()) {
match log4rs::init_file(conf_file, Default::default()) {
Ok(_) => true,
Err(_err) => false,
}
} else
{ false };
if !inited {
let mlevel =
if let Some(lvl) = llevel {
LevelFilter::from_str(&lvl).unwrap_or(LevelFilter::Info)
} else {
LevelFilter::Info
};
match get_default_config_rez(consolemode, &lpath, Some(mlevel)) {
Ok(_) => {},
Err(err) => {
if lpath.to_string_lossy() != self::LOG_DEFAULT_PATH {
lpath = PathBuf::from(self::LOG_DEFAULT_PATH);
if get_default_config_rez(consolemode, &lpath, Some(mlevel)).is_ok() {
info!("Default Logging intitialized {}, Failed to iniit specified {:?}",
current_exe::get_info_long(), err);
}
}
},
}
}
if log_enabled!(Level::Debug)
{ info!("Logging intitialized {}", current_exe::get_info_long());}
}
Thanks JR
Thanks for the explanation JR, as discussed its out of scope without a major version bump.