1#![allow(non_upper_case_globals)]
2#[macro_use]
3mod macros;
4mod lvl;
5pub use self::lvl::LogLvl;
6use super::StaticMut;
7
8use time::{now, Tm};
9use std::sync::atomic::{AtomicBool, ATOMIC_BOOL_INIT, Ordering};
10use std::collections::BTreeSet as Set;
11use std::collections::btree_set::Iter;
12use std::env::var;
13use std::env::args;
14pub static mut ENV_VAR_KEY: &'static str = "LOG";
16pub static mut CLI_OPTION_KEYS: [&'static str; 2] = ["-log", "--log"];
18
19lazy_static!{
20 static ref LOGGER:StaticMut<Logger>=StaticMut::new(Logger::default());
21 static ref LogFmterDefault:StaticMut<LogFmter> =StaticMut::new(LogFmter::default());
22}
23static LogFmterInitialized: AtomicBool = ATOMIC_BOOL_INIT;
24
25#[derive(Debug,Default)]
27pub struct Logger {
28 initialized: AtomicBool,
29 enabled: AtomicBool,
30 max_lvl: LogLvl,
31 mod_paths: Set<String>,
32 without_cli_options: AtomicBool,
33}
34
35impl Logger {
36 pub fn set_info_all() {
38 Self::initialized_set(true);
39 Self::enable_set(true);
40 let mut logger = LOGGER.as_mut();
41 logger.mod_paths.insert("*".to_string());
42 logger.max_lvl = LogLvl::Info;
43 }
44 pub fn open() {
46 Self::enable_set(true);
47 }
48 pub fn close() {
50 Self::enable_set(false);
51 }
52 pub fn initialized() -> bool {
53 LOGGER.as_ref().initialized.load(Ordering::Relaxed)
54 }
55 pub fn initialized_set(b: bool) {
56 LOGGER.as_ref().initialized.store(b, Ordering::SeqCst);
57 }
58 pub fn enable() -> bool {
59 LOGGER.as_ref().enabled.load(Ordering::Relaxed)
60 }
61 pub fn enable_set(b: bool) {
62 LOGGER.as_ref().enabled.store(b, Ordering::SeqCst);
63 }
64 pub fn without_cli_options() -> bool {
65 LOGGER.as_ref().without_cli_options.load(Ordering::Relaxed)
66 }
67 pub fn without_cli_options_set(b: bool) {
68 LOGGER
69 .as_ref()
70 .without_cli_options
71 .store(b, Ordering::SeqCst);
72 }
73 pub fn max_lvl() -> &'static LogLvl {
74 &LOGGER.as_ref().max_lvl
75 }
76 pub fn mps() -> Iter<'static, String> {
78 LOGGER.as_ref().mod_paths.iter()
79 }
80 pub fn now() -> Tm {
82 now()
83 }
84 fn cli_options(cli_option_keys: &[&'static str]) -> Option<String> {
90 let mut args: Vec<String> = args().skip(1).collect();
91 let idx = args.as_slice()
92 .iter()
93 .position(|s| cli_option_keys.iter().any(|ss| ss == &s.as_str()));
94 if let Some(idx) = idx {
96 if args.len() >= idx + 2 {
97 return Some(args.remove(idx + 1));
99 }
100 }
101 None
102 }
103 pub fn init(crate_name: &'static str) {
104 if Self::initialized() {
105 return;
106 }
107 if let Ok(s) = var(unsafe { ENV_VAR_KEY }) {
112 Self::init_with_str(crate_name, s);
113 }
114 if !Self::initialized() && !Self::without_cli_options() {
119 if let Some(s) = Self::cli_options(unsafe { &CLI_OPTION_KEYS[..] }) {
120 Self::init_with_str(crate_name, s);
121 }
122 }
123 if !Self::initialized() {
124 Self::set_info_all();
125 }
126 }
128 pub fn init_with_str<S: Into<String>>(mut crate_name: &'static str, var: S) {
134 let mut logger = LOGGER.as_mut();
138 if logger.initialized.load(Ordering::Relaxed) {
140 return;
141 }
142 logger.initialized.store(true, Ordering::SeqCst);
143 if crate_name.contains(':') {
145 let sep_idx = crate_name.find(':').unwrap();
146 crate_name = &crate_name[..sep_idx];
147 }
148
149 let s = var.into();
150 let s = s.trim();
151 let sep_idx = s.find('/');
152 if sep_idx.is_none() {
153 return;
155 }
156 let sep_idx = sep_idx.unwrap();
157 let (lvl_str, mut mps_str) = (&s[..sep_idx], &s[sep_idx + 1..]);
158 if mps_str.is_empty() {
160 mps_str = crate_name; }
162 if let Some(lvl) = LogLvl::from_str(lvl_str) {
164 logger.max_lvl = lvl;
165 } else {
166 return;
167 }
168 let mps: Vec<&str> = mps_str
169 .split(',')
170 .map(|ss| ss.trim().trim_matches(':'))
171 .filter(|ss| !ss.is_empty())
172 .collect();
173 if mps.contains(&"*") {
175 logger.mod_paths.insert("*".to_string());
176 } else {
177 mps.into_iter()
178 .map(|ss| logger.mod_paths.insert(ss.to_string()))
179 .last();
180 }
181 Self::open();
182 }
184 pub fn filter(mod_path: &str, lvl: LogLvl) -> bool {
186 let logger = LOGGER.as_ref();
187 if !logger.enabled.load(Ordering::Relaxed) {
192 return false;
193 }
194 if lvl > logger.max_lvl {
195 return false;
196 }
197 if logger.mod_paths.contains("*") || logger.mod_paths.contains(mod_path) {
199 return true;
200 }
201 for path in &logger.mod_paths {
202 if mod_path.starts_with(path) && (&mod_path[0..path.len() + 1]).ends_with(':') {
203 return true;
204 }
205 }
206 false
207 }
208}
209
210use std::fmt::Arguments;
211#[derive(Debug)]
213pub struct LogLoc {
214 mod_path: &'static str,
215 line: u32,
216 column: u32,
217 file: &'static str,
218 time_local: Tm,
219 time_utc: Tm,
220}
221impl LogLoc {
222 pub fn new(mod_path: &'static str, line: u32, column: u32, file: &'static str) -> Self {
224 let time_local = now();
225 LogLoc {
226 mod_path: mod_path,
227 line: line,
228 column: column,
229 file: file,
230 time_local: time_local,
231 time_utc: time_local.to_utc(),
232 }
233 }
234 pub fn mp(&self) -> &'static str {
236 self.mod_path
237 }
238 pub fn line(&self) -> &u32 {
239 &self.line
240 }
241 pub fn column(&self) -> &u32 {
242 &self.column
243 }
244 pub fn file(&self) -> &'static str {
245 self.file
246 }
247 pub fn time_local(&self) -> &Tm {
248 &self.time_local
249 }
250 pub fn time_utc(&self) -> &Tm {
251 &self.time_utc
252 }
253}
254
255use std::fmt;
256impl fmt::Display for LogLoc {
257 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
258 write!(f, "{}:{}:{}", self.mp(), self.line(), self.column())
259 }
260}
261
262#[derive(Debug)]
264pub struct LogMsg<'a> {
265 loc: LogLoc,
266 msg: Arguments<'a>,
267 lvl: LogLvl,
268}
269impl<'a> LogMsg<'a> {
270 pub fn new(loc: LogLoc, message: Arguments<'a>, lvl: LogLvl) -> Self {
271 LogMsg {
272 loc: loc,
273 msg: message,
274 lvl: lvl,
275 }
276 }
277 pub fn loc(&self) -> &LogLoc {
278 &self.loc
279 }
280 pub fn msg(&'a self) -> &'a Arguments {
281 &self.msg
282 }
283 pub fn lvl(&'a self) -> &'a LogLvl {
284 &self.lvl
285 }
286 pub fn call(&self) -> String {
288 LogFmter::call(self)
289 }
290}
291
292pub struct LogFmter(Box<Fn(&LogMsg) -> String>);
294
295impl LogFmter {
296 pub fn set<F: IntoLogFmter>(f: F) {
302 if LogFmterInitialized.load(Ordering::Relaxed) {
303 return;
304 }
305 LogFmterInitialized.store(true, Ordering::SeqCst);
306 LogFmterDefault.set(f.into())
307 }
308 pub fn call(msg: &LogMsg) -> String {
310 (LogFmterDefault.as_ref().0)(msg)
311 }
312}
313
314pub fn fmter(msg: &LogMsg) -> String {
316 format!("[{}!]#{}:{}:{} {}",
317 msg.lvl(),
318 msg.loc().mp(),
319 msg.loc().line(),
320 msg.loc().column(),
321 msg.msg())
322}
323
324pub fn fmter_with_time(msg: &LogMsg) -> String {
326 let t = msg.loc().time_local();
327 format!("[{:04}-{:02}-{:02} {:02}:{:02}:{:02} {}!]#{}:{}:{} {}",
328 t.tm_year + 1900,
329 t.tm_mon + 1,
330 t.tm_mday,
331 t.tm_hour,
332 t.tm_min,
333 t.tm_sec,
334 msg.lvl(),
335 msg.loc().mp(),
336 msg.loc().line(),
337 msg.loc().column(),
338 msg.msg())
339}
340
341impl Default for LogFmter {
342 fn default() -> LogFmter {
343 LogFmter(Box::new(fmter))
344 }
345}
346
347pub trait IntoLogFmter {
353 fn into(self) -> LogFmter;
354}
355
356impl<F: Fn(&LogMsg) -> String + 'static + Send> IntoLogFmter for F {
357 fn into(self) -> LogFmter {
358 LogFmter(Box::new(self))
359 }
360}