1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
|
use {
crate::SyslogBuilder,
libsyslog_sys::*,
log::{
Level,
LevelFilter,
Metadata,
Record,
SetLoggerError,
set_boxed_logger,
set_max_level,
},
std::{
ffi::CString,
os::raw::c_int,
},
};
/// Logger object to make [log][log] output message to syslog.
///
/// Creating and initializing a logger object will make [`log`]'s logging
/// macros go to syslog. By default a logger object gets created with facility
/// [Facility::User][`crate::Facility::User`], the name of the process as its ident, a global log
/// level of [`LevelFilter::Info`], no module loglevels and with logopt set to
/// [`libsyslog_sys::LOG_PID`]. The [`SyslogBuilder`] can be used to customize
/// those default values.
#[derive(Debug)]
pub struct Syslog {
pub(crate) facility: c_int,
pub(crate) ident: CString,
pub(crate) level: LevelFilter,
pub(crate) logopt: c_int,
pub(crate) module_levels: Vec<(String, LevelFilter)>,
}
impl Syslog {
#[must_use = "No Syslog is constructed unless .build() is called."]
pub fn builder() -> SyslogBuilder {
//! Returns the [`SyslogBuilder`] used to create a [`Syslog`] logger object.
SyslogBuilder::default()
}
pub fn init(mut self) -> Result<(), SetLoggerError> {
//! Initialize logging by opening a connection to syslog and hand over `self` to be
//! [log's][`log::set_boxed_logger`] global logger.
//!
//! Passes the result of [set_boxed_logger][`log::set_boxed_logger`], where
//! [`SetLoggerError`] is returned if a global logger already is active.
unsafe { openlog(self.ident.as_ptr(), self.logopt, self.facility); }
self.module_levels.sort_by_key(|(modpath, _)| modpath.len().wrapping_neg());
let max_module_level = self.module_levels.iter().map(|(_, level)| level).copied().max();
let max_level = max_module_level.map(|level| level.max(self.level)).unwrap_or(self.level);
set_max_level(max_level);
set_boxed_logger(Box::new(self))
}
}
impl Drop for Syslog {
fn drop(&mut self) {
//! Closes the syslog connection. (Warning: Might potentially block.)
unsafe { closelog(); }
}
}
impl log::Log for Syslog {
fn enabled(&self, metadata: &Metadata) -> bool {
&metadata.level().to_level_filter() <= self.module_levels.iter()
.find(|(modpath, _)| metadata.target().starts_with(modpath) ).map(|(_, level)| level)
.unwrap_or(&self.level)
}
fn log(&self, record: &Record) {
if self.enabled(record.metadata()) {
if let (Ok(fmt), Ok(msg)) = ( CString::new("%s"),
CString::new(format!("{}", record.args())))
{
let fmt_ptr = fmt.as_ptr();
let msg_ptr = msg.as_ptr();
match record.level() {
Level::Debug => unsafe { syslog(LOG_DEBUG, fmt_ptr, msg_ptr); }
Level::Error => unsafe { syslog(LOG_ERR, fmt_ptr, msg_ptr); }
Level::Info => unsafe { syslog(LOG_INFO, fmt_ptr, msg_ptr); }
Level::Warn => unsafe { syslog(LOG_WARNING, fmt_ptr, msg_ptr); }
Level::Trace => unsafe { syslog(LOG_DEBUG, fmt_ptr, msg_ptr); }
}
}
}
}
fn flush(&self) {}
}
|