cyfs_util/util/
log_util.rs

1use super::path_util::{get_app_log_dir, get_log_dir};
2use cyfs_base::{BuckyError, BuckyErrorCode, BuckyResult};
3use flexi_logger::{
4    opt_format, Cleanup, Criterion, DeferredNow, Duplicate, Level, Logger, Naming, Record,
5};
6use std::path::{Path, PathBuf};
7
8fn cyfs_default_format(
9    w: &mut dyn std::io::Write,
10    now: &mut DeferredNow,
11    record: &Record,
12) -> Result<(), std::io::Error> {
13    write!(
14        w,
15        "[{}] {} [{}] {}",
16        now.now().format("%Y-%m-%d %H:%M:%S%.3f"),
17        record.level(),
18        record.module_path().unwrap_or("<unnamed>"),
19        //record.file().unwrap_or("<unnamed>"),
20        //record.line().unwrap_or(0),
21        &record.args()
22    )
23}
24
25#[cfg(feature = "colors")]
26pub fn cyfs_colored_default_format(
27    w: &mut dyn std::io::Write,
28    now: &mut DeferredNow,
29    record: &Record,
30) -> Result<(), std::io::Error> {
31    let level = record.level();
32    write!(
33        w,
34        "[{}] {} [{}] {}",
35        style(level, now.now().format("%Y-%m-%d %H:%M:%S%.3f")),
36        style(level, record.level()),
37        record.module_path().unwrap_or("<unnamed>"),
38        //record.file().unwrap_or("<unnamed>"),
39        //record.line().unwrap_or(0),
40        style(level, &record.args())
41    )
42}
43
44fn str_to_duplevel(level: &str) -> Duplicate {
45    match level {
46        "none" => Duplicate::None,
47        "trace" => Duplicate::Trace,
48        "debug" => Duplicate::Debug,
49        "info" => Duplicate::Info,
50        "warn" => Duplicate::Warn,
51        "error" => Duplicate::Error,
52        _ => Duplicate::All,
53    }
54}
55
56use log::{Log, Metadata};
57
58struct ModuleLevel {
59    name: String,
60    level: Level,
61}
62
63// 过滤掉一些基础模块的trace日志
64struct FilterLog {
65    logger: Box<dyn Log>,
66    mod_levels: Vec<ModuleLevel>,
67}
68
69impl FilterLog {
70    fn new(logger: Box<dyn Log>) -> Self {
71        let mut ret = Self {
72            logger,
73            mod_levels: Vec::new(),
74        };
75        ret.disable_async_std_log();
76
77        ret
78    }
79    // 屏蔽一些基础库的trace log等
80    fn disable_async_std_log(&mut self) {
81        self.mod_levels.push(ModuleLevel {
82            name: "async_io".to_owned(),
83            level: Level::Info,
84        });
85        self.mod_levels.push(ModuleLevel {
86            name: "polling".to_owned(),
87            level: Level::Info,
88        });
89        self.mod_levels.push(ModuleLevel {
90            name: "async_tungstenite".to_owned(),
91            level: Level::Info,
92        });
93        self.mod_levels.push(ModuleLevel {
94            name: "tungstenite".to_owned(),
95            level: Level::Info,
96        });
97        self.mod_levels.push(ModuleLevel {
98            name: "async_std".to_owned(),
99            level: Level::Info,
100        });
101        self.mod_levels.push(ModuleLevel {
102            name: "tide".to_owned(),
103            level: Level::Info,
104        });
105    }
106}
107
108impl Log for FilterLog {
109    fn enabled(&self, metadata: &Metadata) -> bool {
110        self.logger.enabled(metadata)
111    }
112
113    fn log(&self, record: &Record) {
114        let target = record.metadata().target();
115        //println!("log target={}", target);
116
117        for item in &self.mod_levels {
118            if target.starts_with(&item.name) {
119                if record.level() > item.level {
120                    return;
121                }
122            }
123        }
124        self.logger.log(record);
125    }
126
127    fn flush(&self) {
128        self.logger.flush();
129    }
130}
131
132pub struct ModuleLog {
133    log_dir: PathBuf,
134
135    main_logger: Box<dyn Log>,
136    bdt_logger: Option<Box<dyn Log>>,
137}
138
139impl ModuleLog {
140    pub fn new(
141        log_dir: &Path,
142        log_level: Option<&str>,
143        screen_level: Option<&str>,
144    ) -> BuckyResult<Self> {
145        let main_logger = Self::new_logger("main", log_dir, log_level, screen_level)?;
146
147        let ret = Self {
148            log_dir: log_dir.to_owned(),
149            main_logger,
150            bdt_logger: None,
151        };
152
153        Ok(ret)
154    }
155
156    pub fn enable_bdt(&mut self, log_level: Option<&str>, screen_level: Option<&str>) {
157        assert!(self.bdt_logger.is_none());
158
159        if let Ok(logger) = Self::new_logger("bdt", &self.log_dir, log_level, screen_level) {
160            self.bdt_logger = Some(logger);
161        }
162    }
163
164    pub fn start(self) {
165        // 捕获所有的panic
166        ::log_panics::init();
167
168        let logger = Box::new(self) as Box<dyn Log>;
169        if let Err(e) = log::set_boxed_logger(logger) {
170            let msg = format!("call set_boxed_logger failed! {}", e);
171            println!("{}", msg);
172        }
173    }
174
175    // 默认日志级别
176    fn default_log_level(log_level: Option<&str>) -> &str {
177        #[cfg(debug_assertions)]
178        let log_default_level = "debug";
179
180        #[cfg(not(debug_assertions))]
181        let log_default_level = "info";
182
183        match log_level {
184            Some(level) if level.len() > 0 => level,
185            _ => log_default_level,
186        }
187    }
188
189    /*
190    // 目前优先使用flexi_logger的RUST_LOG环境变量来统一控制
191    // 日志级别优先级:ENV > 参数 > 默认值
192    // 子模块会默认使用main模块日志级别,除非有独立的配置
193    fn env_log_level(log_level: Option<&str>, mod_name: &str) -> String {
194        let default_log_level = Self::default_log_level(log_level);
195
196        // 首先获取主模块的env配置
197        let env_log_level = std::env::var("CYFS_LOG_LEVEL").unwrap_or_else(|_| default_log_level.to_owned());
198
199        if mod_name != "main" {
200            let var_name = format!("CYFS_{}_LOG_LEVEL", mod_name.to_uppercase());
201            std::env::var(&var_name).unwrap_or_else(|_| env_log_level)
202        }else {
203            env_log_level
204        }
205    }
206    */
207
208    fn new_logger(
209        mod_name: &str,
210        log_dir: &Path,
211        log_level: Option<&str>,
212        screen_level: Option<&str>,
213    ) -> BuckyResult<Box<dyn Log>> {
214        let log_level = Self::default_log_level(log_level);
215
216        let discriminant = if mod_name == "main" {
217            std::process::id().to_string()
218        } else {
219            format!("{}_{}", mod_name, std::process::id())
220        };
221
222        let file_spec = flexi_logger::FileSpec::default()
223            .directory(log_dir)
224            .discriminant(discriminant)
225            .suppress_timestamp();
226
227        let mut logger = Logger::try_with_env_or_str(log_level)
228            .map_err(|e| {
229                let msg = format!(
230                    "init logger from env or str error! level={:?}, {}",
231                    log_level, e
232                );
233                println!("{}", msg);
234                BuckyError::new(BuckyErrorCode::InvalidFormat, msg)
235            })?
236            .log_to_file(file_spec)
237            .rotate(
238                Criterion::Size(1024 * 1024 * 10),
239                Naming::Numbers,
240                Cleanup::KeepLogFiles(20),
241            )
242            .format_for_files(opt_format)
243            .duplicate_to_stderr(str_to_duplevel(screen_level.unwrap_or("all")));
244
245        // #[cfg(windows)]
246        // {
247        //use flexi_logger::detailed_format;
248        logger = logger.format_for_stderr(cyfs_default_format);
249        //}
250        // #[cfg(not(windows))]
251        // {
252        //     use flexi_logger::colored_detailed_format;
253        //     logger = logger.format_for_stderr(colored_detailed_format);
254        // }
255        #[cfg(feature = "colors")]
256        {
257            logger = logger.format_for_stderr(cyfs_colored_default_format);
258        }
259        let (logger, _handle) = logger.build().map_err(|e| {
260            let msg = format!("init logger failed! {}", e);
261            println!("{}", msg);
262
263            BuckyError::from(msg)
264        })?;
265
266        let logger = FilterLog::new(logger);
267        Ok(Box::new(logger))
268    }
269}
270
271impl Log for ModuleLog {
272    fn enabled(&self, metadata: &Metadata) -> bool {
273        self.main_logger.enabled(metadata)
274    }
275
276    fn log(&self, record: &Record) {
277        //println!("{:?}", record);
278        if let Some(bdt_logger) = &self.bdt_logger {
279            let target = record.metadata().target();
280            if target.starts_with("cyfs_bdt::") {
281                bdt_logger.log(record);
282                return;
283            }
284        }
285
286        self.main_logger.log(record);
287    }
288
289    fn flush(&self) {
290        self.main_logger.flush();
291        if let Some(bdt_logger) = &self.bdt_logger {
292            bdt_logger.flush();
293        }
294    }
295}
296
297fn init_log_internal(log_dir: &Path, log_level: Option<&str>) {
298    // 捕获所有的panic
299    ::log_panics::init();
300
301    if let Ok(logger) = ModuleLog::new_logger("main", log_dir, log_level, None) {
302        if let Err(e) = log::set_boxed_logger(logger) {
303            println!("init default file log failed! {}", e);
304        }
305    }
306}
307
308/*
309#[deprecated(
310    note = "Please use the cyfs_debug::CyfsLogger instead"
311)]
312*/
313pub fn init_log(service_name: &str, log_level: Option<&str>) {
314    init_log_internal(&get_log_dir(service_name), log_level);
315}
316
317pub fn create_log_with_isolate_bdt(
318    service_name: &str,
319    log_level: Option<&str>,
320    bdt_log_level: Option<&str>,
321) -> BuckyResult<ModuleLog> {
322    let mut mod_log = ModuleLog::new(&get_log_dir(service_name), log_level.clone(), None)?;
323    mod_log.enable_bdt(bdt_log_level, None);
324    Ok(mod_log)
325}
326
327#[deprecated(
328    note = "Please use the cyfs_debug::CyfsLogger instead"
329)]
330pub fn init_log_with_isolate_bdt(
331    service_name: &str,
332    log_level: Option<&str>,
333    bdt_log_level: Option<&str>,
334) {
335    if let Ok(mod_log) = create_log_with_isolate_bdt(service_name, log_level, bdt_log_level) {
336        mod_log.start();
337    }
338}
339
340#[deprecated(
341    note = "Please use the cyfs_debug::CyfsLogger instead"
342)]
343pub fn init_log_with_isolate_bdt_screen(
344    service_name: &str,
345    log_level: Option<&str>,
346    bdt_log_level: Option<&str>,
347    screen_level: Option<&str>,
348) {
349    if let Ok(mut mod_log) =
350        ModuleLog::new(&get_log_dir(service_name), log_level.clone(), screen_level)
351    {
352        mod_log.enable_bdt(bdt_log_level, screen_level);
353        mod_log.start();
354    }
355}
356
357#[deprecated(
358    note = "Please use the cyfs_debug::CyfsLogger instead"
359)]
360pub fn init_log_with_path(log_dir: &Path, log_level: Option<&str>) {
361    init_log_internal(log_dir, log_level);
362}
363
364#[deprecated(
365    note = "Please use the cyfs_debug::CyfsLogger instead"
366)]
367pub fn init_app_log(app_name: &str, log_level: Option<&str>) {
368    init_log_internal(&get_app_log_dir(app_name), log_level);
369}
370
371
372pub fn flush_log() {
373    log::logger().flush();
374}