extern crate log;
extern crate android_liblog_sys;
use std::ffi::CString;
use log::{Log, LogLevel, LogLevelFilter, LogMetadata, LogRecord, SetLoggerError};
use android_liblog_sys::{__android_log_write, LogPriority};
pub struct AndroidLogger {
tag: CString,
format: Box<Fn(&LogRecord) -> String + Sync + Send>,
}
pub struct LogBuilder {
tag: CString,
format: Box<Fn(&LogRecord) -> String + Sync + Send>,
}
pub fn init<S: Into<String>>(tag: S) -> Result<(), SetLoggerError> {
AndroidLogger::new(tag).init()
}
impl AndroidLogger {
pub fn new<S: Into<String>>(tag: S) -> AndroidLogger {
LogBuilder::new(tag).build()
}
pub fn init(self) -> Result<(), SetLoggerError> {
log::set_logger(|max_level| {
max_level.set(LogLevelFilter::max());
Box::new(self)
})
}
}
impl Log for AndroidLogger {
fn enabled(&self, _: &LogMetadata) -> bool {
true
}
fn log(&self, record: &LogRecord) {
if !Log::enabled(self, record.metadata()) {
return;
}
let format = CString::new((self.format)(record)).unwrap();
let prio = match record.level() {
LogLevel::Error => LogPriority::ERROR,
LogLevel::Warn => LogPriority::WARN,
LogLevel::Info => LogPriority::INFO,
LogLevel::Debug => LogPriority::DEBUG,
LogLevel::Trace => LogPriority::VERBOSE,
};
unsafe {
__android_log_write(prio as _, self.tag.as_ptr(), format.as_ptr());
}
}
}
impl LogBuilder {
pub fn new<S: Into<String>>(tag: S) -> LogBuilder {
LogBuilder {
tag: CString::new(tag.into()).unwrap(),
format: Box::new(|record: &LogRecord| {
format!("{}: {}", record.location().module_path(), record.args())
}),
}
}
pub fn format<F: 'static>(&mut self, format: F) -> &mut Self
where F: Fn(&LogRecord) -> String + Sync + Send
{
self.format = Box::new(format);
self
}
pub fn init(self) -> Result<(), SetLoggerError> {
self.build().init()
}
pub fn build(self) -> AndroidLogger {
AndroidLogger {
tag: self.tag,
format: self.format,
}
}
}