1 //
2 //  Copyright 2023 Google, Inc.
3 //
4 //  Licensed under the Apache License, Version 2.0 (the "License");
5 //  you may not use this file except in compliance with the License.
6 //  You may obtain a copy of the License at:
7 //
8 //  http://www.apache.org/licenses/LICENSE-2.0
9 //
10 //  Unless required by applicable law or agreed to in writing, software
11 //  distributed under the License is distributed on an "AS IS" BASIS,
12 //  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 //  See the License for the specific language governing permissions and
14 //  limitations under the License.
15 
16 //! A logger for use by netsim and netsimd.
17 //!
18 //! Uses the env_logger crate that allows control of logging through
19 //! the RUST_LOG environment variable.
20 
21 use env_logger::{Builder, Env};
22 use log::{Level, Record};
23 use std::{
24     ffi::OsStr,
25     io::Write,
26     path::{Path, MAIN_SEPARATOR},
27 };
28 
29 use crate::util::time_display::log_current_time;
30 
31 /// Initiating the environment for logging with given prefix
32 ///
33 /// The current log format follows the same format as Android Emulator team.
init(prefix: &'static str, is_verbose: bool)34 pub fn init(prefix: &'static str, is_verbose: bool) {
35     let log_filter = if is_verbose { "debug" } else { "info" };
36     let mut builder = Builder::from_env(Env::default().default_filter_or(log_filter));
37     builder.format(move |buf, record| {
38         let level = level_to_string(record.level());
39         let message = format!(
40             "{} {} {} {}:{} - {}",
41             prefix,
42             level,
43             log_current_time(),
44             format_file(record),
45             record.line().unwrap_or(0),
46             record.args()
47         );
48         writeln!(buf, "{}", message)
49     });
50     builder.init();
51 }
52 
53 /// Initiating the environment for logging in Rust unit tests
54 ///
55 /// The current log format follows the same format as Android Emulator team.
init_for_test()56 pub fn init_for_test() {
57     let mut binding = Builder::from_env(Env::default().default_filter_or("info"));
58     let builder = binding.is_test(true);
59     builder.format(move |buf, record| {
60         let level = level_to_string(record.level());
61         let message =
62             format!("{} {} \t| netsim-test: {}", level, log_current_time(), record.args());
63         writeln!(buf, "{}", message)
64     });
65     builder.init();
66 }
67 
68 /// Helper function for parsing the file name from given record file path
69 /// This will provide the file information where the log function is called
format_file<'a>(record: &'a Record<'a>) -> &'a str70 fn format_file<'a>(record: &'a Record<'a>) -> &'a str {
71     match record.file() {
72         Some(filepath) => {
73             let file = Path::new(filepath);
74             let netsim_path = format!("tools{MAIN_SEPARATOR}netsim");
75             // If file path includes tools/netsim, only print the file name
76             if file.to_str().is_some_and(|f| f.contains(&netsim_path)) {
77                 return file.file_name().unwrap_or(OsStr::new("N/A")).to_str().unwrap();
78             }
79             // Print full path for all dependent crates
80             return file.to_str().unwrap();
81         }
82         None => "N/A",
83     }
84 }
85 
86 /// Helper function for translating log levels to string.
level_to_string(level: Level) -> &'static str87 fn level_to_string(level: Level) -> &'static str {
88     match level {
89         Level::Error => "E",
90         Level::Warn => "W",
91         Level::Info => "I",
92         Level::Debug => "D",
93         Level::Trace => "T",
94     }
95 }
96 
97 /// This test is an example of having logs in Rust unit tests
98 ///
99 /// Expected log: INFO  | netsim-test: Hello Netsim
100 #[test]
test_init_for_test()101 fn test_init_for_test() {
102     init_for_test();
103     log::info!("Hello Netsim");
104 }
105