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