Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Split things into different files, to optimize future work #85

Merged
merged 2 commits into from
Mar 4, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions rustfmt.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
style_edition = "2024"
48 changes: 48 additions & 0 deletions src/arrays.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
use crate::LOGGING_TAG_MAX_LEN;
use std::ffi::CStr;
use std::mem::MaybeUninit;

// FIXME: When `maybe_uninit_uninit_array` is stabilized, use it instead of this helper
pub fn uninit_array<const N: usize, T>() -> [MaybeUninit<T>; N] {
// SAFETY: Array contains MaybeUninit, which is fine to be uninit
unsafe { MaybeUninit::uninit().assume_init() }
}

// FIXME: Remove when maybe_uninit_slice is stabilized to provide MaybeUninit::slice_assume_init_ref()
pub unsafe fn slice_assume_init_ref<T>(slice: &[MaybeUninit<T>]) -> &[T] {
&*(slice as *const [MaybeUninit<T>] as *const [T])
}

/// Fills up `storage` with `tag` and a necessary NUL terminator, optionally ellipsizing the input
/// `tag` if it's too large.
///
/// Returns a [`CStr`] containing the initialized portion of `storage`, including its NUL
/// terminator.
pub fn fill_tag_bytes<'a>(
storage: &'a mut [MaybeUninit<u8>; LOGGING_TAG_MAX_LEN + 1],
tag: &[u8],
) -> &'a CStr {
// FIXME: Simplify when maybe_uninit_fill with MaybeUninit::fill_from() is stabilized
let initialized = if tag.len() > LOGGING_TAG_MAX_LEN {
for (input, output) in tag
.iter()
// Elipsize the last two characters (TODO: use special … character)?
.take(LOGGING_TAG_MAX_LEN - 2)
.chain(b"..\0")
.zip(storage.iter_mut())
{
output.write(*input);
}
storage.as_slice()
} else {
for (input, output) in tag.iter().chain(b"\0").zip(storage.iter_mut()) {
output.write(*input);
}
&storage[..tag.len() + 1]
};

// SAFETY: The above code ensures that `initialized` only refers to a portion of the `array`
// slice that was initialized, thus it is safe to cast those `MaybeUninit<u8>`s to `u8`:
let initialized = unsafe { slice_assume_init_ref(initialized) };
CStr::from_bytes_with_nul(initialized).expect("Unreachable: we wrote a nul terminator")
}
95 changes: 95 additions & 0 deletions src/config.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
use crate::{FormatFn, LogId};
use log::{Level, LevelFilter, Record};
use std::ffi::CString;
use std::fmt;

/// Filter for android logger.
#[derive(Default)]
pub struct Config {
pub(crate) log_level: Option<LevelFilter>,
pub(crate) buf_id: Option<LogId>,
filter: Option<env_filter::Filter>,
pub(crate) tag: Option<CString>,
pub(crate) custom_format: Option<FormatFn>,
}

impl fmt::Debug for Config {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Config")
.field("log_level", &self.log_level)
.field("buf_id", &self.buf_id)
.field("filter", &self.filter)
.field("tag", &self.tag)
.field("custom_format", match &self.custom_format {
Some(_) => &"Some(_)",
None => &"None",
})
.finish()
}
}

impl Config {
/// Changes the maximum log level.
///
/// Note, that `Trace` is the maximum level, because it provides the
/// maximum amount of detail in the emitted logs.
///
/// If `Off` level is provided, then nothing is logged at all.
///
/// [`log::max_level()`] is considered as the default level.
pub fn with_max_level(mut self, level: LevelFilter) -> Self {
self.log_level = Some(level);
self
}

/// Changes the Android logging system buffer to be used.
///
/// By default, logs are sent to the [`Main`] log. Other logging buffers may
/// only be accessible to certain processes.
///
/// [`Main`]: LogId::Main
pub fn with_log_buffer(mut self, buf_id: LogId) -> Self {
self.buf_id = Some(buf_id);
self
}

pub(crate) fn filter_matches(&self, record: &Record) -> bool {
if let Some(ref filter) = self.filter {
filter.matches(record)
} else {
true
}
}

pub(crate) fn is_loggable(&self, level: Level) -> bool {
// todo: consider __android_log_is_loggable.
level <= self.log_level.unwrap_or_else(log::max_level)
}

pub fn with_filter(mut self, filter: env_filter::Filter) -> Self {
self.filter = Some(filter);
self
}

pub fn with_tag<S: Into<Vec<u8>>>(mut self, tag: S) -> Self {
self.tag = Some(CString::new(tag).expect("Can't convert tag to CString"));
self
}

/// Sets the format function for formatting the log output.
/// ```
/// # use android_logger::Config;
/// android_logger::init_once(
/// Config::default()
/// .with_max_level(log::LevelFilter::Trace)
/// .format(|f, record| write!(f, "my_app: {}", record.args()))
/// )
/// ```
pub fn format<F>(mut self, format: F) -> Self
where
F: Fn(&mut dyn fmt::Write, &Record) -> fmt::Result + Sync + Send + 'static,
{
self.custom_format = Some(Box::new(format));
self
}
}
47 changes: 47 additions & 0 deletions src/id.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/// Possible identifiers of a specific buffer of Android logging system for
/// logging a message.
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum LogId {
/// Main log buffer.
///
/// This is the only log buffer available to apps.
Main,

/// Radio log buffer.
Radio,

/// Event log buffer.
Events,

/// System log buffer.
System,

/// Crash log buffer.
Crash,

/// Kernel log buffer.
Kernel,

/// Security log buffer.
Security,

/// Statistics log buffer.
Stats,
}

#[cfg(target_os = "android")]
impl LogId {
pub(crate) const fn to_native(log_id: Option<Self>) -> Option<log_ffi::log_id_t> {
match log_id {
Some(Self::Main) => Some(log_ffi::log_id_t::MAIN),
Some(Self::Radio) => Some(log_ffi::log_id_t::RADIO),
Some(Self::Events) => Some(log_ffi::log_id_t::EVENTS),
Some(Self::System) => Some(log_ffi::log_id_t::SYSTEM),
Some(Self::Crash) => Some(log_ffi::log_id_t::CRASH),
Some(Self::Kernel) => Some(log_ffi::log_id_t::KERNEL),
Some(Self::Security) => Some(log_ffi::log_id_t::SECURITY),
Some(Self::Stats) => Some(log_ffi::log_id_t::STATS),
None => None,
}
}
}
Loading