Files
firezone/rust/android-client-ffi/src/make_writer.rs
Thomas Eizinger 5566f1847f refactor(rust): move crates into a more sensical hierarchy (#9066)
The current `rust/` directory is a bit of a wild-west in terms of how
the crates are organised. Most of them are simply at the top-level when
in reality, they are all `connlib`-related. The Apple and Android FFI
crates - which are entrypoints in the Rust code are defined several
layers deep.

To improve the situation, we move around and rename several crates. The
end result is that all top-level crates / directories are:

- Either entrypoints into the Rust code, i.e. applications such as
Gateway, Relay or a Client
- Or crates shared across all those entrypoints, such as `telemetry` or
`logging`
2025-05-12 01:04:17 +00:00

90 lines
2.3 KiB
Rust

//! Heavily inspired from https://github.com/Actyx/tracing-android/blob/master/src/android.rs.
use std::{
ffi::{CStr, CString},
io::{self, BufWriter},
};
use tracing::Level;
const LOGGING_MSG_MAX_LEN: usize = 4000;
pub(crate) struct MakeWriter {
tag: CString,
}
pub(crate) struct Writer {
level: Level,
tag: CString,
}
impl MakeWriter {
pub(crate) fn new(tag: &'static str) -> Self {
Self {
tag: CString::new(tag).expect("tag must not contain nul-byte"),
}
}
fn make_writer_for_level(&self, level: Level) -> BufWriter<Writer> {
let inner = Writer {
level,
tag: self.tag.clone(),
};
BufWriter::with_capacity(LOGGING_MSG_MAX_LEN, inner)
}
}
impl tracing_subscriber::fmt::MakeWriter<'_> for MakeWriter {
type Writer = BufWriter<Writer>;
fn make_writer(&self) -> Self::Writer {
self.make_writer_for_level(Level::INFO)
}
fn make_writer_for(&self, meta: &tracing::Metadata<'_>) -> Self::Writer {
self.make_writer_for_level(*meta.level())
}
}
impl io::Write for Writer {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
let written = buf.len().min(LOGGING_MSG_MAX_LEN);
let msg = &buf[..written];
let msg = CString::new(msg.to_vec())?;
android_log(self.level, self.tag.as_c_str(), &msg);
Ok(written)
}
fn flush(&mut self) -> io::Result<()> {
Ok(())
}
}
#[cfg(target_os = "android")]
fn android_log(level: Level, tag: &CStr, msg: &CStr) {
let prio = match level {
Level::WARN => android_log_sys::LogPriority::WARN,
Level::INFO => android_log_sys::LogPriority::INFO,
Level::DEBUG => android_log_sys::LogPriority::DEBUG,
Level::ERROR => android_log_sys::LogPriority::ERROR,
Level::TRACE => android_log_sys::LogPriority::VERBOSE,
};
// Safety: FFI calls are unsafe.
unsafe {
android_log_sys::__android_log_write(
prio as android_log_sys::c_int,
tag.as_ptr() as *const android_log_sys::c_char,
msg.as_ptr() as *const android_log_sys::c_char,
)
};
}
#[cfg(not(target_os = "android"))]
fn android_log(_: Level, _: &CStr, _: &CStr) {
unimplemented!("Logger is not meant to be used in non-Android environments")
}