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}