• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2022 The Chromium OS Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 //! Facilities for sending log message to syslog.
6 //!
7 //! Every function exported by this module is thread-safe. Each function will silently fail until
8 //! `syslog::init()` is called and returns `Ok`.
9 //!
10 //! # Examples
11 //!
12 //! ```
13 //! use crate::platform::{error, syslog, warn};
14 //!
15 //! if let Err(e) = syslog::init() {
16 //!     println!("failed to initiailize syslog: {}", e);
17 //!     return;
18 //! }
19 //! warn!("this is your {} warning", "final");
20 //! error!("something went horribly wrong: {}", "out of RAMs");
21 //! ```
22 
23 pub use super::win::syslog::PlatformSyslog;
24 use super::RawDescriptor;
25 use crate::descriptor::AsRawDescriptor;
26 use crate::{syslog_lock, CHRONO_TIMESTAMP_FIXED_FMT};
27 use serde::{Deserialize, Serialize};
28 use std::{
29     convert::{From, Into, TryFrom},
30     env,
31     ffi::{OsStr, OsString},
32     fmt::{
33         Display, {self},
34     },
35     fs::File,
36     io,
37     io::{stderr, Cursor, Write},
38     path::{Path, PathBuf},
39     sync::{MutexGuard, Once},
40 };
41 
42 use remain::sorted;
43 use sync::Mutex;
44 use thiserror::Error as ThisError;
45 
46 /// The priority (i.e. severity) of a syslog message.
47 ///
48 /// See syslog man pages for information on their semantics.
49 #[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
50 pub enum Priority {
51     Emergency = 0,
52     Alert = 1,
53     Critical = 2,
54     Error = 3,
55     Warning = 4,
56     Notice = 5,
57     Info = 6,
58     Debug = 7,
59 }
60 
61 impl Display for Priority {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result62     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
63         use self::Priority::*;
64 
65         let string = match self {
66             Emergency => "EMERGENCY",
67             Alert => "ALERT",
68             Critical => "CRITICAL",
69             Error => "ERROR",
70             Warning => "WARNING",
71             Notice => "NOTICE",
72             Info => "INFO",
73             Debug => "DEBUG",
74         };
75 
76         write!(f, "{}", string)
77     }
78 }
79 
80 impl TryFrom<&str> for Priority {
81     type Error = &'static str;
82 
try_from(value: &str) -> Result<Self, <Self as TryFrom<&str>>::Error>83     fn try_from(value: &str) -> Result<Self, <Self as TryFrom<&str>>::Error> {
84         match value {
85             "0" | "EMERGENCY" => Ok(Priority::Emergency),
86             "1" | "ALERT" => Ok(Priority::Alert),
87             "2" | "CRITICAL" => Ok(Priority::Critical),
88             "3" | "ERROR" => Ok(Priority::Error),
89             "4" | "WARNING" => Ok(Priority::Warning),
90             "5" | "NOTICE" => Ok(Priority::Notice),
91             "6" | "INFO" => Ok(Priority::Info),
92             "7" | "DEBUG" => Ok(Priority::Debug),
93             _ => Err("Priority can only be parsed from 0-7 and given variant names"),
94         }
95     }
96 }
97 
98 #[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
99 pub enum PriorityFilter {
100     Silent,
101     Priority(Priority),
102     ShowAll,
103 }
104 
105 impl From<Priority> for PriorityFilter {
from(pri: Priority) -> Self106     fn from(pri: Priority) -> Self {
107         PriorityFilter::Priority(pri)
108     }
109 }
110 
111 impl TryFrom<&str> for PriorityFilter {
112     type Error = &'static str;
113 
try_from(value: &str) -> Result<Self, Self::Error>114     fn try_from(value: &str) -> Result<Self, Self::Error> {
115         match value.to_uppercase().as_str() {
116             "S" | "SILENT" => Ok(PriorityFilter::Silent),
117             "*" => Ok(PriorityFilter::ShowAll),
118             value => match Priority::try_from(value) {
119                 Ok(pri) => Ok(PriorityFilter::Priority(pri)),
120                 Err(_) => Err("PriorityFilter can only be parsed from valid Priority
121                 value, S, *, or SILENT"),
122             },
123         }
124     }
125 }
126 
127 /// The facility of a syslog message.
128 ///
129 /// See syslog man pages for information on their semantics.
130 #[derive(Copy, Clone)]
131 pub enum Facility {
132     Kernel = 0,
133     User = 1 << 3,
134     Mail = 2 << 3,
135     Daemon = 3 << 3,
136     Auth = 4 << 3,
137     Syslog = 5 << 3,
138     Lpr = 6 << 3,
139     News = 7 << 3,
140     Uucp = 8 << 3,
141     Local0 = 16 << 3,
142     Local1 = 17 << 3,
143     Local2 = 18 << 3,
144     Local3 = 19 << 3,
145     Local4 = 20 << 3,
146     Local5 = 21 << 3,
147     Local6 = 22 << 3,
148     Local7 = 23 << 3,
149 }
150 
151 /// Errors returned by `syslog::init()`.
152 #[sorted]
153 #[derive(ThisError, Debug)]
154 pub enum Error {
155     /// Error while attempting to connect socket.
156     #[error("failed to connect socket: {0}")]
157     Connect(io::Error),
158     /// There was an error using `open` to get the lowest file descriptor.
159     #[error("failed to get lowest file descriptor: {0}")]
160     GetLowestFd(io::Error),
161     /// The guess of libc's file descriptor for the syslog connection was invalid.
162     #[error("guess of fd for syslog connection was invalid")]
163     InvalidFd,
164     /// Initialization was never attempted.
165     #[error("initialization was never attempted")]
166     NeverInitialized,
167     /// Initialization has previously failed and can not be retried.
168     #[error("initialization previously failed and cannot be retried")]
169     Poisoned,
170     /// Error while creating socket.
171     #[error("failed to create socket: {0}")]
172     Socket(io::Error),
173 }
174 
175 /// Defines a log level override for a set of source files with the given path_prefix
176 #[derive(Debug)]
177 struct PathFilter {
178     path_prefix: PathBuf,
179     level: PriorityFilter,
180 }
181 
get_proc_name() -> Option<String>182 fn get_proc_name() -> Option<String> {
183     env::args_os()
184         .next()
185         .map(PathBuf::from)
186         .and_then(|s| s.file_name().map(OsStr::to_os_string))
187         .map(OsString::into_string)
188         .and_then(Result::ok)
189 }
190 
191 struct State {
192     stderr: bool,
193     file: Option<File>,
194     proc_name: Option<String>,
195     syslog: PlatformSyslog,
196     log_level: PriorityFilter, // This is the default global log level
197     path_log_levels: Vec<PathFilter>, // These are sorted with longest path prefixes first
198 }
199 
200 impl State {
new() -> Result<State, Error>201     fn new() -> Result<State, Error> {
202         Ok(State {
203             stderr: true,
204             file: None,
205             proc_name: get_proc_name(),
206             syslog: PlatformSyslog::new()?,
207             log_level: PriorityFilter::Priority(Priority::Info),
208             path_log_levels: Vec::new(),
209         })
210     }
211 }
212 
213 /// Set the log level filter.
214 ///
215 /// Does nothing if syslog was never initialized. Set log level filter with the given priority filter level.
set_log_level<T: Into<PriorityFilter>>(log_level: T)216 pub fn set_log_level<T: Into<PriorityFilter>>(log_level: T) {
217     let mut state = syslog_lock!();
218     state.log_level = log_level.into();
219 }
220 
221 /// Adds a new per-path log level filter.
add_path_log_level<T: Into<PriorityFilter>>(path_prefix: &str, log_level: T)222 pub fn add_path_log_level<T: Into<PriorityFilter>>(path_prefix: &str, log_level: T) {
223     // Insert filter so that path_log_levels is always sorted with longer prefixes first
224     let mut state = syslog_lock!();
225     let index = state
226         .path_log_levels
227         .binary_search_by_key(&path_prefix.len(), |p| {
228             std::usize::MAX - p.path_prefix.as_os_str().len()
229         })
230         .unwrap_or_else(|e| e);
231     state.path_log_levels.insert(
232         index,
233         PathFilter {
234             path_prefix: PathBuf::from(path_prefix),
235             level: log_level.into(),
236         },
237     );
238 }
239 
240 static STATE_ONCE: Once = Once::new();
241 static mut STATE: *const Mutex<State> = 0 as *const _;
242 
new_mutex_ptr<T>(inner: T) -> *const Mutex<T>243 fn new_mutex_ptr<T>(inner: T) -> *const Mutex<T> {
244     Box::into_raw(Box::new(Mutex::new(inner)))
245 }
246 
247 /// Initialize the syslog connection and internal variables.
248 ///
249 /// This should only be called once per process before any other threads have been spawned or any
250 /// signal handlers have been registered. Every call made after the first will have no effect
251 /// besides return `Ok` or `Err` appropriately.
init() -> Result<(), Error>252 pub fn init() -> Result<(), Error> {
253     let mut err = Error::Poisoned;
254     STATE_ONCE.call_once(|| match State::new() {
255         // Safe because STATE mutation is guarded by `Once`.
256         Ok(state) => unsafe { STATE = new_mutex_ptr(state) },
257         Err(e) => err = e,
258     });
259 
260     if unsafe { STATE.is_null() } {
261         Err(err)
262     } else {
263         Ok(())
264     }
265 }
266 
lock() -> Result<MutexGuard<'static, State>, Error>267 fn lock() -> Result<MutexGuard<'static, State>, Error> {
268     // Safe because we assume that STATE is always in either a valid or NULL state.
269     let state_ptr = unsafe { STATE };
270     if state_ptr.is_null() {
271         return Err(Error::NeverInitialized);
272     }
273     // Safe because STATE only mutates once and we checked for NULL.
274     let state = unsafe { &*state_ptr };
275     let guard = state.lock();
276     Ok(guard)
277 }
278 
279 // Attempts to lock and retrieve the state. Returns from the function silently on failure.
280 #[macro_export]
281 macro_rules! syslog_lock {
282     () => {
283         match lock() {
284             Ok(s) => s,
285             _ => return,
286         }
287     };
288 }
289 
log_enabled(pri: Priority, file_path: &str) -> Option<bool>290 pub fn log_enabled(pri: Priority, file_path: &str) -> Option<bool> {
291     let log_level = match lock() {
292         Ok(state) => {
293             let parsed_path = Path::new(file_path);
294             // Since path_log_levels is sorted with longest prefixes first, this will yield the
295             // longest matching prefix if one exists.
296             state
297                 .path_log_levels
298                 .iter()
299                 .find_map(|filter| {
300                     if parsed_path.starts_with(filter.path_prefix.as_path()) {
301                         Some(filter.level)
302                     } else {
303                         None
304                     }
305                 })
306                 .unwrap_or(state.log_level)
307         }
308         _ => return None,
309     };
310     match log_level {
311         PriorityFilter::ShowAll => Some(true),
312         PriorityFilter::Silent => Some(false),
313         PriorityFilter::Priority(log_level) => Some((pri as u8) <= (log_level as u8)),
314     }
315 }
316 
317 /// A macro for logging at an arbitrary priority level.
318 ///
319 /// Note that this will fail silently if syslog was not initialized.
320 #[macro_export]
321 macro_rules! log {
322     ($pri:expr, $($args:tt)+) => ({
323         if let Some(true) = $crate::platform::syslog::log_enabled($pri, file!()) {
324             $crate::platform::syslog::log($pri, $crate::platform::syslog::Facility::User, Some((file!(), line!())), format_args!($($args)+))
325         }
326     })
327 }
328 
329 /// Replaces the process name reported in each syslog message.
330 ///
331 /// The default process name is the _file name_ of `argv[0]`. For example, if this program was
332 /// invoked as
333 ///
334 /// ```bash
335 /// $ path/to/app --delete everything
336 /// ```
337 ///
338 /// the default process name would be _app_.
339 ///
340 /// Does nothing if syslog was never initialized.
set_proc_name<T: Into<String>>(proc_name: T)341 pub fn set_proc_name<T: Into<String>>(proc_name: T) {
342     let mut state = syslog_lock!();
343     state.proc_name = Some(proc_name.into());
344 }
345 
346 pub(crate) trait Syslog {
new() -> Result<Self, Error> where Self: Sized347     fn new() -> Result<Self, Error>
348     where
349         Self: Sized;
350 
351     /// Enables or disables echoing log messages to the syslog.
352     ///
353     /// The default behavior is **enabled**.
354     ///
355     /// If `enable` goes from `true` to `false`, the syslog connection is closed. The connection is
356     /// reopened if `enable` is set to `true` after it became `false`.
357     ///
358     /// Returns an error if syslog was never initialized or the syslog connection failed to be
359     /// established.
360     ///
361     /// # Arguments
362     /// * `enable` - `true` to enable echoing to syslog, `false` to disable echoing to syslog.
enable(&mut self, enable: bool) -> Result<(), Error>363     fn enable(&mut self, enable: bool) -> Result<(), Error>;
364 
log( &self, proc_name: Option<&str>, pri: Priority, fac: Facility, file_line: Option<(&str, u32)>, args: fmt::Arguments, )365     fn log(
366         &self,
367         proc_name: Option<&str>,
368         pri: Priority,
369         fac: Facility,
370         file_line: Option<(&str, u32)>,
371         args: fmt::Arguments,
372     );
373 
push_descriptors(&self, fds: &mut Vec<RawDescriptor>)374     fn push_descriptors(&self, fds: &mut Vec<RawDescriptor>);
375 }
376 
377 /// Enables or disables echoing log messages to the syslog.
378 ///
379 /// The default behavior is **enabled**.
380 ///
381 /// If `enable` goes from `true` to `false`, the syslog connection is closed. The connection is
382 /// reopened if `enable` is set to `true` after it became `false`.
383 ///
384 /// Returns an error if syslog was never initialized or the syslog connection failed to be
385 /// established.
386 ///
387 /// # Arguments
388 /// * `enable` - `true` to enable echoing to syslog, `false` to disable echoing to syslog.
echo_syslog(enable: bool) -> Result<(), Error>389 pub fn echo_syslog(enable: bool) -> Result<(), Error> {
390     let state_ptr = unsafe { STATE };
391     if state_ptr.is_null() {
392         return Err(Error::NeverInitialized);
393     }
394     let mut state = lock().map_err(|_| Error::Poisoned)?;
395 
396     state.syslog.enable(enable)
397 }
398 
399 /// Replaces the optional `File` to echo log messages to.
400 ///
401 /// The default behavior is to not echo to a file. Passing `None` to this function restores that
402 /// behavior.
403 ///
404 /// Does nothing if syslog was never initialized.
405 ///
406 /// # Arguments
407 /// * `file` - `Some(file)` to echo to `file`, `None` to disable echoing to the file previously passed to `echo_file`.
echo_file(file: Option<File>)408 pub fn echo_file(file: Option<File>) {
409     let mut state = syslog_lock!();
410     state.file = file;
411 }
412 
413 /// Enables or disables echoing log messages to the `std::io::stderr()`.
414 ///
415 /// The default behavior is **enabled**.
416 ///
417 /// Does nothing if syslog was never initialized.
418 ///
419 /// # Arguments
420 /// * `enable` - `true` to enable echoing to stderr, `false` to disable echoing to stderr.
echo_stderr(enable: bool)421 pub fn echo_stderr(enable: bool) {
422     let mut state = syslog_lock!();
423     state.stderr = enable;
424 }
425 
426 /// Retrieves the file descriptors owned by the global syslogger.
427 ///
428 /// Does nothing if syslog was never initialized. If their are any file descriptors, they will be
429 /// pushed into `fds`.
430 ///
431 /// Note that the `stderr` file descriptor is never added, as it is not owned by syslog.
push_descriptors(fds: &mut Vec<RawDescriptor>)432 pub fn push_descriptors(fds: &mut Vec<RawDescriptor>) {
433     let state = syslog_lock!();
434     state.syslog.push_descriptors(fds);
435     fds.extend(state.file.iter().map(|f| f.as_raw_descriptor()));
436 }
437 
438 /// Compatability function for callers using the old naming
push_fds(fds: &mut Vec<RawDescriptor>)439 pub fn push_fds(fds: &mut Vec<RawDescriptor>) {
440     push_descriptors(fds)
441 }
442 
443 /// Records a log message with the given details.
444 ///
445 /// Note that this will fail silently if syslog was not initialized.
446 ///
447 /// # Arguments
448 /// * `pri` - The `Priority` (i.e. severity) of the log message.
449 /// * `fac` - The `Facility` of the log message. Usually `Facility::User` should be used.
450 /// * `file_line` - Optional tuple of the name of the file that generated the
451 ///                 log and the line number within that file.
452 /// * `args` - The log's message to record, in the form of `format_args!()`  return value
453 ///
454 /// # Examples
455 ///
456 /// ```
457 /// # use crate::platform::syslog;
458 /// # if let Err(e) = syslog::init() {
459 /// #     println!("failed to initiailize syslog: {}", e);
460 /// #     return;
461 /// # }
462 /// syslog::log(syslog::Priority::Error,
463 ///             syslog::Facility::User,
464 ///             Some((file!(), line!())),
465 ///             format_args!("hello syslog"));
466 /// ```
log(pri: Priority, fac: Facility, file_line: Option<(&str, u32)>, args: fmt::Arguments)467 pub fn log(pri: Priority, fac: Facility, file_line: Option<(&str, u32)>, args: fmt::Arguments) {
468     let mut state = syslog_lock!();
469     let mut buf = [0u8; 2048];
470 
471     state.syslog.log(
472         state.proc_name.as_ref().map(|s| s.as_ref()),
473         pri,
474         fac,
475         file_line,
476         args,
477     );
478 
479     let res = {
480         let mut buf_cursor = Cursor::new(&mut buf[..]);
481         let now = chrono::Local::now()
482             .format(CHRONO_TIMESTAMP_FIXED_FMT!())
483             .to_string();
484         if let Some((file_name, line)) = &file_line {
485             write!(&mut buf_cursor, "[{}:{}:{}:{}] ", now, pri, file_name, line)
486         } else {
487             write!(&mut buf_cursor, "[{}]", now)
488         }
489         .and_then(|()| writeln!(&mut buf_cursor, "{}", args))
490         .map(|()| buf_cursor.position() as usize)
491     };
492     if let Ok(len) = &res {
493         write_to_file(&buf, *len, &mut state);
494     } else if let Err(e) = &res {
495         // Don't use warn macro to avoid potential recursion issues after macro expansion.
496         let err_buf = [0u8; 1024];
497         let mut buf_cursor = Cursor::new(&mut buf[..]);
498         if writeln!(
499             &mut buf_cursor,
500             "[{}]:WARNING: Failed to log with err: {:?}",
501             chrono::Local::now().format(CHRONO_TIMESTAMP_FIXED_FMT!()),
502             e
503         )
504         .is_ok()
505         {
506             write_to_file(&err_buf, buf_cursor.position() as usize, &mut state);
507         }
508     }
509 }
510 
write_to_file(buf: &[u8], len: usize, state: &mut State)511 fn write_to_file(buf: &[u8], len: usize, state: &mut State) {
512     if let Some(file) = state.file.as_mut() {
513         let _ = file.write_all(&buf[..len]);
514     }
515     if state.stderr {
516         let _ = stderr().write_all(&buf[..len]);
517     }
518 }
519 
520 /// A macro for logging an error.
521 ///
522 /// Note that this will fail silently if syslog was not initialized.
523 #[macro_export]
524 macro_rules! error {
525     ($($args:tt)+) => ($crate::log!($crate::platform::syslog::Priority::Error, $($args)*))
526 }
527 
528 /// A macro for logging a warning.
529 ///
530 /// Note that this will fail silently if syslog was not initialized.
531 #[macro_export]
532 macro_rules! warn {
533     ($($args:tt)+) => ($crate::log!($crate::platform::syslog::Priority::Warning, $($args)*))
534 }
535 
536 /// A macro for logging info.
537 ///
538 /// Note that this will fail silently if syslog was not initialized.
539 #[macro_export]
540 macro_rules! info {
541     ($($args:tt)+) => ($crate::log!($crate::platform::syslog::Priority::Info, $($args)*))
542 }
543 
544 /// A macro for logging debug information.
545 ///
546 /// Note that this will fail silently if syslog was not initialized.
547 #[macro_export]
548 macro_rules! debug {
549     ($($args:tt)+) => ($crate::log!($crate::platform::syslog::Priority::Debug, $($args)*))
550 }
551 
552 // Struct that implements io::Write to be used for writing directly to the syslog
553 pub struct Syslogger {
554     buf: String,
555     priority: Priority,
556     facility: Facility,
557 }
558 
559 impl Syslogger {
new(p: Priority, f: Facility) -> Syslogger560     pub fn new(p: Priority, f: Facility) -> Syslogger {
561         Syslogger {
562             buf: String::new(),
563             priority: p,
564             facility: f,
565         }
566     }
567 }
568 
569 impl io::Write for Syslogger {
write(&mut self, buf: &[u8]) -> io::Result<usize>570     fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
571         let parsed_str = String::from_utf8_lossy(buf);
572         self.buf.push_str(&parsed_str);
573 
574         if let Some(last_newline_idx) = self.buf.rfind('\n') {
575             for line in self.buf[..last_newline_idx].lines() {
576                 log(self.priority, self.facility, None, format_args!("{}", line));
577             }
578 
579             self.buf.drain(..=last_newline_idx);
580         }
581 
582         Ok(buf.len())
583     }
584 
flush(&mut self) -> io::Result<()>585     fn flush(&mut self) -> io::Result<()> {
586         Ok(())
587     }
588 }
589 
590 // TODO(b/223733375): Enable ignored flaky tests.
591 #[cfg(test)]
592 mod tests {
593     use super::*;
594 
595     use super::super::{BlockingMode, FramingMode, StreamChannel};
596     use regex::Regex;
597     use std::{convert::TryInto, io::Read, os::windows::io::FromRawHandle};
598 
599     #[test]
600     #[ignore]
init_syslog()601     fn init_syslog() {
602         init().unwrap();
603     }
604 
605     #[test]
606     #[ignore]
syslog_log()607     fn syslog_log() {
608         init().unwrap();
609         log(
610             Priority::Error,
611             Facility::User,
612             Some((file!(), line!())),
613             format_args!("hello syslog"),
614         );
615     }
616 
617     #[test]
618     #[ignore]
proc_name()619     fn proc_name() {
620         init().unwrap();
621         log(
622             Priority::Error,
623             Facility::User,
624             Some((file!(), line!())),
625             format_args!("before proc name"),
626         );
627         set_proc_name("win_sys_util-test");
628         log(
629             Priority::Error,
630             Facility::User,
631             Some((file!(), line!())),
632             format_args!("after proc name"),
633         );
634     }
635 
636     #[test]
637     #[ignore]
macros()638     fn macros() {
639         init().unwrap();
640         error!("this is an error {}", 3);
641         warn!("this is a warning {}", "uh oh");
642         info!("this is info {}", true);
643         debug!("this is debug info {:?}", Some("helpful stuff"));
644     }
645 
646     #[test]
647     #[ignore]
syslogger_char()648     fn syslogger_char() {
649         init().unwrap();
650         let mut syslogger = Syslogger::new(Priority::Info, Facility::Daemon);
651 
652         let string = "Writing chars to syslog";
653         for c in string.chars() {
654             syslogger.write_all(&[c as u8]).expect("error writing char");
655         }
656 
657         syslogger
658             .write_all(&[b'\n'])
659             .expect("error writing newline char");
660     }
661 
662     #[test]
663     #[ignore]
syslogger_line()664     fn syslogger_line() {
665         init().unwrap();
666         let mut syslogger = Syslogger::new(Priority::Info, Facility::Daemon);
667 
668         let s = "Writing string to syslog\n";
669         syslogger
670             .write_all(s.as_bytes())
671             .expect("error writing string");
672     }
673 
674     #[test]
675     #[ignore]
syslogger_partial()676     fn syslogger_partial() {
677         init().unwrap();
678         let mut syslogger = Syslogger::new(Priority::Info, Facility::Daemon);
679 
680         let s = "Writing partial string";
681         // Should not log because there is no newline character
682         syslogger
683             .write_all(s.as_bytes())
684             .expect("error writing string");
685     }
686 
clear_path_log_levels()687     fn clear_path_log_levels() {
688         syslog_lock!().path_log_levels.clear();
689     }
690 
691     #[test]
692     #[ignore]
syslogger_verify_line_written()693     fn syslogger_verify_line_written() {
694         init().unwrap();
695         clear_path_log_levels();
696         let (mut reader, writer) =
697             StreamChannel::pair(BlockingMode::Blocking, FramingMode::Byte).unwrap();
698 
699         // Safe because writer is guaranteed to exist, and we forget the StreamChannel that used
700         // to own the StreamChannel.
701         let writer_file = unsafe { File::from_raw_handle(writer.as_raw_descriptor()) };
702         std::mem::forget(writer);
703 
704         echo_file(Some(writer_file));
705         error!("Test message.");
706 
707         // Ensure the message we wrote actually was written to the supplied "file" (which is
708         // really a named pipe here).
709         let mut buf: [u8; 1024] = [0; 1024];
710         let bytes_read = reader.read(&mut buf).unwrap();
711         assert!(bytes_read > 0);
712         let log_msg = String::from_utf8(buf.to_vec()).unwrap();
713         let re = Regex::new(r"^\[.+:ERROR:.+:[0-9]+\] Test message.").unwrap();
714         assert!(re.is_match(&log_msg));
715     }
716 
717     #[test]
718     #[ignore]
log_priority_try_from_number()719     fn log_priority_try_from_number() {
720         assert_eq!("0".try_into(), Ok(Priority::Emergency));
721         assert!(Priority::try_from("100").is_err());
722     }
723 
724     #[test]
725     #[ignore]
log_priority_try_from_words()726     fn log_priority_try_from_words() {
727         assert_eq!("EMERGENCY".try_into(), Ok(Priority::Emergency));
728         assert!(Priority::try_from("_EMERGENCY").is_err());
729     }
730 
731     #[test]
732     #[ignore]
log_priority_filter_try_from_star()733     fn log_priority_filter_try_from_star() {
734         assert_eq!("*".try_into(), Ok(PriorityFilter::ShowAll));
735     }
736 
737     #[test]
738     #[ignore]
log_priority_filter_try_from_silence()739     fn log_priority_filter_try_from_silence() {
740         assert_eq!("S".try_into(), Ok(PriorityFilter::Silent));
741         assert_eq!("s".try_into(), Ok(PriorityFilter::Silent));
742         assert_eq!("SILENT".try_into(), Ok(PriorityFilter::Silent));
743     }
744 
745     #[test]
746     #[ignore]
log_priority_filter_try_from_priority_str()747     fn log_priority_filter_try_from_priority_str() {
748         assert_eq!(
749             "DEBUG".try_into(),
750             Ok(PriorityFilter::Priority(Priority::Debug))
751         );
752         assert_eq!(
753             "debug".try_into(),
754             Ok(PriorityFilter::Priority(Priority::Debug))
755         );
756         assert!(PriorityFilter::try_from("_DEBUG").is_err());
757     }
758 
759     #[test]
760     #[ignore]
log_should_always_be_enabled_for_level_show_all()761     fn log_should_always_be_enabled_for_level_show_all() {
762         init().unwrap();
763         clear_path_log_levels();
764         set_log_level(PriorityFilter::ShowAll);
765         assert!(log_enabled(Priority::Debug, "").unwrap());
766     }
767 
768     #[test]
769     #[ignore]
log_should_always_be_disabled_for_level_silent()770     fn log_should_always_be_disabled_for_level_silent() {
771         init().unwrap();
772         clear_path_log_levels();
773         set_log_level(PriorityFilter::Silent);
774         let enabled = log_enabled(Priority::Emergency, "");
775         set_log_level(PriorityFilter::ShowAll);
776         assert!(!enabled.unwrap());
777     }
778 
779     #[test]
780     #[ignore]
log_should_be_enabled_if_filter_level_has_a_lower_or_equal_priority()781     fn log_should_be_enabled_if_filter_level_has_a_lower_or_equal_priority() {
782         init().unwrap();
783         clear_path_log_levels();
784         set_log_level(Priority::Info);
785         let info_enabled = log_enabled(Priority::Info, "");
786         let warn_enabled = log_enabled(Priority::Warning, "");
787         set_log_level(PriorityFilter::ShowAll);
788         assert!(info_enabled.unwrap());
789         assert!(warn_enabled.unwrap());
790     }
791 
792     #[test]
793     #[ignore]
log_should_be_disabled_if_filter_level_has_a_higher_priority()794     fn log_should_be_disabled_if_filter_level_has_a_higher_priority() {
795         init().unwrap();
796         clear_path_log_levels();
797         set_log_level(Priority::Info);
798         let enabled = log_enabled(Priority::Debug, "");
799         set_log_level(PriorityFilter::ShowAll);
800         assert!(!enabled.unwrap());
801     }
802 
803     #[test]
804     #[ignore]
path_overides_should_apply_to_logs()805     fn path_overides_should_apply_to_logs() {
806         init().unwrap();
807         clear_path_log_levels();
808         set_log_level(Priority::Info);
809         add_path_log_level("fake/debug/src", Priority::Debug);
810 
811         assert!(!log_enabled(Priority::Debug, "test.rs").unwrap());
812         assert!(log_enabled(Priority::Debug, "fake/debug/src/test.rs").unwrap());
813     }
814 
815     #[test]
816     #[ignore]
longest_path_prefix_match_should_apply_if_multiple_filters_match()817     fn longest_path_prefix_match_should_apply_if_multiple_filters_match() {
818         init().unwrap();
819         clear_path_log_levels();
820         set_log_level(Priority::Info);
821         add_path_log_level("fake/debug/src", Priority::Debug);
822         add_path_log_level("fake/debug/src/silence", PriorityFilter::Silent);
823 
824         assert!(!log_enabled(Priority::Info, "fake/debug/src/silence/test.rs").unwrap());
825     }
826 
827     #[test]
828     #[ignore]
syslogger_log_level_filter()829     fn syslogger_log_level_filter() {
830         init().unwrap();
831         clear_path_log_levels();
832         set_log_level(Priority::Error);
833 
834         let (mut reader, writer) =
835             StreamChannel::pair(BlockingMode::Blocking, FramingMode::Byte).unwrap();
836 
837         // Safe because writer is guaranteed to exist, and we forget the StreamChannel that used
838         // to own the StreamChannel.
839         let writer_file = unsafe { File::from_raw_handle(writer.as_raw_descriptor()) };
840         std::mem::forget(writer);
841 
842         echo_file(Some(writer_file));
843         error!("Test message.");
844         debug!("Test message.");
845 
846         add_path_log_level(file!(), Priority::Debug);
847         debug!("Test with file filter.");
848 
849         set_log_level(PriorityFilter::ShowAll);
850 
851         // Ensure the message we wrote actually was written to the supplied "file" (which is
852         // really a named pipe here).
853         let mut buf: [u8; 1024] = [0; 1024];
854         let bytes_read = reader.read(&mut buf).unwrap();
855         assert!(bytes_read > 0);
856         let log_msg = String::from_utf8(buf.to_vec()).unwrap();
857         let re_error = Regex::new(r"(?m)^\[.*ERROR:.+:[0-9]+\] Test message.").unwrap();
858         let re_debug = Regex::new(r"(?m)^\[.*DEBUG:.+:[0-9]+\] Test message.").unwrap();
859         let filter_debug =
860             Regex::new(r"(?m)^\[.*DEBUG:.+:[0-9]+\] Test with file filter.").unwrap();
861         assert!(re_error.is_match(&log_msg));
862         assert!(!re_debug.is_match(&log_msg));
863         assert!(filter_debug.is_match(&log_msg));
864     }
865 }
866