use crate::{thread, Buffer, Priority, Record, TagMode};
use env_logger::filter::{Builder, Filter};
use log::{LevelFilter, Log, Metadata};
use parking_lot::RwLock;
use std::{io, process, sync::Arc, time::SystemTime};
pub(crate) struct Configuration {
pub(crate) filter: Filter,
pub(crate) tag: TagMode,
pub(crate) prepend_module: bool,
#[allow(unused)]
pub(crate) pstore: bool,
pub(crate) buffer_id: Buffer,
}
#[derive(Clone)]
pub struct Logger {
pub(crate) configuration: Arc<RwLock<Configuration>>,
}
impl Logger {
pub fn buffer(&self, buffer: Buffer) -> &Self {
self.configuration.write().buffer_id = buffer;
self
}
pub fn tag(&self, tag: &str) -> &Self {
self.configuration.write().tag = TagMode::Custom(tag.into());
self
}
pub fn tag_target(&self) -> &Self {
self.configuration.write().tag = TagMode::Target;
self
}
pub fn tag_target_strip(&self) -> &Self {
self.configuration.write().tag = TagMode::TargetStrip;
self
}
pub fn prepend_module(&self, prepend_module: bool) -> &Self {
self.configuration.write().prepend_module = prepend_module;
self
}
pub fn filter_module(&self, module: &str, level: LevelFilter) -> &Self {
self.configuration.write().filter = Builder::default().filter_module(module, level).build();
self
}
pub fn filter_level(&self, level: LevelFilter) -> &Self {
self.configuration.write().filter = Builder::default().filter_level(level).build();
self
}
pub fn filter(&self, module: Option<&str>, level: LevelFilter) -> &Self {
self.configuration.write().filter = Builder::default().filter(module, level).build();
self
}
pub fn parse_filters(&mut self, filters: &str) -> &mut Self {
let filter = Builder::default().parse(filters).build();
log::set_max_level(filter.filter());
self.configuration.write().filter = filter;
self
}
#[cfg(target_os = "android")]
pub fn pstore(&self, pstore: bool) -> &Self {
self.configuration.write().pstore = pstore;
self
}
}
pub(crate) struct LoggerImpl {
configuration: Arc<RwLock<Configuration>>,
}
impl LoggerImpl {
pub fn new(configuration: Arc<RwLock<Configuration>>) -> Result<LoggerImpl, io::Error> {
Ok(LoggerImpl { configuration })
}
}
impl Log for LoggerImpl {
fn enabled(&self, metadata: &Metadata) -> bool {
self.configuration.read().filter.enabled(metadata)
}
fn log(&self, record: &log::Record) {
let configuration = self.configuration.read();
if !configuration.filter.matches(record) {
return;
}
let args = record.args().to_string();
let message = if let Some(module_path) = record.module_path() {
if configuration.prepend_module {
[module_path, &args].join(": ")
} else {
args
}
} else {
args
};
let priority: Priority = record.metadata().level().into();
let tag = match &configuration.tag {
TagMode::Target => record.target(),
TagMode::TargetStrip => record
.target()
.split_once("::")
.map(|(tag, _)| tag)
.unwrap_or_else(|| record.target()),
TagMode::Custom(tag) => tag.as_str(),
};
let timestamp = SystemTime::now();
let record = Record {
timestamp,
pid: process::id() as u16,
thread_id: thread::id() as u16,
buffer_id: configuration.buffer_id,
tag,
priority,
message: &message,
};
crate::log_record(&record).ok();
#[cfg(target_os = "android")]
{
if configuration.pstore {
crate::pmsg::log(&record);
}
}
}
#[cfg(not(target_os = "android"))]
fn flush(&self) {
use std::io::Write;
io::stderr().flush().ok();
}
#[cfg(target_os = "android")]
fn flush(&self) {
if self.configuration.read().pstore {
crate::pmsg::flush().ok();
}
}
}