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