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
28fn 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
38fn milli_fmt(milli: u128) -> String {
40 let milli = milli + 62135596800000 + 8 * MILLIS_SECONDS_PER_HOUR;
43 let mut d = milli / MILLIS_SECONDS_PER_DAY;
44
45 let mut n = d / DAYS_PER400_YEARS;
47 let mut y = 400 * n;
48 d -= DAYS_PER400_YEARS * n;
49
50 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; 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 $( 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}