xref: /aosp_15_r20/system/logging/rust/logger.rs (revision 598139dc91b21518d67c408eaea2644226490971)
1*598139dcSAndroid Build Coastguard Worker // Copyright 2021, The Android Open Source Project
2*598139dcSAndroid Build Coastguard Worker //
3*598139dcSAndroid Build Coastguard Worker // Licensed under the Apache License, Version 2.0 (the "License");
4*598139dcSAndroid Build Coastguard Worker // you may not use this file except in compliance with the License.
5*598139dcSAndroid Build Coastguard Worker // You may obtain a copy of the License at
6*598139dcSAndroid Build Coastguard Worker //
7*598139dcSAndroid Build Coastguard Worker //     http://www.apache.org/licenses/LICENSE-2.0
8*598139dcSAndroid Build Coastguard Worker //
9*598139dcSAndroid Build Coastguard Worker // Unless required by applicable law or agreed to in writing, software
10*598139dcSAndroid Build Coastguard Worker // distributed under the License is distributed on an "AS IS" BASIS,
11*598139dcSAndroid Build Coastguard Worker // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12*598139dcSAndroid Build Coastguard Worker // See the License for the specific language governing permissions and
13*598139dcSAndroid Build Coastguard Worker // limitations under the License.
14*598139dcSAndroid Build Coastguard Worker 
15*598139dcSAndroid Build Coastguard Worker //! Provides a universal logger interface that allows logging both on-device (using android_logger)
16*598139dcSAndroid Build Coastguard Worker //! and on-host (using env_logger).
17*598139dcSAndroid Build Coastguard Worker //! On-host, this allows the use of the RUST_LOG environment variable as documented in
18*598139dcSAndroid Build Coastguard Worker //! https://docs.rs/env_logger.
19*598139dcSAndroid Build Coastguard Worker use std::ffi::CString;
20*598139dcSAndroid Build Coastguard Worker use std::sync::atomic::{AtomicBool, Ordering};
21*598139dcSAndroid Build Coastguard Worker 
22*598139dcSAndroid Build Coastguard Worker static LOGGER_INITIALIZED: AtomicBool = AtomicBool::new(false);
23*598139dcSAndroid Build Coastguard Worker 
24*598139dcSAndroid Build Coastguard Worker type FormatFn = Box<dyn Fn(&log::Record) -> String + Sync + Send>;
25*598139dcSAndroid Build Coastguard Worker 
26*598139dcSAndroid Build Coastguard Worker /// Logger configuration, opportunistically mapped to configuration parameters for android_logger
27*598139dcSAndroid Build Coastguard Worker /// or env_logger where available.
28*598139dcSAndroid Build Coastguard Worker #[derive(Default)]
29*598139dcSAndroid Build Coastguard Worker pub struct Config<'a> {
30*598139dcSAndroid Build Coastguard Worker     log_level: Option<log::LevelFilter>,
31*598139dcSAndroid Build Coastguard Worker     custom_format: Option<FormatFn>,
32*598139dcSAndroid Build Coastguard Worker     filter: Option<&'a str>,
33*598139dcSAndroid Build Coastguard Worker     #[allow(dead_code)] // Field is only used on device, and ignored on host.
34*598139dcSAndroid Build Coastguard Worker     tag: Option<CString>,
35*598139dcSAndroid Build Coastguard Worker }
36*598139dcSAndroid Build Coastguard Worker 
37*598139dcSAndroid Build Coastguard Worker /// Based on android_logger::Config
38*598139dcSAndroid Build Coastguard Worker impl<'a> Config<'a> {
39*598139dcSAndroid Build Coastguard Worker     /// Changes the maximum log level.
40*598139dcSAndroid Build Coastguard Worker     ///
41*598139dcSAndroid Build Coastguard Worker     /// Note, that `Trace` is the maximum level, because it provides the
42*598139dcSAndroid Build Coastguard Worker     /// maximum amount of detail in the emitted logs.
43*598139dcSAndroid Build Coastguard Worker     ///
44*598139dcSAndroid Build Coastguard Worker     /// If `Off` level is provided, then nothing is logged at all.
45*598139dcSAndroid Build Coastguard Worker     ///
46*598139dcSAndroid Build Coastguard Worker     /// [`log::max_level()`] is considered as the default level.
with_max_level(mut self, level: log::LevelFilter) -> Self47*598139dcSAndroid Build Coastguard Worker     pub fn with_max_level(mut self, level: log::LevelFilter) -> Self {
48*598139dcSAndroid Build Coastguard Worker         self.log_level = Some(level);
49*598139dcSAndroid Build Coastguard Worker         self
50*598139dcSAndroid Build Coastguard Worker     }
51*598139dcSAndroid Build Coastguard Worker 
52*598139dcSAndroid Build Coastguard Worker     /// Set a log tag. Only used on device.
with_tag_on_device<S: Into<Vec<u8>>>(mut self, tag: S) -> Self53*598139dcSAndroid Build Coastguard Worker     pub fn with_tag_on_device<S: Into<Vec<u8>>>(mut self, tag: S) -> Self {
54*598139dcSAndroid Build Coastguard Worker         self.tag = Some(CString::new(tag).expect("Can't convert tag to CString"));
55*598139dcSAndroid Build Coastguard Worker         self
56*598139dcSAndroid Build Coastguard Worker     }
57*598139dcSAndroid Build Coastguard Worker 
58*598139dcSAndroid Build Coastguard Worker     /// Set the format function for formatting the log output.
59*598139dcSAndroid Build Coastguard Worker     /// ```
60*598139dcSAndroid Build Coastguard Worker     /// # use universal_logger::Config;
61*598139dcSAndroid Build Coastguard Worker     /// universal_logger::init(
62*598139dcSAndroid Build Coastguard Worker     ///     Config::default()
63*598139dcSAndroid Build Coastguard Worker     ///         .with_max_level(log::LevelFilter::Trace)
64*598139dcSAndroid Build Coastguard Worker     ///         .format(|record| format!("my_app: {}", record.args()))
65*598139dcSAndroid Build Coastguard Worker     /// )
66*598139dcSAndroid Build Coastguard Worker     /// ```
format<F>(mut self, format: F) -> Self where F: Fn(&log::Record) -> String + Sync + Send + 'static,67*598139dcSAndroid Build Coastguard Worker     pub fn format<F>(mut self, format: F) -> Self
68*598139dcSAndroid Build Coastguard Worker     where
69*598139dcSAndroid Build Coastguard Worker         F: Fn(&log::Record) -> String + Sync + Send + 'static,
70*598139dcSAndroid Build Coastguard Worker     {
71*598139dcSAndroid Build Coastguard Worker         self.custom_format = Some(Box::new(format));
72*598139dcSAndroid Build Coastguard Worker         self
73*598139dcSAndroid Build Coastguard Worker     }
74*598139dcSAndroid Build Coastguard Worker 
75*598139dcSAndroid Build Coastguard Worker     /// Set a filter, using the format specified in https://docs.rs/env_logger.
with_filter(mut self, filter: &'a str) -> Self76*598139dcSAndroid Build Coastguard Worker     pub fn with_filter(mut self, filter: &'a str) -> Self {
77*598139dcSAndroid Build Coastguard Worker         self.filter = Some(filter);
78*598139dcSAndroid Build Coastguard Worker         self
79*598139dcSAndroid Build Coastguard Worker     }
80*598139dcSAndroid Build Coastguard Worker }
81*598139dcSAndroid Build Coastguard Worker 
82*598139dcSAndroid Build Coastguard Worker /// Initializes logging on host. Returns false if logging is already initialized.
83*598139dcSAndroid Build Coastguard Worker /// Config values take precedence over environment variables for host logging.
84*598139dcSAndroid Build Coastguard Worker #[cfg(not(target_os = "android"))]
init(config: Config) -> bool85*598139dcSAndroid Build Coastguard Worker pub fn init(config: Config) -> bool {
86*598139dcSAndroid Build Coastguard Worker     // Return immediately if the logger is already initialized.
87*598139dcSAndroid Build Coastguard Worker     if LOGGER_INITIALIZED.fetch_or(true, Ordering::SeqCst) {
88*598139dcSAndroid Build Coastguard Worker         return false;
89*598139dcSAndroid Build Coastguard Worker     }
90*598139dcSAndroid Build Coastguard Worker 
91*598139dcSAndroid Build Coastguard Worker     let mut builder = env_logger::Builder::from_default_env();
92*598139dcSAndroid Build Coastguard Worker     if let Some(log_level) = config.log_level {
93*598139dcSAndroid Build Coastguard Worker         builder.filter_level(log_level);
94*598139dcSAndroid Build Coastguard Worker     }
95*598139dcSAndroid Build Coastguard Worker     if let Some(custom_format) = config.custom_format {
96*598139dcSAndroid Build Coastguard Worker         use std::io::Write; // Trait used by write!() macro, but not in Android code
97*598139dcSAndroid Build Coastguard Worker 
98*598139dcSAndroid Build Coastguard Worker         builder.format(move |f, r| {
99*598139dcSAndroid Build Coastguard Worker             let formatted = custom_format(r);
100*598139dcSAndroid Build Coastguard Worker             writeln!(f, "{}", formatted)
101*598139dcSAndroid Build Coastguard Worker         });
102*598139dcSAndroid Build Coastguard Worker     }
103*598139dcSAndroid Build Coastguard Worker     if let Some(filter_str) = config.filter {
104*598139dcSAndroid Build Coastguard Worker         builder.parse_filters(filter_str);
105*598139dcSAndroid Build Coastguard Worker     }
106*598139dcSAndroid Build Coastguard Worker 
107*598139dcSAndroid Build Coastguard Worker     builder.init();
108*598139dcSAndroid Build Coastguard Worker     true
109*598139dcSAndroid Build Coastguard Worker }
110*598139dcSAndroid Build Coastguard Worker 
111*598139dcSAndroid Build Coastguard Worker /// Initializes logging on device. Returns false if logging is already initialized.
112*598139dcSAndroid Build Coastguard Worker #[cfg(target_os = "android")]
init(config: Config) -> bool113*598139dcSAndroid Build Coastguard Worker pub fn init(config: Config) -> bool {
114*598139dcSAndroid Build Coastguard Worker     // Return immediately if the logger is already initialized.
115*598139dcSAndroid Build Coastguard Worker     if LOGGER_INITIALIZED.fetch_or(true, Ordering::SeqCst) {
116*598139dcSAndroid Build Coastguard Worker         return false;
117*598139dcSAndroid Build Coastguard Worker     }
118*598139dcSAndroid Build Coastguard Worker 
119*598139dcSAndroid Build Coastguard Worker     // We do not have access to the private variables in android_logger::Config, so we have to use
120*598139dcSAndroid Build Coastguard Worker     // the builder instead.
121*598139dcSAndroid Build Coastguard Worker     let mut builder = android_logger::Config::default();
122*598139dcSAndroid Build Coastguard Worker     if let Some(log_level) = config.log_level {
123*598139dcSAndroid Build Coastguard Worker         builder = builder.with_max_level(log_level);
124*598139dcSAndroid Build Coastguard Worker     }
125*598139dcSAndroid Build Coastguard Worker     if let Some(custom_format) = config.custom_format {
126*598139dcSAndroid Build Coastguard Worker         builder = builder.format(move |f, r| {
127*598139dcSAndroid Build Coastguard Worker             let formatted = custom_format(r);
128*598139dcSAndroid Build Coastguard Worker             write!(f, "{}", formatted)
129*598139dcSAndroid Build Coastguard Worker         });
130*598139dcSAndroid Build Coastguard Worker     }
131*598139dcSAndroid Build Coastguard Worker     if let Some(filter_str) = config.filter {
132*598139dcSAndroid Build Coastguard Worker         let filter = env_logger::filter::Builder::new().parse(filter_str).build();
133*598139dcSAndroid Build Coastguard Worker         builder = builder.with_filter(filter);
134*598139dcSAndroid Build Coastguard Worker     }
135*598139dcSAndroid Build Coastguard Worker     if let Some(tag) = config.tag {
136*598139dcSAndroid Build Coastguard Worker         builder = builder.with_tag(tag);
137*598139dcSAndroid Build Coastguard Worker     }
138*598139dcSAndroid Build Coastguard Worker 
139*598139dcSAndroid Build Coastguard Worker     android_logger::init_once(builder);
140*598139dcSAndroid Build Coastguard Worker     true
141*598139dcSAndroid Build Coastguard Worker }
142*598139dcSAndroid Build Coastguard Worker 
143*598139dcSAndroid Build Coastguard Worker /// Note that the majority of tests checking behavior are under the tests/ folder, as they all
144*598139dcSAndroid Build Coastguard Worker /// require independent initialization steps. The local test module just performs some basic crash
145*598139dcSAndroid Build Coastguard Worker /// testing without performing initialization.
146*598139dcSAndroid Build Coastguard Worker #[cfg(test)]
147*598139dcSAndroid Build Coastguard Worker mod tests {
148*598139dcSAndroid Build Coastguard Worker     use super::*;
149*598139dcSAndroid Build Coastguard Worker 
150*598139dcSAndroid Build Coastguard Worker     #[test]
test_with_max_level()151*598139dcSAndroid Build Coastguard Worker     fn test_with_max_level() {
152*598139dcSAndroid Build Coastguard Worker         let config = Config::default()
153*598139dcSAndroid Build Coastguard Worker             .with_max_level(log::LevelFilter::Trace)
154*598139dcSAndroid Build Coastguard Worker             .with_max_level(log::LevelFilter::Error);
155*598139dcSAndroid Build Coastguard Worker 
156*598139dcSAndroid Build Coastguard Worker         assert_eq!(config.log_level, Some(log::LevelFilter::Error));
157*598139dcSAndroid Build Coastguard Worker     }
158*598139dcSAndroid Build Coastguard Worker 
159*598139dcSAndroid Build Coastguard Worker     #[test]
test_with_filter()160*598139dcSAndroid Build Coastguard Worker     fn test_with_filter() {
161*598139dcSAndroid Build Coastguard Worker         let filter = "debug,hello::crate=trace";
162*598139dcSAndroid Build Coastguard Worker         let config = Config::default().with_filter(filter);
163*598139dcSAndroid Build Coastguard Worker 
164*598139dcSAndroid Build Coastguard Worker         assert_eq!(config.filter.unwrap(), filter)
165*598139dcSAndroid Build Coastguard Worker     }
166*598139dcSAndroid Build Coastguard Worker 
167*598139dcSAndroid Build Coastguard Worker     #[test]
test_with_tag_on_device()168*598139dcSAndroid Build Coastguard Worker     fn test_with_tag_on_device() {
169*598139dcSAndroid Build Coastguard Worker         let config = Config::default().with_tag_on_device("my_app");
170*598139dcSAndroid Build Coastguard Worker 
171*598139dcSAndroid Build Coastguard Worker         assert_eq!(config.tag.unwrap(), CString::new("my_app").unwrap());
172*598139dcSAndroid Build Coastguard Worker     }
173*598139dcSAndroid Build Coastguard Worker 
174*598139dcSAndroid Build Coastguard Worker     #[test]
test_format()175*598139dcSAndroid Build Coastguard Worker     fn test_format() {
176*598139dcSAndroid Build Coastguard Worker         let config = Config::default().format(|record| format!("my_app: {}", record.args()));
177*598139dcSAndroid Build Coastguard Worker 
178*598139dcSAndroid Build Coastguard Worker         assert!(config.custom_format.is_some());
179*598139dcSAndroid Build Coastguard Worker     }
180*598139dcSAndroid Build Coastguard Worker }
181