qog/
lib.rs

1use log;
2
3use std::io::Write;
4
5const MILLIS_SECONDS_PER_SECOND: u128 = 1000;
6const MILLIS_SECONDS_PER_MINUTE: u128 = 60 * 1000;
7const MILLIS_SECONDS_PER_HOUR: u128 = 60 * MILLIS_SECONDS_PER_MINUTE;
8const MILLIS_SECONDS_PER_DAY: u128 = 24 * MILLIS_SECONDS_PER_HOUR;
9const DAYS_PER400_YEARS: u128 = 365 * 400 + 97;
10const DAYS_PER100_YEARS: u128 = 365 * 100 + 24;
11const DAYS_PER4_YEARS: u128 = 365 * 4 + 1;
12const DAYS_BEFORE: [u128; 13] = [
13    0,
14    31,
15    31 + 28,
16    31 + 28 + 31,
17    31 + 28 + 31 + 30,
18    31 + 28 + 31 + 30 + 31,
19    31 + 28 + 31 + 30 + 31 + 30,
20    31 + 28 + 31 + 30 + 31 + 30 + 31,
21    31 + 28 + 31 + 30 + 31 + 30 + 31 + 31,
22    31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30,
23    31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31,
24    31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + 30,
25    31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + 30 + 31,
26];
27
28// 格式:2006-01-02 15:04:05.000
29fn now_fmt() -> String {
30    milli_fmt(
31        std::time::SystemTime::now()
32            .duration_since(std::time::UNIX_EPOCH)
33            .unwrap_or(std::time::Duration::ZERO)
34            .as_millis(),
35    )
36}
37
38// milli: 相对于 1970-01-01 00:00:00.000 的毫秒数
39fn milli_fmt(milli: u128) -> String {
40    // 加上 1970-01-01 00:00:00.000 相对于 0001-01-01 00:00:00.000 的毫秒数
41    // 转到东八时区(北京)
42    let milli = milli + 62135596800000 + 8 * MILLIS_SECONDS_PER_HOUR;
43    let mut d = milli / MILLIS_SECONDS_PER_DAY;
44
45    // Account for 400 year cycles.
46    let mut n = d / DAYS_PER400_YEARS;
47    let mut y = 400 * n;
48    d -= DAYS_PER400_YEARS * n;
49
50    // Cut off 100-year cycles.
51    // The last cycle has one extra leap year, so on the last day
52    // of that year, day / daysPer100Years will be 4 instead of 3.
53    // Cut it back down to 3 by subtracting n>>2.
54    n = d / DAYS_PER100_YEARS;
55    n -= n >> 2;
56    y += 100 * n;
57    d -= DAYS_PER100_YEARS * n;
58
59    n = d / DAYS_PER4_YEARS;
60    y += 4 * n;
61    d -= DAYS_PER4_YEARS * n;
62
63    n = d / 365;
64    n -= n >> 2;
65    y += n;
66    d -= 365 * n;
67
68    let year = y + 1;
69    let mut day = d;
70
71    if is_leap(year) {
72        if day > 31 + 29 - 1 {
73            day -= 1;
74        } else if day == 31 + 29 - 1 {
75            let (hour, min, sec, mils) = clock(milli);
76            return format!(
77                "{}-02-29 {:0>2}:{:0>2}:{:0>2}.{:0<3}",
78                year, hour, min, sec, mils
79            );
80        }
81    }
82
83    let mut month = (day / 31) as usize;
84    let end = DAYS_BEFORE[month + 1];
85
86    let begin = if day >= end {
87        month += 1;
88        end
89    } else {
90        DAYS_BEFORE[month]
91    };
92
93    month += 1; // because January is 1
94    day = day - begin + 1;
95
96    let (hour, min, sec, mils) = clock(milli);
97
98    format!(
99        "{}-{:0>2}-{:0>2} {:0>2}:{:0>2}:{:0>2}.{:0<3}",
100        year, month, day, hour, min, sec, mils
101    )
102}
103
104#[inline]
105fn clock(milli: u128) -> (u128, u128, u128, u128) {
106    let mut mils = milli % MILLIS_SECONDS_PER_DAY;
107    let hour = mils / MILLIS_SECONDS_PER_HOUR;
108    mils -= hour * MILLIS_SECONDS_PER_HOUR;
109    let min = mils / MILLIS_SECONDS_PER_MINUTE;
110    mils -= min * MILLIS_SECONDS_PER_MINUTE;
111    let sec = mils / MILLIS_SECONDS_PER_SECOND;
112    mils -= sec * MILLIS_SECONDS_PER_SECOND;
113
114    (hour, min, sec, mils)
115}
116
117#[inline]
118fn is_leap(year: u128) -> bool {
119    year % 4 == 0 && (year % 100 != 0 || year % 400 == 0)
120}
121
122pub struct Hog {
123    lvl: log::Level,
124    file: std::sync::Mutex<std::fs::File>,
125}
126
127#[macro_export]
128macro_rules! op {
129    ($op:expr $(, $key:ident = $val:expr )+ $(,)?) => {{
130        use std::fmt::Write;
131        let mut s = String::with_capacity(128);
132        let _ = write!(&mut s, "op={}", $op);
133
134        $( // 每对键值直接写入,避免 unwrap
135            let _ = write!(&mut s, "||{}=", stringify!($key));
136            let _ = write!(&mut s, "{}", $val);
137        )+
138
139        log::info!("{}", s);
140    }};
141}
142
143impl Hog {
144    pub fn new(lvl: log::Level, filename: String) -> Self {
145        let f = std::fs::OpenOptions::new()
146            .create(true)
147            .append(true)
148            .open(filename)
149            .expect("cannot open file");
150        Hog {
151            lvl,
152            file: std::sync::Mutex::new(f),
153        }
154    }
155
156    pub fn init(self) {
157        log::set_max_level(match self.lvl {
158            log::Level::Error => log::LevelFilter::Error,
159            log::Level::Warn => log::LevelFilter::Warn,
160            log::Level::Info => log::LevelFilter::Info,
161            log::Level::Debug => log::LevelFilter::Debug,
162            log::Level::Trace => log::LevelFilter::Trace,
163        });
164        log::set_boxed_logger(Box::new(self)).unwrap();
165    }
166
167    pub fn default() {
168        Self::new(log::Level::Debug, String::from("qog.log")).init();
169    }
170}
171
172impl log::Log for Hog {
173    fn enabled(&self, metadata: &log::Metadata) -> bool {
174        metadata.level() <= self.lvl
175    }
176
177    fn log(&self, record: &log::Record) {
178        if !self.enabled(record.metadata()) {
179            return;
180        }
181
182        match self.file.lock() {
183            Ok(mut file) => {
184                let target = record.target();
185                let _ = write!(
186                    file,
187                    "{} {:} {}:{} {}\n",
188                    now_fmt(),
189                    record.level(),
190                    target.rsplit("::").next().unwrap_or(target),
191                    record.line().unwrap_or(0),
192                    record.args()
193                );
194            }
195            Err(err) => eprintln!("failed to lock the log file<{}>", err),
196        }
197    }
198
199    fn flush(&self) {}
200}
201
202#[cfg(test)]
203mod test {
204    use super::*;
205    use log;
206
207    #[test]
208    fn demo() {
209        Hog::default();
210        log::trace!("trace level");
211        log::debug!("23333, {}", 234);
212        log::info!("23333, {}", 234);
213        log::warn!("23333, {}", 234);
214        log::error!("23333, {}", 234);
215        op!(
216            "orgMsg",
217            traceId = String::from("3c7c48b638d9a2569d55ae6234d4f752"),
218            did = "5H06354PAZ086CA",
219            bizId = 8,
220            msgType = "videoMotion",
221            orgTopic = "open-to-yr-lca90f0018eb9c43e0-alarm",
222            dstTopic = "mgw-lc-default-topic-prod"
223        );
224    }
225}