android-logd-logger 0.5.0

A logging implementation for `log` which directly writes to the Android logd daemon
Documentation
//! Android event logging support.
//!
//! This module provides functionality for writing structured binary events to Android's
//! event log buffer. Events consist of a tag (numeric identifier) and a value that can
//! be a primitive type, string, or list of values.

use bytes::{BufMut, Bytes, BytesMut};
use std::{iter::FromIterator, time::SystemTime};

use crate::{Buffer, Error, LOGGER_ENTRY_MAX_LEN};

/// Event tag identifier.
///
/// A numeric identifier used to categorize events. Event tags are typically
/// defined in Android's event-log-tags files.
pub type EventTag = u32;

/// An event log entry.
///
/// Events are structured log entries that consist of a timestamp, a numeric tag,
/// and a value. They are written to Android's event log buffer and can be viewed
/// with `logcat -b events`.
#[derive(Debug, Clone, PartialEq)]
pub struct Event {
    /// Timestamp
    pub timestamp: SystemTime,
    /// Tag
    pub tag: EventTag,
    /// Value
    pub value: EventValue,
}

/// The value payload of an event.
///
/// Event values can be primitives (int, long, float), strings, or lists of values.
/// This allows for structured logging of complex data.
///
/// # Examples
///
/// ```
/// use android_logd_logger::EventValue;
///
/// // Simple values
/// let int_val: EventValue = 42.into();
/// let str_val: EventValue = "hello".into();
/// let float_val: EventValue = 3.14.into();
///
/// // List of values
/// let list_val: EventValue = vec![
///     EventValue::Int(1),
///     EventValue::String("test".to_string()),
///     EventValue::Float(2.5)
/// ].into();
/// ```
#[derive(Debug, PartialEq, Clone)]
pub enum EventValue {
    /// Void value
    Void,
    /// Int value
    Int(i32),
    /// Long value
    Long(i64),
    /// Float value
    Float(f32),
    /// String value
    String(String),
    /// List of values
    List(Vec<EventValue>),
}

impl EventValue {
    /// Returns the serialized size of this value in bytes.
    ///
    /// This includes the type tag and the value data.
    pub fn serialized_size(&self) -> usize {
        match self {
            &EventValue::Void => 0,
            EventValue::Int(_) | EventValue::Float(_) => 1 + 4,
            EventValue::Long(_) => 1 + 8,
            EventValue::String(s) => 1 + 4 + s.len(),
            EventValue::List(l) => 1 + 1 + l.iter().map(EventValue::serialized_size).sum::<usize>(),
        }
    }

    /// Serializes the event value into bytes.
    ///
    /// The serialization format follows Android's event log binary format,
    /// with a type tag followed by the value data.
    pub fn as_bytes(&self) -> Bytes {
        const EVENT_TYPE_INT: u8 = 0;
        const EVENT_TYPE_LONG: u8 = 1;
        const EVENT_TYPE_STRING: u8 = 2;
        const EVENT_TYPE_LIST: u8 = 3;
        const EVENT_TYPE_FLOAT: u8 = 4;

        let mut buffer = BytesMut::with_capacity(self.serialized_size());
        match self {
            EventValue::Void => (),
            EventValue::Int(num) => {
                buffer.put_u8(EVENT_TYPE_INT);
                buffer.put_i32_le(*num);
            }
            EventValue::Long(num) => {
                buffer.put_u8(EVENT_TYPE_LONG);
                buffer.put_i64_le(*num);
            }
            EventValue::Float(num) => {
                buffer.put_u8(EVENT_TYPE_FLOAT);
                buffer.put_f32_le(*num);
            }
            EventValue::String(string) => {
                buffer.put_u8(EVENT_TYPE_STRING);
                buffer.put_u32_le(string.len() as u32);
                buffer.put(string.as_bytes());
            }
            EventValue::List(values) => {
                buffer.put_u8(EVENT_TYPE_LIST);
                buffer.put_u8(values.len() as u8);
                values.iter().for_each(|value| buffer.put(value.as_bytes()));
            }
        };
        buffer.freeze()
    }
}

impl From<()> for EventValue {
    fn from(_: ()) -> Self {
        EventValue::Void
    }
}

impl From<i32> for EventValue {
    fn from(v: i32) -> Self {
        EventValue::Int(v)
    }
}

impl From<i64> for EventValue {
    fn from(v: i64) -> Self {
        EventValue::Long(v)
    }
}

impl From<f32> for EventValue {
    fn from(v: f32) -> Self {
        EventValue::Float(v)
    }
}

impl From<&str> for EventValue {
    fn from(v: &str) -> Self {
        EventValue::String(v.to_string())
    }
}

impl<T> FromIterator<T> for EventValue
where
    T: Into<EventValue>,
{
    fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Self {
        EventValue::List(iter.into_iter().map(Into::into).collect())
    }
}

impl<T> From<Vec<T>> for EventValue
where
    T: Into<EventValue>,
{
    fn from(mut v: Vec<T>) -> Self {
        EventValue::List(v.drain(..).map(|e| e.into()).collect())
    }
}

impl<T, U> From<(T, U)> for EventValue
where
    T: Into<EventValue>,
    U: Into<EventValue>,
{
    fn from(value: (T, U)) -> Self {
        EventValue::List(vec![value.0.into(), value.1.into()])
    }
}

