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) {} }