diff options
Diffstat (limited to 'libsyslog/src/syslog.rs')
-rw-r--r-- | libsyslog/src/syslog.rs | 92 |
1 files changed, 92 insertions, 0 deletions
diff --git a/libsyslog/src/syslog.rs b/libsyslog/src/syslog.rs new file mode 100644 index 0000000..6b36147 --- /dev/null +++ b/libsyslog/src/syslog.rs @@ -0,0 +1,92 @@ +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(msg) = CString::new(format!("{}", record.args())) { + match record.level() { + Level::Debug => unsafe { syslog(LOG_DEBUG, msg.as_ptr()); } + Level::Error => unsafe { syslog(LOG_ERR, msg.as_ptr()); } + Level::Info => unsafe { syslog(LOG_INFO, msg.as_ptr()); } + Level::Warn => unsafe { syslog(LOG_WARNING, msg.as_ptr()); } + Level::Trace => unsafe { syslog(LOG_DEBUG, msg.as_ptr()); } + } + } + } + } + + fn flush(&self) {} +} |