use crate::{
config::{LogFormat, SinkConfigBuild, SinkConfigTrait},
env::EnvVarDefault,
log_impl::{LogSink, LogSinkTrait},
time::Timer,
};
use log::{Level, Record};
use std::hash::{Hash, Hasher};
use std::path::Path;
use std::str::FromStr;
#[derive(Hash)]
pub struct LogConsole {
pub target: ConsoleTarget,
pub level: Level,
pub format: LogFormat,
}
impl LogConsole {
pub fn new(target: ConsoleTarget, level: Level, format: LogFormat) -> Self {
Self { target, level, format }
}
}
#[derive(Copy, Clone, Debug, Hash, PartialEq)]
#[repr(u8)]
pub enum ConsoleTarget {
Stdout = 1,
Stderr = 2,
}
impl FromStr for ConsoleTarget {
type Err = ();
fn from_str(s: &str) -> Result<Self, ()> {
let v = s.to_lowercase();
match v.as_str() {
"stdout" => Ok(ConsoleTarget::Stdout),
"stderr" => Ok(ConsoleTarget::Stderr),
"out" => Ok(ConsoleTarget::Stdout),
"err" => Ok(ConsoleTarget::Stderr),
"1" => Ok(ConsoleTarget::Stdout),
"2" => Ok(ConsoleTarget::Stderr),
_ => Err(()),
}
}
}
crate::impl_from_env!(ConsoleTarget);
impl SinkConfigBuild for LogConsole {
fn build(&self) -> LogSink {
LogSink::Console(LogSinkConsole::new(self))
}
}
impl SinkConfigTrait for LogConsole {
fn get_level(&self) -> Level {
self.level
}
fn get_file_path(&self) -> Option<Box<Path>> {
None
}
fn write_hash(&self, hasher: &mut Box<dyn Hasher>) {
self.hash(hasher);
hasher.write(b"LogConsole");
}
}
pub(crate) struct LogSinkConsole {
target_fd: libc::c_int,
max_level: Level,
formatter: LogFormat,
}
impl LogSinkConsole {
fn new(config: &LogConsole) -> Self {
Self {
target_fd: config.target as i32,
max_level: config.level,
formatter: config.format.clone(),
}
}
}
impl LogSinkTrait for LogSinkConsole {
fn open(&self) -> std::io::Result<()> {
Ok(())
}
fn reopen(&self) -> std::io::Result<()> {
Ok(())
}
#[inline(always)]
fn log(&self, now: &Timer, r: &Record) {
if r.level() <= self.max_level {
let buf = self.formatter.process(now, r);
unsafe {
let _ = libc::write(self.target_fd, buf.as_ptr() as *const libc::c_void, buf.len());
}
}
}
#[inline(always)]
fn flush(&self) {}
}