1use serde::{Serialize, Deserialize};
2use std::error::Error;
3use log::LevelFilter;
4use log4rs::append::console::ConsoleAppender;
5use log4rs::append::file::FileAppender;
6use log4rs::encode::pattern::PatternEncoder;
7use log4rs::config::Config as LogConfig;
8use log4rs::config::{Appender, Logger, Root};
9use std::env;
11
12use crate::data::get_log_filename;
13use crate::res::ERes;
14
15fn default_level() -> String {
16 "info".into()
17}
18
19#[derive(Serialize, Deserialize, Debug, Clone)]
21pub struct Logging {
22 pub file_name: String,
23 #[serde(default="default_level")]
24 pub level: String,
25 pub file_logging_disabled: Option<bool>,
26}
27impl Logging {
28 pub fn init(&self) -> ERes<()> {
30 create_log_config_and_init(self)?;
31 Ok(())
32 }
33}
34impl Default for Logging {
35 fn default() -> Self {
36 Logging {
37 file_name: get_log_filename().into(),
38 level: default_level(),
39 file_logging_disabled: None,
40 }
41 }
42}
43
44fn level_filter_from_string(es:&str) -> Result<LevelFilter, Box<dyn Error>> {
45 let ll = match es {
46 "debug" => LevelFilter::Debug,
47 "info" => LevelFilter::Info,
48 "warn" => LevelFilter::Warn,
49 "error" => LevelFilter::Error,
50 "trace" => LevelFilter::Trace,
51 _ => return Err(Box::from(format!("Invalid log level '{}'", es)))
52 };
53 Ok(ll)
54}
55
56pub fn create_log_config(lg:&Logging) -> ERes<LogConfig> {
58 const PATTERN:&str = "[{d(%Y-%m-%d %H:%M:%S)} {l}] {m}{n}";
59
60 let no_file_logging = if let Some(fld) = &lg.file_logging_disabled {
61 *fld
62 } else {
63 false
64 };
65
66 let enable_file_logging = ! no_file_logging;
67
68 let level_filter = match env::var("RUST_LOG") {
69 Ok(rl) => level_filter_from_string(&rl)?,
70 Err(_) => level_filter_from_string(lg.level.as_str())?
71 };
72
73 let config = if enable_file_logging {
74 let stdout = ConsoleAppender::builder()
75 .encoder(Box::new(PatternEncoder::new(PATTERN)))
76 .build();
77
78 let requests = FileAppender::builder()
79 .encoder(Box::new(PatternEncoder::new(PATTERN)))
80 .build(&lg.file_name)?;
81
82 let cfg = LogConfig::builder()
83 .appender(Appender::builder()
84 .build("stdout", Box::new(stdout)))
85 .appender(Appender::builder()
86 .build("requests", Box::new(requests)))
87 .logger(Logger::builder()
88 .build("app::backend::db", level_filter))
89 .logger(Logger::builder()
90 .appender("requests")
91 .additive(false)
92 .build("app::requests", level_filter))
93 .build(Root::builder()
94 .appender("stdout")
95 .appender("requests")
96 .build(level_filter))?;
97 cfg
98 } else {
99 let stdout = ConsoleAppender::builder()
100 .encoder(Box::new(PatternEncoder::new(PATTERN)))
101 .build();
102 let cfg = LogConfig::builder()
103 .appender(Appender::builder()
104 .build("stdout", Box::new(stdout)))
105 .logger(Logger::builder()
106 .build("app::backend::db", level_filter))
107 .build(Root::builder()
108 .appender("stdout")
109 .build(level_filter))?;
110 cfg
111 };
112
113 Ok(config)
114}
115
116fn create_log_config_and_init(lg:&Logging) -> ERes<()> {
118 let lcfg = create_log_config(lg)?;
119 match log4rs::init_config(lcfg) {
120 Ok(_)=> Ok(()),
121 Err(e) => Err(Box::new(e)),
122 }
123}