summaryrefslogtreecommitdiff
path: root/libsyslog/src/syslog.rs
diff options
context:
space:
mode:
Diffstat (limited to 'libsyslog/src/syslog.rs')
-rw-r--r--libsyslog/src/syslog.rs92
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) {}
+}