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.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 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
63struct 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 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 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 ::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 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 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 logger = logger.format_for_stderr(cyfs_default_format);
249 #[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 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 ::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
308pub 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}