summaryrefslogtreecommitdiff
path: root/libsyslog/src/syslog.rs
blob: 6b36147c371bb4d271c91ae77ac686022635c280 (plain)
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
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) {}
}