1 //! Modify the Bluetooth logging configuration to enable debug logging. 2 //! 3 //! There are two logging implementations depending on whether the log is 4 //! emitted from Rust or C/C++. In order to keep log levels in sync between the 5 //! two, the |BluetoothLogging| struct will configure both the Rust logging and 6 //! the C/C++ logging (via topshim). 7 use bt_topshim::syslog::{set_default_log_level, set_log_level_for_tag, Level}; 8 use log::LevelFilter; 9 use syslog::{BasicLogger, Error, Facility, Formatter3164}; 10 11 use log_panics; 12 13 /// API to modify log levels that is exposed via RPC. 14 pub trait IBluetoothLogging { 15 /// Check whether debug logging is enabled. is_debug_enabled(&self) -> bool16 fn is_debug_enabled(&self) -> bool; 17 18 /// Change whether debug logging is enabled. set_debug_logging(&mut self, enabled: bool)19 fn set_debug_logging(&mut self, enabled: bool); 20 } 21 22 /// Logging related implementation. 23 pub struct BluetoothLogging { 24 /// Should debug logs be emitted? 25 is_debug: bool, 26 27 /// If this flag is not set, we will not emit debug logs for all tags. 28 /// `VERBOSE_ONLY_LOG_TAGS` will be set to emit up to `INFO` only. This 29 /// can only be configured in the constructor (not modifiable at runtime). 30 is_verbose_debug: bool, 31 32 /// Log to stderr? 33 is_stderr: bool, 34 35 /// Is logging already initialized? 36 is_initialized: bool, 37 } 38 39 const VERBOSE_ONLY_LOG_TAGS: &[&str] = &[ 40 "bt_bta_av", // AV apis 41 "btm_sco", // SCO data path logs 42 "l2c_csm", // L2CAP state machine 43 "l2c_link", // L2CAP link layer logs 44 "sco_hci", // SCO over HCI 45 "uipc", // Userspace IPC implementation 46 ]; 47 48 impl BluetoothLogging { new(is_debug: bool, is_verbose_debug: bool, log_output: &str) -> Self49 pub fn new(is_debug: bool, is_verbose_debug: bool, log_output: &str) -> Self { 50 let is_stderr = log_output == "stderr"; 51 Self { is_debug, is_verbose_debug, is_stderr, is_initialized: false } 52 } 53 initialize(&mut self) -> Result<(), Error>54 pub fn initialize(&mut self) -> Result<(), Error> { 55 let level = if self.is_debug { LevelFilter::Debug } else { LevelFilter::Info }; 56 57 if self.is_stderr { 58 env_logger::Builder::new().filter(None, level).init(); 59 } else { 60 let formatter = Formatter3164 { 61 facility: Facility::LOG_USER, 62 hostname: None, 63 process: "btadapterd".into(), 64 pid: 0, 65 }; 66 67 let logger = syslog::unix(formatter)?; 68 let _ = log::set_boxed_logger(Box::new(BasicLogger::new(logger))) 69 .map(|()| log::set_max_level(level)); 70 log_panics::init(); 71 } 72 73 // Set initial log levels and filter out tags if not verbose debug. 74 set_default_log_level(self.get_libbluetooth_level()); 75 if self.is_debug && !self.is_verbose_debug { 76 for tag in VERBOSE_ONLY_LOG_TAGS { 77 set_log_level_for_tag(tag, Level::Info); 78 } 79 } 80 81 // Initialize the underlying system as well. 82 self.is_initialized = true; 83 Ok(()) 84 } 85 get_libbluetooth_level(&self) -> Level86 fn get_libbluetooth_level(&self) -> Level { 87 if self.is_debug { 88 if self.is_verbose_debug { 89 Level::Verbose 90 } else { 91 Level::Debug 92 } 93 } else { 94 Level::Info 95 } 96 } 97 } 98 99 impl IBluetoothLogging for BluetoothLogging { is_debug_enabled(&self) -> bool100 fn is_debug_enabled(&self) -> bool { 101 self.is_initialized && self.is_debug 102 } 103 set_debug_logging(&mut self, enabled: bool)104 fn set_debug_logging(&mut self, enabled: bool) { 105 if !self.is_initialized { 106 return; 107 } 108 109 self.is_debug = enabled; 110 111 // Update log level in Linux stack. 112 let level = if self.is_debug { LevelFilter::Debug } else { LevelFilter::Info }; 113 log::set_max_level(level); 114 115 // Update log level in libbluetooth. 116 let level = self.get_libbluetooth_level(); 117 set_default_log_level(level); 118 119 // Mark the start of debug logging with a debug print. 120 if self.is_debug { 121 log::debug!("Debug logging successfully enabled!"); 122 } 123 124 log::info!("Setting debug logging to {}", self.is_debug); 125 } 126 } 127