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