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}