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