extern crate android_log_sys as log_ffi;
#[macro_use] extern crate log;
use log_ffi::LogPriority;
use log::{Log,LogLevel,LogMetadata,LogRecord};
use std::ffi::CStr;
use std::mem;
use std::fmt;
use std::ptr;
fn android_log(prio: log_ffi::LogPriority, tag: &CStr, msg: &CStr) {
unsafe { log_ffi::__android_log_write(prio as log_ffi::c_int, tag.as_ptr() as *const log_ffi::c_char, msg.as_ptr() as *const log_ffi::c_char) };
}
pub struct AndroidLogger;
const LOGGING_TAG_MAX_LEN: usize = 23;
const LOGGING_MSG_MAX_LEN: usize = 4000;
impl Default for AndroidLogger {
fn default() -> AndroidLogger {
AndroidLogger
}
}
impl Log for AndroidLogger {
fn enabled(&self, _: &LogMetadata) -> bool {
true
}
fn log(&self, record: &LogRecord) {
let mut tag_bytes: [u8; LOGGING_TAG_MAX_LEN + 1] = unsafe { mem::uninitialized() };
self.fill_tag_bytes(&mut tag_bytes, record);
let tag: &CStr = unsafe { CStr::from_ptr(mem::transmute(tag_bytes.as_ptr())) };
let mut writer = PlatformLogWriter::new(
record.level(),
tag
);
let _ = fmt::write(&mut writer, *record.args());
writer.flush();
}
}
impl AndroidLogger {
fn fill_tag_bytes(&self, array: &mut [u8], record: &LogRecord) {
let tag_bytes_iter = record.location().module_path().bytes();
if tag_bytes_iter.len() > LOGGING_TAG_MAX_LEN {
for (input, output) in tag_bytes_iter
.take(LOGGING_TAG_MAX_LEN - 2)
.chain(b"..\0".iter().cloned())
.zip(array.iter_mut())
{
*output = input;
}
} else {
for (input, output) in tag_bytes_iter
.chain(b"\0".iter().cloned())
.zip(array.iter_mut())
{
*output = input;
}
}
}
}
struct PlatformLogWriter<'a> {
priority: LogPriority,
len: usize,
last_newline_index: usize,
tag: &'a CStr,
buffer: [u8; LOGGING_MSG_MAX_LEN + 1],
}
impl<'a> PlatformLogWriter<'a> {
pub fn new<'r>(level: LogLevel, tag: &'r CStr) -> PlatformLogWriter<'r> {
PlatformLogWriter {
priority: match level {
LogLevel::Warn => LogPriority::WARN,
LogLevel::Info => LogPriority::INFO,
LogLevel::Debug => LogPriority::DEBUG,
LogLevel::Error => LogPriority::ERROR,
LogLevel::Trace => LogPriority::VERBOSE,
},
len: 0,
last_newline_index: 0,
tag: tag,
buffer: unsafe { mem::uninitialized() },
}
}
fn temporal_flush(&mut self) {
let total_len = self.len;
if total_len == 0 {
return;
}
if self.last_newline_index > 0 {
let copy_from_index = self.last_newline_index;
let remaining_chunk_len = total_len - copy_from_index;
self.output_specified_len(copy_from_index);
self.copy_bytes_to_start(copy_from_index, remaining_chunk_len);
self.len = remaining_chunk_len;
} else {
self.output_specified_len(total_len);
self.len = 0;
}
self.last_newline_index = 0;
}
fn flush(&mut self) {
let total_len = self.len;
if total_len == 0 {
return;
}
self.output_specified_len(total_len);
self.len = 0;
self.last_newline_index = 0;
}
fn output_specified_len(&mut self, len: usize) {
let mut last_byte: u8 = b'\0';
mem::swap(&mut last_byte, unsafe { self.buffer.get_unchecked_mut(len) });
let msg: &CStr = unsafe { CStr::from_ptr(mem::transmute(self.buffer.as_ptr())) };
android_log(self.priority, self.tag, msg);
*unsafe { self.buffer.get_unchecked_mut(len) } = last_byte;
}
fn copy_bytes_to_start(&mut self, index: usize, len: usize) {
let src = unsafe { self.buffer.as_ptr().offset(index as isize) };
let dst = self.buffer.as_mut_ptr();
unsafe { ptr::copy(src, dst, len) };
}
}
impl<'a> fmt::Write for PlatformLogWriter<'a> {
fn write_str(&mut self, s: &str) -> fmt::Result {
let mut incomming_bytes = s.as_bytes();
while incomming_bytes.len() > 0 {
let len = self.len;
let new_len = len + incomming_bytes.len();
let last_newline = self.buffer[len..LOGGING_MSG_MAX_LEN].iter_mut()
.zip(incomming_bytes)
.enumerate()
.fold(None, |acc, (i, (output, input))| {
*output = *input;
if *input == b'\n' { Some(i) } else { acc }
});
if let Some(newline) = last_newline {
self.last_newline_index = len + newline;
}
let written_len = if new_len <= LOGGING_MSG_MAX_LEN {
self.len = new_len;
new_len - len } else {
self.len = LOGGING_MSG_MAX_LEN;
self.temporal_flush();
LOGGING_MSG_MAX_LEN - len };
incomming_bytes = &incomming_bytes[written_len..];
}
Ok(())
}
}
pub fn init_once(log_level: LogLevel) {
match log::set_logger(|max_log_level| {
max_log_level.set(log_level.to_log_level_filter());
return Box::new(AndroidLogger::default());
}) {
Err(e) => debug!("{}", e),
_ => (),
}
}