use std::io::{stderr, Write}; use log::{warn, LevelFilter}; use log4rs::{ append::{ console::ConsoleAppender, rolling_file::{ policy::compound::{ roll::fixed_window::FixedWindowRoller, trigger::size::SizeTrigger, CompoundPolicy, }, RollingFileAppender, }, }, config::{Appender, Config, Root}, encode::pattern::PatternEncoder, file::{Deserializers, RawConfig}, init_config, }; use serde_yaml::from_str; use crate::{misc::Vfs, Error}; pub fn init(vfs: &Vfs) { let (config, err) = match load_from_file(vfs) { Ok(config) => (config, None), Err(err) => (default_config(), Some(err)), }; init_config(config).expect("Unable to initialize logger"); if let Some(err) = err { warn!("Unable to load log config from file: {}", err); } } fn load_from_file(vfs: &Vfs) -> Result { let mut config = String::default(); vfs.join("resources/log4rs.yml")? .open_file()? .read_to_string(&mut config)?; let mut stderr = stderr(); let config = from_str::(&config)?; let deserializers = Deserializers::default(); let (appenders, errors) = config.appenders_lossy(&deserializers); for error in &errors { let _ = writeln!(stderr, "log: {}", error); } let (config, errors) = Config::builder() .appenders(appenders) .loggers(config.loggers()) .build_lossy(config.root()); for error in &errors { let _ = writeln!(stderr, "log: {}", error); } Ok(config) } #[allow(clippy::identity_op)] fn default_config() -> Config { let mut root = Root::builder(); let mut config = Config::builder(); let encoder = PatternEncoder::new("[{d} {h({l:>5})}] {m}{n}"); let stdout = ConsoleAppender::builder() .encoder(Box::new(encoder)) .build(); root = root.appender("stdout"); config = config.appender(Appender::builder().build("stdout", Box::new(stdout))); if let Ok(roller) = FixedWindowRoller::builder() .base(1) .build("space-crush-{}.log", 5) { let path = "space-crush.log"; let trigger = SizeTrigger::new(1 * 1024 * 1024); let policy = CompoundPolicy::new(Box::new(trigger), Box::new(roller)); let encoder = PatternEncoder::new( "{d} {h({l:<5})}{n} Module: {M}{n} File: {f}:{L}{n} Message: {m}{n}", ); if let Ok(file) = RollingFileAppender::builder() .append(true) .encoder(Box::new(encoder)) .build(path, Box::new(policy)) { root = root.appender("file"); config = config.appender(Appender::builder().build("file", Box::new(file))); } } config .build(root.build(LevelFilter::Info)) .expect("Unable to create default log config") }