impl<T, U, V> From<(T, U, V)> for EventValue
where
    T: Into<EventValue>,
    U: Into<EventValue>,
    V: Into<EventValue>,
{
    fn from(value: (T, U, V)) -> Self {
        EventValue::List(vec![value.0.into(), value.1.into(), value.2.into()])
    }
}

impl<T, U, V, X> From<(T, U, V, X)> for EventValue
where
    T: Into<EventValue>,
    U: Into<EventValue>,
    V: Into<EventValue>,
    X: Into<EventValue>,
{
    fn from(value: (T, U, V, X)) -> Self {
        EventValue::List(vec![value.0.into(), value.1.into(), value.2.into(), value.3.into()])
    }
}

impl<T, U, V, X, Y> From<(T, U, V, X, Y)> for EventValue
where
    T: Into<EventValue>,
    U: Into<EventValue>,
    V: Into<EventValue>,
    X: Into<EventValue>,
    Y: Into<EventValue>,
{
    fn from(value: (T, U, V, X, Y)) -> Self {
        EventValue::List(vec![
            value.0.into(),
            value.1.into(),
            value.2.into(),
            value.3.into(),
            value.4.into(),
        ])
    }
}

/// Writes an event with the current timestamp to the events buffer.
///
/// This is a convenience function that creates an event with the current system
/// time and writes it to `Buffer::Events`.
///
/// # Parameters
///
/// - `tag`: The event tag identifier
/// - `value`: The event value (can be any type that converts to `EventValue`)
///
/// # Errors
///
/// Returns an error if the event data exceeds the maximum size.
///
/// # Examples
///
/// ```
/// use android_logd_logger::{write_event, write_event_now, Error, Event, EventValue};
/// android_logd_logger::builder().init();
///
/// write_event_now(1, "test").unwrap();
///
/// let value: Vec<EventValue> = vec![1.into(), "one".into(), 123.3.into()].into();
/// write_event_now(2, value).unwrap();
/// ```
pub fn write_event_now<T: Into<EventValue>>(tag: EventTag, value: T) -> Result<(), Error> {
    write_event(&Event {
        timestamp: SystemTime::now(),
        tag,
        value: value.into(),
    })
}

/// Writes an event with the current timestamp to a specific buffer.
///
/// This is a convenience function that creates an event with the current system
/// time and writes it to the specified buffer.
///
/// # Parameters
///
/// - `log_buffer`: The target log buffer
/// - `tag`: The event tag identifier
/// - `value`: The event value (can be any type that converts to `EventValue`)
///
/// # Errors
///
/// Returns an error if the event data exceeds the maximum size.
///
/// # Examples
///
/// ```
/// use android_logd_logger::{write_event_buffer_now, Buffer, Error, Event, EventValue};
/// android_logd_logger::builder().init();
///
/// write_event_buffer_now(Buffer::Stats, 1, "test").unwrap();
///
/// let value: Vec<EventValue> = vec![1.into(), "one".into(), 123.3.into()].into();
/// write_event_buffer_now(Buffer::Stats, 2, value).unwrap();
/// ```
pub fn write_event_buffer_now<T: Into<EventValue>>(log_buffer: Buffer, tag: EventTag, value: T) -> Result<(), Error> {
    write_event_buffer(
        log_buffer,
        &Event {
            timestamp: SystemTime::now(),
            tag,
            value: value.into(),
        },
    )
}

/// Writes an event to the events buffer.
///
/// # Parameters
///
/// - `event`: The event to write
///
/// # Errors
///
/// Returns an error if the event data exceeds the maximum size.
///
/// # Examples
///
/// ```
/// use android_logd_logger::{write_event, Error, Event, EventValue};
/// android_logd_logger::builder().init();
///
/// write_event(&Event {
///     timestamp: std::time::SystemTime::now(),
///     tag: 1,
///     value: "blah".into(),
/// }).unwrap();
/// ```
pub fn write_event(event: &Event) -> Result<(), Error> {
    write_event_buffer(Buffer::Events, event)
}

/// Writes an event to a specific buffer.
///
/// # Parameters
///
/// - `log_buffer`: The target log buffer
/// - `event`: The event to write
///
/// # Errors
///
/// Returns an error if the event data exceeds the maximum size.
///
/// # Examples
///
/// ```
/// use android_logd_logger::{write_event_buffer, Buffer, Error, Event, EventValue};
/// android_logd_logger::builder().init();
///
/// write_event_buffer(Buffer::Stats, &Event {
///     timestamp: std::time::SystemTime::now(),
///     tag: 1,
///     value: "blah".into(),
/// }).unwrap();
/// ```
pub fn write_event_buffer(log_buffer: Buffer, event: &Event) -> Result<(), Error> {
    if event.value.serialized_size() > (LOGGER_ENTRY_MAX_LEN - 1 - 2 - 4 - 4 - 4) {
        return Err(Error::EventSize);
    }

    #[cfg(target_os = "android")]
    crate::logd::write_event(log_buffer, event);

    #[cfg(not(target_os = "android"))]
    println!("buffer: {:?}, event: {:?}", log_buffer, event);

    Ok(())
}