captains_log/
console_impl.rs

1use crate::{
2    config::{LogFormat, SinkConfigTrait},
3    env::EnvVarDefault,
4    log_impl::{LogSink, LogSinkTrait},
5    time::Timer,
6};
7use log::{Level, Record};
8use std::hash::{Hash, Hasher};
9use std::path::Path;
10use std::str::FromStr;
11
12#[derive(Hash)]
13pub struct LogConsole {
14    pub target: ConsoleTarget,
15
16    /// max log level in this file
17    pub level: Level,
18
19    pub format: LogFormat,
20}
21
22impl LogConsole {
23    pub fn new(target: ConsoleTarget, level: Level, format: LogFormat) -> Self {
24        Self { target, level, format }
25    }
26}
27
28#[derive(Copy, Clone, Debug, Hash, PartialEq)]
29#[repr(u8)]
30pub enum ConsoleTarget {
31    Stdout = 1,
32    Stderr = 2,
33}
34
35impl FromStr for ConsoleTarget {
36    type Err = ();
37
38    /// accepts case-insensitive: stdout, stderr, out, err, 1, 2
39    fn from_str(s: &str) -> Result<Self, ()> {
40        let v = s.to_lowercase();
41        match v.as_str() {
42            "stdout" => Ok(ConsoleTarget::Stdout),
43            "stderr" => Ok(ConsoleTarget::Stderr),
44            "out" => Ok(ConsoleTarget::Stdout),
45            "err" => Ok(ConsoleTarget::Stderr),
46            "1" => Ok(ConsoleTarget::Stdout),
47            "2" => Ok(ConsoleTarget::Stderr),
48            _ => Err(()),
49        }
50    }
51}
52
53// Tried to impl blanket trait T: FromStr, rust reports conflict with
54// - impl<T, U> Into<U> for T where U: From<T>;
55crate::impl_from_env!(ConsoleTarget);
56
57impl SinkConfigTrait for LogConsole {
58    fn get_level(&self) -> Level {
59        self.level
60    }
61
62    fn get_file_path(&self) -> Option<Box<Path>> {
63        None
64    }
65
66    fn write_hash(&self, hasher: &mut Box<dyn Hasher>) {
67        self.hash(hasher);
68        hasher.write(b"LogConsole");
69    }
70
71    fn build(&self) -> LogSink {
72        LogSink::Console(LogSinkConsole::new(self))
73    }
74}
75
76pub(crate) struct LogSinkConsole {
77    target_fd: libc::c_int,
78    max_level: Level,
79    formatter: LogFormat,
80}
81
82impl LogSinkConsole {
83    pub fn new(config: &LogConsole) -> Self {
84        Self {
85            target_fd: config.target as i32,
86            max_level: config.level,
87            formatter: config.format.clone(),
88        }
89    }
90}
91
92impl LogSinkTrait for LogSinkConsole {
93    fn reopen(&self) -> std::io::Result<()> {
94        Ok(())
95    }
96
97    #[inline(always)]
98    fn log(&self, now: &Timer, r: &Record) {
99        if r.level() <= self.max_level {
100            let buf = self.formatter.process(now, r);
101            unsafe {
102                let _ = libc::write(self.target_fd, buf.as_ptr() as *const libc::c_void, buf.len());
103            }
104        }
105    }
106
107    #[inline(always)]
108    fn flush(&self) {}
109}