1use super::LogLevel;
2use cyfs_base::{BuckyError, BuckyErrorCode, BuckyResult};
3use cyfs_util::TomlHelper;
4
5use std::{collections::{HashMap, hash_map::Entry}, str::FromStr};
6use std::path::{Path, PathBuf};
7
8const CYFS_CONSOLE_LOG_LEVEL_KEY: &str = "CYFS_CONSOLE_LOG_LEVEL";
10const CYFS_FILE_LOG_LEVEL_KEY: &str = "CYFS_FILE_LOG_LEVEL";
11
12
13pub enum LogDebugInfoFlag {
14 Args = 0x01,
15 Env = 0x02,
16}
17
18#[derive(Clone, Debug)]
19pub struct LogDebugInfoFlags(u32);
20
21impl Default for LogDebugInfoFlags {
22 fn default() -> Self {
23 Self(u32::MAX)
24 }
25}
26
27impl LogDebugInfoFlags {
28 pub fn new(flags: u32) -> Self {
29 Self(flags)
30 }
31
32 pub fn without_args(mut self) -> Self {
33 self.0 &= !(LogDebugInfoFlag::Args as u32);
34 self
35 }
36
37 pub fn is_args_present(&self) -> bool {
38 self.0 & LogDebugInfoFlag::Args as u32 == LogDebugInfoFlag::Args as u32
39 }
40
41 pub fn without_env(mut self) -> Self {
42 self.0 &= !(LogDebugInfoFlag::Env as u32);
43 self
44 }
45
46 pub fn is_env_present(&self) -> bool {
47 self.0 & LogDebugInfoFlag::Env as u32 == LogDebugInfoFlag::Env as u32
48 }
49}
50
51impl Into<u32> for LogDebugInfoFlags {
52 fn into(self) -> u32 {
53 self.0
54 }
55}
56
57#[derive(Clone)]
58pub struct LogModuleConfig {
59 pub name: String,
60
61 pub level: LogLevel,
62
63 pub console: LogLevel,
65
66 pub file: bool,
68
69 pub file_name: Option<String>,
71
72 pub file_max_size: u64,
74
75 pub file_max_count: u32,
77
78 pub debug_info_flags: u32,
80}
81
82impl LogModuleConfig {
83 pub fn new_default(name: &str) -> Self {
84 Self {
85 name: name.to_owned(),
86
87 level: LogLevel::default(),
88 console: LogLevel::default(),
89 file: true,
90 file_name: Some(name.to_string()),
91 file_max_size: 1024 * 1024 * 10,
92 file_max_count: 10,
93 debug_info_flags: LogDebugInfoFlags::default().into(),
94 }
95 }
96
97 pub fn is_global_module(&self) -> bool {
98 self.name == "global"
99 }
100
101 pub fn max_level(&self) -> LogLevel {
102 std::cmp::max(&self.level, &self.console).to_owned()
103 }
104
105 pub fn set_level(&mut self, level: &str) {
106 self.level = LogLevel::from_str(level).expect(&format!("invalid level str: {}", level));
107 }
108
109 pub fn set_console(&mut self, level: &str) {
110 self.console = LogLevel::from_str(level).expect(&format!("invalid level str: {}", level));
111 }
112
113 pub fn set_file(&mut self, enable: bool) {
114 self.file = enable;
115 }
116
117 pub fn set_file_max_size(&mut self, file_max_size: u64) {
118 self.file_max_size = file_max_size;
119 }
120
121 pub fn set_file_max_count(&mut self, file_max_count: u32) {
122 self.file_max_count = file_max_count;
123 }
124
125 pub fn set_debug_info_flags(&mut self, flags: u32) {
126 self.debug_info_flags = flags;
127 }
128
129 pub fn get_debug_info_flags(&self) -> LogDebugInfoFlags {
130 LogDebugInfoFlags::new(self.debug_info_flags)
131 }
132
133 fn load_console_level_from_env() -> Option<LogLevel> {
135 match std::env::var(CYFS_CONSOLE_LOG_LEVEL_KEY) {
136 Ok(val) => {
137 match LogLevel::from_str(&val) {
138 Ok(level) => Some(level),
139 Err(e) => {
140 println!("parse env log level error! {}, {}", val, e);
141 None
142 }
143 }
144 }
145 Err(_) => {
146 None
147 }
148 }
149 }
150
151 fn load_file_level_from_env() -> Option<LogLevel> {
152 match std::env::var(CYFS_FILE_LOG_LEVEL_KEY) {
153 Ok(val) => {
154 match LogLevel::from_str(&val) {
155 Ok(level) => Some(level),
156 Err(e) => {
157 println!("parse env log level error! {}, {}", val, e);
158 None
159 }
160 }
161 }
162 Err(_) => {
163 None
164 }
165 }
166 }
167
168 pub fn load(&mut self, node: &toml::value::Table) {
169 let mut console: Option<LogLevel> = None;
170 let mut level: Option<LogLevel> = None;
171 for (k, v) in node {
172 match k.as_str() {
173 "console" => {
174 if let Ok(v) = TomlHelper::decode_from_string(v) {
175 console = Some(v);
176 }
177 }
178
179 "file" => {
180 if let Ok(v) = TomlHelper::decode_from_string::<String>(v) {
181 if v.as_str() == "off" {
182 self.file = false;
183 self.file_name = None;
184 } else {
185 self.file = true;
186 self.file_name = Some(v);
187 }
188 }
189 }
190
191 "level" => {
192 if let Ok(v) = TomlHelper::decode_from_string(v) {
193 level = Some(v);
194 }
195 }
196
197 "file_max_size" => {
198 match TomlHelper::decode_to_int::<u64>(v) {
199 Ok(v) => self.file_max_size = v,
200 Err(e) => println!("decode log.toml file_max_size field error! {}", e),
201 }
202 }
203
204 "file_max_count" => {
205 match TomlHelper::decode_to_int::<u32>(v) {
206 Ok(v) => self.file_max_count = v,
207 Err(e) => println!("decode log.toml file_max_count field error! {}", e),
208 }
209 }
210
211 v @ _ => {
212 println!("unknown module config field: {}", v);
213 }
214 }
215 }
216
217 if console.is_none() && level.is_some() {
219 console = level.clone();
220 }
221
222 if let Some(v) = level {
223 self.level = v;
224 }
225 if let Some(v) = console {
226 self.console = v;
227 }
228
229 if let Some(level) = Self::load_console_level_from_env() {
231 println!("load module console level from env: mod={}, level={}, old={}", self.name, level, self.console);
232 self.console = level;
233 }
234
235 if let Some(level) = Self::load_file_level_from_env() {
236 println!("load module file level from env: mod={}, level={}, old={}", self.name, level, self.level);
237 self.level = level;
238 }
239 }
240}
241
242pub struct LogConfig {
243 pub log_dir: PathBuf,
244 pub basename: String,
245
246 pub global: LogModuleConfig,
247 pub modules: HashMap<String, LogModuleConfig>,
248}
249
250impl LogConfig {
251 pub fn new(log_dir: PathBuf) -> Self {
252 let arg0 = std::env::args().next().unwrap_or_else(|| "main".to_owned());
253 let basename = Path::new(&arg0).file_stem().unwrap().to_string_lossy().to_string();
254
255 Self {
256 log_dir,
257 global: LogModuleConfig::new_default("global"),
258 modules: HashMap::new(),
259 basename,
260 }
261 }
262
263 pub fn set_log_dir(&mut self, log_dir: PathBuf) {
264 self.log_dir = log_dir;
265 }
266
267 pub fn get_mod_config(&self, name: Option<&str>) -> &LogModuleConfig {
268 match name {
269 None => {
270 &self.global
271 }
272 Some(name) => {
273 if let Some(m) = self.modules.get(name) {
274 return m;
275 }
276
277 &self.global
278 }
279 }
280 }
281
282 pub fn load(&mut self, config_node: &toml::Value) -> BuckyResult<()> {
283
284 let node = config_node.as_table().ok_or_else(|| {
285 let msg = format!(
286 "invalid log config format! content={}",
287 config_node,
288 );
289 error!("{}", msg);
290
291 BuckyError::new(BuckyErrorCode::InvalidFormat, msg)
292 })?;
293
294 for (k, v) in node {
295 if !v.is_table() {
296 error!("invalid config node format: {}", v);
297 continue;
298 }
299
300 let v = v.as_table().unwrap();
301 match k.as_str() {
302 "global" => {
303 self.global.load(v);
304 }
305
306 key @ _ => {
307 self.load_mod(key, v);
308 }
309 }
310 }
311
312 Ok(())
313 }
314
315 fn load_mod(&mut self, name: &str, node: &toml::value::Table) {
316 let mut module = LogModuleConfig::new_default(name);
317 module.load(node);
318
319 self.add_mod(module);
320 }
321
322 pub fn add_mod(&mut self, module: LogModuleConfig) {
323 if let Some(old) = self.modules.insert(module.name.clone(), module) {
324 println!("replace old log module: {}={}", old.name, old.level);
325 }
326 }
327
328 pub fn disable_module_log(&mut self, name: &str, level: &LogLevel) {
329 let mut module = LogModuleConfig::new_default(name);
330 module.level = *level;
331 module.console = *level;
332 module.file_name = None;
333
334 match self.modules.entry(name.to_owned()) {
336 Entry::Vacant(entry) => {
337 entry.insert(module);
338 },
339 Entry::Occupied(mut _entry)=>{
340 println!("module already in config: name={}", name);
341 }
342 };
343 }
344
345 pub fn disable_async_std_log(&mut self) {
347 let mod_list = [
348 ("async_io", LogLevel::Warn),
349 ("polling", LogLevel::Warn),
350 ("async_tungstenite", LogLevel::Warn),
351 ("tungstenite", LogLevel::Warn),
352 ("async_std", LogLevel::Warn),
353 ("tide", LogLevel::Off),
354 ];
355
356 mod_list.iter().for_each(|(name, level)| {
357 self.disable_module_log(name, level);
358 })
359 }
360}