android_logd_logger/lib.rs
1//! A pure Rust logger for Android's `logd` logging system.
2//!
3//! This crate provides a logger implementation that writes directly to Android's `logd` socket,
4//! bypassing the need for `liblog` or any other native Android libraries. On non-Android platforms,
5//! logs are printed to stderr in a format similar to `logcat`.
6//!
7//! # Features
8//!
9//! - **Pure Rust**: No FFI or native dependencies required
10//! - **Direct socket communication**: Writes directly to the `logd` socket
11//! - **Multiple log buffers**: Support for main, radio, events, system, crash, stats, and security buffers
12//! - **Event logging**: Write structured events to Android's event log
13//! - **Persistent logging**: Optional logging to pstore (survives reboots on Android)
14//! - **Runtime configuration**: Adjust log levels, tags, and filters after initialization
15//! - **Cross-platform**: Works on Android and falls back to stderr on other platforms
16//!
17//! # Quick Start
18//!
19//! ```
20//! use log::{debug, error, info, trace, warn};
21//!
22//! fn main() {
23//! android_logd_logger::builder()
24//! .parse_filters("debug")
25//! .tag("MyApp")
26//! .prepend_module(true)
27//! .init();
28//!
29//! trace!("trace message: is not logged");
30//! debug!("debug message");
31//! info!("info message");
32//! warn!("warn message");
33//! error!("error message");
34//! }
35//! ```
36//!
37//! # Runtime Configuration
38//!
39//! The logger can be reconfigured at runtime using the [`Logger`] handle:
40//!
41//! ```
42//! use log::LevelFilter;
43//!
44//! let logger = android_logd_logger::builder().init();
45//!
46//! // Change the tag at runtime
47//! logger.tag("NewTag");
48//!
49//! // Adjust log levels
50//! logger.filter_level(LevelFilter::Warn);
51//! ```
52//!
53//! # Event Logging
54//!
55//! Android's event log can be used for structured logging:
56//!
57//! ```
58//! use android_logd_logger::{write_event_now, EventValue};
59//!
60//! // Simple event
61//! write_event_now(1, "test").unwrap();
62//!
63//! // Complex event with multiple values
64//! let value: Vec<EventValue> = vec![1.into(), "one".into(), 123.3.into()];
65//! write_event_now(2, value).unwrap();
66//! ```
67
68#![deny(missing_docs)]
69
70use env_logger::filter::Builder as FilterBuilder;
71use log::{set_boxed_logger, LevelFilter, SetLoggerError};
72use logger::Configuration;
73use parking_lot::RwLock;
74use std::{fmt, io, sync::Arc, time::SystemTime};
75use thiserror::Error;
76
77mod events;
78#[allow(dead_code)]
79#[cfg(not(target_os = "windows"))]
80mod logd;
81mod logger;
82#[cfg(target_os = "android")]
83mod logging_iterator;
84#[cfg(target_os = "android")]
85mod pmsg;
86mod thread;
87
88pub use events::*;
89
90/// Logger configuration handle.
91pub use logger::Logger;
92
93/// Maximum log entry length in bytes (5KB).
94const LOGGER_ENTRY_MAX_LEN: usize = 5 * 1024;
95
96/// Errors that can occur when logging.
97#[derive(Error, Debug)]
98pub enum Error {
99 /// IO error
100 #[error("IO error")]
101 Io(#[from] io::Error),
102 /// The supplied event data exceed the maximum length
103 #[error("Event exceeds maximum size")]
104 EventSize,
105 /// Timestamp error
106 #[error("Timestamp error: {0}")]
107 Timestamp(String),
108}
109
110/// Log priority levels as defined by Android's logd.
111///
112/// These priority levels correspond to Android's logging levels and are used
113/// to categorize log messages by severity. The standard Rust log levels are
114/// automatically mapped to these Android priorities.
115///
116/// # Mapping from Rust log levels
117///
118/// - `log::Level::Error` → `Priority::Error`
119/// - `log::Level::Warn` → `Priority::Warn`
120/// - `log::Level::Info` → `Priority::Info`
121/// - `log::Level::Debug` → `Priority::Debug`
122/// - `log::Level::Trace` → `Priority::Verbose`
123#[derive(Clone, Copy, Debug)]
124#[repr(u8)]
125pub enum Priority {
126 /// Unknown priority (internal use only, not for application use).
127 _Unknown = 0,
128
129 /// Default priority (internal use only, not for application use).
130 _Default = 1,
131
132 /// Verbose log level - detailed diagnostic information.
133 Verbose = 2,
134
135 /// Debug log level - debugging information useful during development.
136 Debug = 3,
137
138 /// Info log level - informational messages about normal operation.
139 Info = 4,
140
141 /// Warning log level - warning messages about potential issues.
142 Warn = 5,
143
144 /// Error log level - error messages about failures.
145 Error = 6,
146
147 /// Fatal log level (internal use only, not for application use).
148 _Fatal = 7,
149
150 /// Silent priority (internal use only, not for application use).
151 _Silent = 8,
152}
153
154impl std::fmt::Display for Priority {
155 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
156 let c = match self {
157 Priority::_Unknown => 'U',
158 Priority::_Default | Priority::Debug => 'D',
159 Priority::Verbose => 'V',
160 Priority::Info => 'I',
161 Priority::Warn => 'W',
162 Priority::Error => 'E',
163 Priority::_Fatal => 'F',
164 Priority::_Silent => 'S',
165 };
166 f.write_str(&c.to_string())
167 }
168}
169
170impl From<log::Level> for Priority {
171 fn from(l: log::Level) -> Priority {
172 match l {
173 log::Level::Error => Priority::Error,
174 log::Level::Warn => Priority::Warn,
175 log::Level::Info => Priority::Info,
176 log::Level::Debug => Priority::Debug,
177 log::Level::Trace => Priority::Verbose,
178 }
179 }
180}
181
182/// Android log buffer identifiers.
183///
184/// Android maintains multiple ring buffers for different types of logs.
185/// Most applications should use [`Buffer::Main`], which is the standard
186/// application log buffer.
187///
188/// # Buffer Types
189///
190/// - **Main**: Standard application logs (default)
191/// - **Radio**: Radio/telephony related logs (system use)
192/// - **Events**: Binary event logs for system events
193/// - **System**: System component logs
194/// - **Crash**: Crash logs
195/// - **Stats**: Statistics logs
196/// - **Security**: Security-related logs
197/// - **Custom**: User-defined buffer ID
198#[derive(Clone, Copy, Debug)]
199#[repr(u8)]
200pub enum Buffer {
201 /// The main log buffer. This is the default and only buffer available to regular apps.
202 Main,
203 /// The radio log buffer for telephony-related logs (typically system use only).
204 Radio,
205 /// The event log buffer for structured binary events.
206 Events,
207 /// The system log buffer for system component logs.
208 System,
209 /// The crash log buffer for crash reports.
210 Crash,
211 /// The statistics log buffer for system statistics.
212 Stats,
213 /// The security log buffer for security-related events.
214 Security,
215 /// A custom buffer with a user-defined ID.
216 Custom(u8),
217}
218
219impl From<Buffer> for u8 {
220 fn from(b: Buffer) -> u8 {
221 match b {
222 Buffer::Main => 0,
223 Buffer::Radio => 1,
224 Buffer::Events => 2,
225 Buffer::System => 3,
226 Buffer::Crash => 4,
227 Buffer::Stats => 5,
228 Buffer::Security => 6,
229 Buffer::Custom(id) => id,
230 }
231 }
232}
233
234/// Internal tag mode configuration.
235///
236/// Determines how log tags are generated from log records.
237#[derive(Debug, Default, Clone)]
238enum TagMode {
239 /// Use the record's target metadata as the tag (full module path).
240 Target,
241 /// Use the root module as tag by stripping everything after the first `::`.
242 /// For example, `crate::module::submodule` becomes `crate`.
243 #[default]
244 TargetStrip,
245 /// Use a custom fixed tag string for all log messages.
246 Custom(String),
247}
248
249/// Internal logging record structure.
250///
251/// This structure is built once per log call and contains all the information
252/// needed to write to both `logd` and `pmsg` devices. By building it once,
253/// we ensure consistent timestamps and avoid duplicate system calls.
254struct Record<'tag, 'msg> {
255 /// Timestamp when the log was created.
256 timestamp: SystemTime,
257 /// Process ID.
258 pid: u16,
259 /// Thread ID.
260 thread_id: u16,
261 /// Target log buffer.
262 buffer_id: Buffer,
263 /// Log tag string.
264 tag: &'tag str,
265 /// Log priority level.
266 priority: Priority,
267 /// Log message content.
268 message: &'msg str,
269}
270
271/// Returns a default [`Builder`] for configuration and initialization of logging.
272///
273/// With the help of the [`Builder`] the logging is configured.
274/// The tag, filter and buffer can be set.
275/// Additionally it is possible to set whether the modul path appears in a log message.
276///
277/// After a call to [`init`](Builder::init) the global logger is initialized with the configuration.
278pub fn builder() -> Builder {
279 Builder::default()
280}
281
282/// Builder for initializing the logger.
283///
284/// The builder provides a fluent API for configuring the logger before initialization.
285/// It allows setting the log tag, filters, buffer, and other options.
286///
287/// # Examples
288///
289/// ```
290/// use log::LevelFilter;
291/// use android_logd_logger::Builder;
292///
293/// let logger = Builder::new()
294/// .tag("MyApp")
295/// .filter_level(LevelFilter::Debug)
296/// .prepend_module(true)
297/// .init();
298/// ```
299pub struct Builder {
300 filter: FilterBuilder,
301 tag: TagMode,
302 prepend_module: bool,
303 pstore: bool,
304 buffer: Option<Buffer>,
305}
306
307impl Default for Builder {
308 fn default() -> Self {
309 Self {
310 filter: FilterBuilder::default(),
311 tag: TagMode::default(),
312 prepend_module: false,
313 pstore: true,
314 buffer: None,
315 }
316 }
317}
318
319impl Builder {
320 /// Initializes the log builder with defaults.
321 ///
322 /// # Examples
323 ///
324 /// Create a new builder and configure filters and style:
325 ///
326 /// ```
327 /// # use log::LevelFilter;
328 /// # use android_logd_logger::Builder;
329 ///
330 /// let mut builder = Builder::new();
331 /// builder.filter(None, LevelFilter::Info).init();
332 /// ```
333 ///
334 /// [`filter`]: #method.filter
335 pub fn new() -> Builder {
336 Builder::default()
337 }
338
339 /// Use a specific android log buffer. Defaults to the main buffer
340 /// is used as tag (if present).
341 ///
342 /// # Examples
343 ///
344 /// ```
345 /// # use android_logd_logger::Builder;
346 /// # use android_logd_logger::Buffer;
347 ///
348 /// let mut builder = Builder::new();
349 /// builder.buffer(Buffer::Crash)
350 /// .init();
351 /// ```
352 pub fn buffer(&mut self, buffer: Buffer) -> &mut Self {
353 self.buffer = Some(buffer);
354 self
355 }
356
357 /// Use a specific log tag. If no tag is set the module path
358 /// is used as tag (if present).
359 ///
360 /// # Examples
361 ///
362 /// ```
363 /// # use android_logd_logger::Builder;
364 ///
365 /// let mut builder = Builder::new();
366 ///
367 /// builder.tag("foo")
368 /// .init();
369 /// ```
370 pub fn tag(&mut self, tag: &str) -> &mut Self {
371 self.tag = TagMode::Custom(tag.to_string());
372 self
373 }
374
375 /// Use the target string as tag
376 ///
377 /// # Examples
378 ///
379 /// ```
380 /// # use android_logd_logger::Builder;
381 ///
382 /// let mut builder = Builder::new();
383 /// builder.tag_target().init();
384 /// ```
385 pub fn tag_target(&mut self) -> &mut Self {
386 self.tag = TagMode::Target;
387 self
388 }
389
390 /// Use the target string as tag and strip off ::.*
391 ///
392 /// # Examples
393 ///
394 /// ```
395 /// # use android_logd_logger::Builder;
396 ///
397 /// let mut builder = Builder::new();
398 /// builder.tag_target_strip().init();
399 /// ```
400 pub fn tag_target_strip(&mut self) -> &mut Self {
401 self.tag = TagMode::TargetStrip;
402 self
403 }
404
405 /// Prepend module to log message.
406 ///
407 /// If set true the Rust module path is prepended to the log message.
408 ///
409 /// # Examples
410 ///
411 /// ```
412 /// # use android_logd_logger::Builder;
413 ///
414 /// let mut builder = Builder::new();
415 /// builder.prepend_module(true).init();
416 /// ```
417 pub fn prepend_module(&mut self, prepend_module: bool) -> &mut Self {
418 self.prepend_module = prepend_module;
419 self
420 }
421
422 /// Adds a directive to the filter for a specific module.
423 ///
424 /// # Examples
425 ///
426 /// Only include messages for warning and above for logs in `path::to::module`:
427 ///
428 /// ```
429 /// # use log::LevelFilter;
430 /// # use android_logd_logger::Builder;
431 ///
432 /// let mut builder = Builder::new();
433 ///
434 /// builder.filter_module("path::to::module", LevelFilter::Info).init();
435 /// ```
436 pub fn filter_module(&mut self, module: &str, level: LevelFilter) -> &mut Self {
437 self.filter.filter_module(module, level);
438 self
439 }
440
441 /// Adds a directive to the filter for all modules.
442 ///
443 /// # Examples
444 ///
445 /// Only include messages for warning and above.
446 ///
447 /// ```
448 /// # use log::LevelFilter;
449 /// # use android_logd_logger::Builder;
450 ///
451 /// let mut builder = Builder::new();
452 /// builder.filter_level(LevelFilter::Info).init();
453 /// ```
454 pub fn filter_level(&mut self, level: LevelFilter) -> &mut Self {
455 self.filter.filter_level(level);
456 self
457 }
458
459 /// Adds filters to the logger.
460 ///
461 /// The given module (if any) will log at most the specified level provided.
462 /// If no module is provided then the filter will apply to all log messages.
463 ///
464 /// # Examples
465 ///
466 /// Only include messages for warning and above for logs in `path::to::module`:
467 ///
468 /// ```
469 /// # use log::LevelFilter;
470 /// # use android_logd_logger::Builder;
471 ///
472 /// let mut builder = Builder::new();
473 /// builder.filter(Some("path::to::module"), LevelFilter::Info).init();
474 /// ```
475 pub fn filter(&mut self, module: Option<&str>, level: LevelFilter) -> &mut Self {
476 self.filter.filter(module, level);
477 self
478 }
479
480 /// Parses the directives string in the same form as the `RUST_LOG`
481 /// environment variable.
482 ///
483 /// See the module documentation for more details.
484 pub fn parse_filters(&mut self, filters: &str) -> &mut Self {
485 self.filter.parse(filters);
486 self
487 }
488
489 /// Enables or disables logging to the pstore filesystem.
490 ///
491 /// Messages logged to the pstore filesystem survive a reboot but not a
492 /// power cycle. By default, logging to the pstore is enabled.
493 #[cfg(target_os = "android")]
494 pub fn pstore(&mut self, log_to_pstore: bool) -> &mut Self {
495 self.pstore = log_to_pstore;
496 self
497 }
498
499 /// Initializes the global logger with the built logd logger.
500 ///
501 /// This should be called early in the execution of a Rust program. Any log
502 /// events that occur before initialization will be ignored.
503 ///
504 /// # Errors
505 ///
506 /// This function will fail if it is called more than once, or if another
507 /// library has already initialized a global logger.
508 pub fn try_init(&mut self) -> Result<Logger, SetLoggerError> {
509 let configuration = Configuration {
510 filter: self.filter.build(),
511 tag: self.tag.clone(),
512 prepend_module: self.prepend_module,
513 pstore: self.pstore,
514 buffer_id: self.buffer.unwrap_or(Buffer::Main),
515 };
516 let max_level = configuration.filter.filter();
517 let configuration = Arc::new(RwLock::new(configuration));
518
519 let logger = Logger {
520 configuration: configuration.clone(),
521 };
522 let logger_impl = logger::LoggerImpl::new(configuration).expect("failed to build logger");
523
524 set_boxed_logger(Box::new(logger_impl))
525 .map(|_| {
526 log::set_max_level(max_level);
527 })
528 .map(|_| logger)
529 }
530
531 /// Initializes the global logger with the built logger.
532 ///
533 /// This should be called early in the execution of a Rust program. Any log
534 /// events that occur before initialization will be ignored.
535 ///
536 /// # Panics
537 ///
538 /// This function will panic if it is called more than once, or if another
539 /// library has already initialized a global logger.
540 pub fn init(&mut self) -> Logger {
541 self.try_init()
542 .expect("Builder::init should not be called after logger initialized")
543 }
544}
545
546/// Construct and send a log entry directly to the logd socket.
547///
548/// This function allows you to create custom log entries with explicit control
549/// over all parameters including timestamp, buffer, priority, process/thread IDs,
550/// tag, and message. This is useful for forwarding logs from other sources or
551/// creating synthetic log entries.
552///
553/// On Android, this writes directly to the logd socket. On other platforms,
554/// it prints to stderr in logcat format.
555///
556/// # Parameters
557///
558/// - `timestamp`: The timestamp for the log entry
559/// - `buffer_id`: The target log buffer
560/// - `priority`: The log priority level
561/// - `pid`: Process ID to associate with the log
562/// - `thread_id`: Thread ID to associate with the log
563/// - `tag`: The log tag string
564/// - `message`: The log message content
565///
566/// # Errors
567///
568/// Returns an error if the log entry cannot be written.
569///
570/// # Example
571///
572/// ```
573/// # use android_logd_logger::{Buffer, Priority};
574/// # use std::time::SystemTime;
575///
576/// android_logd_logger::log(SystemTime::now(), Buffer::Main, Priority::Info, 0, 0, "tag", "message").unwrap();
577/// ```
578#[cfg(target_os = "android")]
579pub fn log(
580 timestamp: SystemTime,
581 buffer_id: Buffer,
582 priority: Priority,
583 pid: u16,
584 thread_id: u16,
585 tag: &str,
586 message: &str,
587) -> Result<(), Error> {
588 let record = Record {
589 timestamp,
590 pid,
591 thread_id,
592 buffer_id,
593 tag,
594 priority,
595 message,
596 };
597
598 logd::log(&record);
599
600 Ok(())
601}
602
603/// Construct and send a log entry (non-Android platforms).
604///
605/// This function allows you to create custom log entries with explicit control
606/// over all parameters. On non-Android platforms, it prints to stderr in logcat format.
607///
608/// # Parameters
609///
610/// - `timestamp`: The timestamp for the log entry
611/// - `buffer_id`: The target log buffer
612/// - `priority`: The log priority level
613/// - `pid`: Process ID to associate with the log
614/// - `thread_id`: Thread ID to associate with the log
615/// - `tag`: The log tag string
616/// - `message`: The log message content
617///
618/// # Errors
619///
620/// Returns an error if the log entry cannot be formatted or written.
621///
622/// # Example
623///
624/// ```
625/// # use android_logd_logger::{Buffer, Priority};
626/// # use std::time::SystemTime;
627///
628/// android_logd_logger::log(SystemTime::now(), Buffer::Main, Priority::Info, 0, 0, "tag", "message").unwrap();
629/// ```
630#[cfg(not(target_os = "android"))]
631pub fn log(
632 timestamp: SystemTime,
633 buffer_id: Buffer,
634 priority: Priority,
635 pid: u16,
636 thread_id: u16,
637 tag: &str,
638 message: &str,
639) -> Result<(), Error> {
640 let record = Record {
641 timestamp,
642 pid,
643 thread_id,
644 buffer_id,
645 tag,
646 priority,
647 message,
648 };
649
650 log_record(&record)
651}
652
653#[cfg(target_os = "android")]
654fn log_record(record: &Record) -> Result<(), Error> {
655 logd::log(record);
656 Ok(())
657}
658
659#[cfg(not(target_os = "android"))]
660fn log_record(record: &Record) -> Result<(), Error> {
661 use std::time::UNIX_EPOCH;
662
663 const DATE_TIME_FORMAT: &[time::format_description::FormatItem<'_>] =
664 time::macros::format_description!("[year]-[month]-[day] [hour]:[minute]:[second].[subsecond digits:3]");
665
666 let Record {
667 timestamp,
668 tag,
669 priority,
670 message,
671 thread_id,
672 pid,
673 ..
674 } = record;
675
676 let timestamp = timestamp
677 .duration_since(UNIX_EPOCH)
678 .map_err(|e| Error::Timestamp(e.to_string()))
679 .and_then(|ts| {
680 time::OffsetDateTime::from_unix_timestamp_nanos(ts.as_nanos() as i128).map_err(|e| Error::Timestamp(e.to_string()))
681 })
682 .and_then(|ts| ts.format(&DATE_TIME_FORMAT).map_err(|e| Error::Timestamp(e.to_string())))?;
683
684 eprintln!("{} {} {} {} {}: {}", timestamp, pid, thread_id, priority, tag, message);
685 Ok(())
686}