android_logd_logger/
logger.rs

1use crate::{thread, Buffer, Priority, Record, TagMode};
2use env_logger::filter::{Builder, Filter};
3use log::{LevelFilter, Log, Metadata};
4use parking_lot::RwLock;
5use std::{io, process, sync::Arc, time::SystemTime};
6
7/// Logger configuration.
8pub(crate) struct Configuration {
9    pub(crate) filter: Filter,
10    pub(crate) tag: TagMode,
11    pub(crate) prepend_module: bool,
12    #[allow(unused)]
13    pub(crate) pstore: bool,
14    pub(crate) buffer_id: Buffer,
15}
16
17/// Logger configuration handler stores access to logger configuration parameters.
18#[derive(Clone)]
19pub struct Logger {
20    pub(crate) configuration: Arc<RwLock<Configuration>>,
21}
22
23impl Logger {
24    /// Sets buffer parameter of logger configuration
25    ///
26    /// # Examples
27    ///
28    /// ```
29    /// # use log::LevelFilter;
30    /// # use android_logd_logger::{Builder, Buffer};
31    ///
32    /// let logger = android_logd_logger::builder().init();
33    ///
34    /// logger.buffer(Buffer::Crash);
35    /// ```
36    pub fn buffer(&self, buffer: Buffer) -> &Self {
37        self.configuration.write().buffer_id = buffer;
38        self
39    }
40
41    // Sets tag parameter of logger configuration to custom value
42    ///
43    /// # Examples
44    ///
45    /// ```
46    /// # use log::LevelFilter;
47    /// # use android_logd_logger::Builder;
48    ///
49    /// let logger = android_logd_logger::builder().init();
50    ///
51    /// logger.tag("foo");
52    /// ```
53    pub fn tag(&self, tag: &str) -> &Self {
54        self.configuration.write().tag = TagMode::Custom(tag.into());
55        self
56    }
57
58    /// Sets tag parameter of logger configuration to target value
59    ///
60    /// # Examples
61    ///
62    /// ```
63    /// # use log::LevelFilter;
64    /// # use android_logd_logger::Builder;
65    ///
66    /// let logger = android_logd_logger::builder().init();
67    ///
68    /// logger.tag_target();
69    /// ```
70    pub fn tag_target(&self) -> &Self {
71        self.configuration.write().tag = TagMode::Target;
72        self
73    }
74
75    /// Sets tag parameter of logger configuration to strip value
76    ///
77    /// # Examples
78    ///
79    /// ```
80    /// # use log::LevelFilter;
81    /// # use android_logd_logger::Builder;
82    ///
83    /// let logger = android_logd_logger::builder().init();
84    ///
85    /// logger.tag_target_strip();
86    /// ```
87    pub fn tag_target_strip(&self) -> &Self {
88        self.configuration.write().tag = TagMode::TargetStrip;
89        self
90    }
91
92    /// Sets prepend module parameter of logger configuration
93    ///
94    /// # Examples
95    ///
96    /// ```
97    /// # use log::LevelFilter;
98    /// # use android_logd_logger::Builder;
99    ///
100    /// let logger = android_logd_logger::builder().init();
101    ///
102    /// logger.prepend_module(true);
103    /// ```
104    pub fn prepend_module(&self, prepend_module: bool) -> &Self {
105        self.configuration.write().prepend_module = prepend_module;
106        self
107    }
108
109    /// Adds a directive to the filter for a specific module.
110    ///
111    /// # Examples
112    ///
113    /// Only include messages for warning and above for logs in `path::to::module`:
114    ///
115    /// ```
116    /// # use log::LevelFilter;
117    /// # use android_logd_logger::Builder;
118    ///
119    /// let logger = android_logd_logger::builder().init();
120    ///
121    /// logger.filter_module("path::to::module", LevelFilter::Info);
122    /// ```
123    pub fn filter_module(&self, module: &str, level: LevelFilter) -> &Self {
124        self.configuration.write().filter = Builder::default().filter_module(module, level).build();
125        self
126    }
127
128    /// Adjust filter.
129    ///
130    /// # Examples
131    ///
132    /// Only include messages for warning and above.
133    ///
134    /// ```
135    /// # use log::LevelFilter;
136    /// # use android_logd_logger::Builder;
137    ///
138    /// let logger = Builder::new().init();
139    /// logger.filter_level(LevelFilter::Info);
140    /// ```
141    pub fn filter_level(&self, level: LevelFilter) -> &Self {
142        self.configuration.write().filter = Builder::default().filter_level(level).build();
143        self
144    }
145
146    /// Adjust filter.
147    ///
148    /// The given module (if any) will log at most the specified level provided.
149    /// If no module is provided then the filter will apply to all log messages.
150    ///
151    /// # Examples
152    ///
153    /// Only include messages for warning and above for logs in `path::to::module`:
154    ///
155    /// ```
156    /// # use log::LevelFilter;
157    /// # use android_logd_logger::Builder;
158    ///
159    /// let logger = Builder::new().init();
160    /// logger.filter(Some("path::to::module"), LevelFilter::Info);
161    /// ```
162    pub fn filter(&self, module: Option<&str>, level: LevelFilter) -> &Self {
163        self.configuration.write().filter = Builder::default().filter(module, level).build();
164        self
165    }
166
167    /// Parses the directives string in the same form as the `RUST_LOG`
168    /// environment variable.
169    ///
170    /// See the module documentation for more details.
171    pub fn parse_filters(&mut self, filters: &str) -> &mut Self {
172        let filter = Builder::default().parse(filters).build();
173        log::set_max_level(filter.filter());
174        self.configuration.write().filter = filter;
175        self
176    }
177
178    /// Sets filter parameter of logger configuration
179    ///
180    /// # Examples
181    ///
182    /// ```
183    /// # use log::LevelFilter;
184    /// # use android_logd_logger::Builder;
185    ///
186    /// let logger = android_logd_logger::builder().init();
187    ///
188    /// logger.pstore(true);
189    /// ```
190    #[cfg(target_os = "android")]
191    pub fn pstore(&self, pstore: bool) -> &Self {
192        self.configuration.write().pstore = pstore;
193        self
194    }
195}
196
197/// Logger implementation.
198pub(crate) struct LoggerImpl {
199    configuration: Arc<RwLock<Configuration>>,
200}
201
202impl LoggerImpl {
203    pub fn new(configuration: Arc<RwLock<Configuration>>) -> Result<LoggerImpl, io::Error> {
204        Ok(LoggerImpl { configuration })
205    }
206}
207
208impl Log for LoggerImpl {
209    fn enabled(&self, metadata: &Metadata) -> bool {
210        self.configuration.read().filter.enabled(metadata)
211    }
212
213    fn log(&self, record: &log::Record) {
214        let configuration = self.configuration.read();
215
216        if !configuration.filter.matches(record) {
217            return;
218        }
219
220        let args = record.args().to_string();
221        let message = if let Some(module_path) = record.module_path() {
222            if configuration.prepend_module {
223                [module_path, &args].join(": ")
224            } else {
225                args
226            }
227        } else {
228            args
229        };
230
231        let priority: Priority = record.metadata().level().into();
232        let tag = match &configuration.tag {
233            TagMode::Target => record.target(),
234            TagMode::TargetStrip => record
235                .target()
236                .split_once("::")
237                .map(|(tag, _)| tag)
238                .unwrap_or_else(|| record.target()),
239            TagMode::Custom(tag) => tag.as_str(),
240        };
241
242        let timestamp = SystemTime::now();
243        let record = Record {
244            timestamp,
245            pid: process::id() as u16,
246            thread_id: thread::id() as u16,
247            buffer_id: configuration.buffer_id,
248            tag,
249            priority,
250            message: &message,
251        };
252
253        crate::log_record(&record).ok();
254
255        #[cfg(target_os = "android")]
256        {
257            if configuration.pstore {
258                crate::pmsg::log(&record);
259            }
260        }
261    }
262
263    #[cfg(not(target_os = "android"))]
264    fn flush(&self) {
265        use std::io::Write;
266        io::stderr().flush().ok();
267    }
268
269    #[cfg(target_os = "android")]
270    fn flush(&self) {
271        if self.configuration.read().pstore {
272            crate::pmsg::flush().ok();
273        }
274    }
275}