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     /// Set the log level.
set_log_level(&mut self, level: Level)22     fn set_log_level(&mut self, level: Level);
23 
24     /// Get the log level.
get_log_level(&self) -> Level25     fn get_log_level(&self) -> Level;
26 }
27 
28 /// Logging related implementation.
29 pub struct BluetoothLogging {
30     /// Current log level
31     /// If the level is not verbose, `VERBOSE_ONLY_LOG_TAGS` will be set to emit up to `INFO` only.
32     log_level: Level,
33 
34     /// Log to stderr?
35     is_stderr: bool,
36 
37     /// Is logging already initialized?
38     is_initialized: bool,
39 }
40 
41 const VERBOSE_ONLY_LOG_TAGS: &[&str] = &[
42     "bt_bta_av", // AV apis
43     "btm_sco",   // SCO data path logs
44     "l2c_csm",   // L2CAP state machine
45     "l2c_link",  // L2CAP link layer logs
46     "sco_hci",   // SCO over HCI
47     "uipc",      // Userspace IPC implementation
48 ];
49 
50 impl BluetoothLogging {
new(is_debug: bool, is_verbose_debug: bool, log_output: &str) -> Self51     pub fn new(is_debug: bool, is_verbose_debug: bool, log_output: &str) -> Self {
52         let is_stderr = log_output == "stderr";
53 
54         let log_level = match (is_debug, is_verbose_debug) {
55             (true, true) => Level::Verbose,
56             (true, false) => Level::Debug,
57             _ => Level::Info,
58         };
59 
60         Self { log_level, is_stderr, is_initialized: false }
61     }
62 
initialize(&mut self) -> Result<(), Error>63     pub fn initialize(&mut self) -> Result<(), Error> {
64         if self.is_stderr {
65             env_logger::Builder::new().filter(None, self.get_log_level_filter()).init();
66         } else {
67             let formatter = Formatter3164 {
68                 facility: Facility::LOG_USER,
69                 hostname: None,
70                 process: "btadapterd".into(),
71                 pid: 0,
72             };
73 
74             let logger = syslog::unix(formatter)?;
75             let _ = log::set_boxed_logger(Box::new(BasicLogger::new(logger)))
76                 .map(|()| self.apply_linux_log_level());
77             log_panics::init();
78         }
79 
80         // Set initial log levels and filter out tags if not verbose debug.
81         self.apply_libbluetooth_log_level();
82 
83         // Initialize the underlying system as well.
84         self.is_initialized = true;
85         Ok(())
86     }
87 
should_enable_debug_mode(&self) -> bool88     fn should_enable_debug_mode(&self) -> bool {
89         self.log_level == Level::Debug || self.log_level == Level::Verbose
90     }
91 
get_log_level_filter(&self) -> LevelFilter92     fn get_log_level_filter(&self) -> LevelFilter {
93         match self.should_enable_debug_mode() {
94             true => LevelFilter::Debug,
95             false => LevelFilter::Info,
96         }
97     }
98 
apply_linux_log_level(&self)99     fn apply_linux_log_level(&self) {
100         log::set_max_level(self.get_log_level_filter());
101     }
102 
apply_libbluetooth_log_level(&self)103     fn apply_libbluetooth_log_level(&self) {
104         set_default_log_level(self.log_level);
105 
106         // TODO(b/371889111): Don't set log level for tag until b/371889111 is fixed.
107         /*
108         // Levels for verbose-only tags.
109         let level = match self.log_level {
110             Level::Verbose => Level::Verbose,
111             _ => Level::Info,
112         };
113         for tag in VERBOSE_ONLY_LOG_TAGS {
114             log::info!("Setting log level for tag {} to {:?}", tag, level);
115             set_log_level_for_tag(tag, level);
116         }
117          */
118     }
119 }
120 
121 impl IBluetoothLogging for BluetoothLogging {
is_debug_enabled(&self) -> bool122     fn is_debug_enabled(&self) -> bool {
123         self.is_initialized && self.should_enable_debug_mode()
124     }
125 
set_debug_logging(&mut self, enabled: bool)126     fn set_debug_logging(&mut self, enabled: bool) {
127         if enabled {
128             match self.log_level {
129                 Level::Verbose => {
130                     self.set_log_level(Level::Verbose);
131                 }
132                 _ => {
133                     self.set_log_level(Level::Debug);
134                 }
135             }
136         } else {
137             self.set_log_level(Level::Info);
138         }
139     }
140 
set_log_level(&mut self, level: Level)141     fn set_log_level(&mut self, level: Level) {
142         if !self.is_initialized {
143             return;
144         }
145 
146         self.log_level = level;
147 
148         // Update log level in Linux stack.
149         self.apply_linux_log_level();
150 
151         // Update log level in libbluetooth.
152         self.apply_libbluetooth_log_level();
153 
154         // Mark the start of debug logging with a debug print.
155         if self.is_debug_enabled() {
156             log::debug!("Debug logging successfully enabled!");
157         }
158 
159         log::info!("Setting log level to {:?}", level);
160     }
161 
get_log_level(&self) -> Level162     fn get_log_level(&self) -> Level {
163         self.log_level
164     }
165 }
166