1 /*
2  * Copyright (C) 2023 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 use std::io::Write;
16 use std::path::Path;
17 use std::sync::{Arc, Mutex};
18 use std::time::SystemTime;
19 
20 use hdc::config;
21 
22 // #[derive(Default)]
23 
24 pub struct LoggerMeta {
25     stdout_require: bool,
26     run_in_server: bool, // scrolling dump only by server
27     current_size: usize,
28     log_file: std::path::PathBuf,
29     log_level: log::LevelFilter,
30 }
31 
32 impl Default for LoggerMeta {
defaultnull33     fn default() -> Self {
34         Self {
35             stdout_require: Default::default(),
36             run_in_server: Default::default(),
37             current_size: Default::default(),
38             log_file: Default::default(),
39             log_level: log::LevelFilter::Debug,
40         }
41     }
42 }
43 
44 type LoggerMeta_ = Arc<Mutex<LoggerMeta>>;
45 
46 struct HostLoggerMeta {}
47 impl HostLoggerMeta {
get_instancenull48     fn get_instance() -> LoggerMeta_ {
49         static mut LOGGER_META: Option<LoggerMeta_> = None;
50         unsafe {
51             LOGGER_META
52                 .get_or_insert_with(|| Arc::new(Mutex::new(LoggerMeta::default())))
53                 .clone()
54         }
55     }
56 
initnull57     fn init(run_in_server: bool, spawned_server: bool, log_level: log::LevelFilter) {
58         let instance = Self::get_instance();
59         let Ok(mut meta) = instance.lock() else {
60             println!("init lock error");
61             return;
62         };
63         meta.log_level = log_level;
64         if run_in_server && !spawned_server {
65             meta.stdout_require = true;
66         }
67         meta.run_in_server = run_in_server;
68         meta.log_file = Path::new(&std::env::temp_dir())
69             .join(config::LOG_FILE_NAME.to_string() + config::LOG_TAIL_NAME);
70         if run_in_server {
71             Self::dump_log_file(config::LOG_BAK_NAME, meta.log_level);
72             if let Err(err) = std::fs::File::create(&meta.log_file) {
73                 println!("File::create failed, {}, {err}", meta.log_file.display());
74             }
75         }
76     }
77 
write_lognull78     fn write_log(content: String) {
79         let instance = Self::get_instance();
80         let mut meta = instance.lock().unwrap();
81         if meta.run_in_server && meta.current_size > config::LOG_FILE_SIZE {
82             meta.current_size = 0;
83             Self::dump_log_file(config::LOG_CACHE_NAME, meta.log_level);
84             std::fs::File::create(&meta.log_file).unwrap();
85         }
86         meta.current_size += content.len();
87         if let Ok(mut f) = std::fs::File::options().append(true).open(&meta.log_file) {
88             writeln!(&mut f, "{}", content).unwrap();
89         }
90         if meta.stdout_require {
91             println!("{}", content);
92         }
93     }
94 
dump_log_filenull95     fn dump_log_file(file_type: &str, log_level: log::LevelFilter) {
96         let file_path = Path::new(&std::env::temp_dir())
97             .join(config::LOG_FILE_NAME.to_string() + config::LOG_TAIL_NAME);
98         let ts = humantime::format_rfc3339_millis(SystemTime::now())
99             .to_string()
100             .replace(':', "");
101         let file_cache_path = if log_level == log::LevelFilter::Trace {
102             Path::new(&std::env::temp_dir())
103                 .join(file_type.to_string() + &ts[..19] + config::LOG_TAIL_NAME)
104         } else {
105             Path::new(&std::env::temp_dir()).join(file_type.to_string() + config::LOG_TAIL_NAME)
106         };
107         if file_path.exists() {
108             if let Err(err) = std::fs::rename(&file_path, file_cache_path) {
109                 hdc::error!("rename failed, {err}");
110             }
111         }
112     }
113 
get_running_modenull114     fn get_running_mode() -> String {
115         let instance = Self::get_instance();
116         let Ok(meta) = instance.lock() else {
117             return "".to_string();
118         };
119         if meta.run_in_server {
120             "server".to_string()
121         } else {
122             "client".to_string()
123         }
124     }
125 }
126 
127 struct SimpleHostLogger;
128 impl log::Log for SimpleHostLogger {
enablednull129     fn enabled(&self, metadata: &log::Metadata) -> bool {
130         metadata.level() <= log::max_level()
131     }
lognull132     fn log(&self, record: &log::Record) {
133         if self.enabled(record.metadata()) {
134             let ts = humantime::format_rfc3339_millis(SystemTime::now()).to_string();
135             let level = &record.level().to_string()[..1];
136             let Some(file) = record.file() else {
137                 println!("Get record file failed");
138                 return;
139             };
140             // cargo编译下的文件目录可能存在\\的目录,需要通过编译宏隔离
141             #[cfg(target_os = "windows")]
142             let file = file.replace('\\', "/");
143             let running_mode = HostLoggerMeta::get_running_mode();
144             let content = format!(
145                 "{} {} {} [{}] {}:{} - {}",
146                 &ts[..10],
147                 &ts[11..23],
148                 level,
149                 running_mode,
150                 file.split_once('/').unwrap_or(("", "")).1,
151                 record.line().unwrap_or_default(),
152                 record.args()
153             );
154             HostLoggerMeta::write_log(content);
155         }
156     }
flushnull157     fn flush(&self) {}
158 }
159 
160 static LOGGER: SimpleHostLogger = SimpleHostLogger;
161 
logger_initnull162 pub fn logger_init(log_level: log::LevelFilter, run_in_server: bool, spawned_server: bool) {
163     HostLoggerMeta::init(run_in_server, spawned_server, log_level);
164     if let Err(err) = log::set_logger(&LOGGER) {
165         println!("log::set_logger failed, {err}");
166         return;
167     }
168     log::set_max_level(log_level);
169 }
170