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
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
#![forbid(unsafe_code)]

use std;

use libc;
use log;
use log4rs;
use syslog;

#[derive(Deserialize)]
struct SyslogAppenderOpenlogConfig {
    ident: String,
    option: syslog::LogOption,
    facility: syslog::Facility,
}

#[derive(PartialEq, Eq, Hash, PartialOrd, Ord, Deserialize)]
#[allow(non_camel_case_types)]
enum FakeLibcLogLevel {
    LOG_EMERG,
    LOG_ALERT,
    LOG_CRIT,
    LOG_ERR,
    LOG_WARNING,
    LOG_NOTICE,
    LOG_INFO,
    LOG_DEBUG,
}

type LevelMapConf = std::collections::BTreeMap<log::Level, FakeLibcLogLevel>;

#[derive(Deserialize)]
struct SyslogAppenderConfig {
    openlog: Option<SyslogAppenderOpenlogConfig>,
    encoder: Option<log4rs::encode::EncoderConfig>,
    level_map: Option<LevelMapConf>,
}

struct SyslogAppenderDeserializer;

impl log4rs::file::Deserialize for SyslogAppenderDeserializer {
    type Trait = log4rs::append::Append;
    type Config = SyslogAppenderConfig;

    fn deserialize(
        &self,
        config: Self::Config,
        deserializers: &log4rs::file::Deserializers,
    ) -> Result<Box<Self::Trait>, Box<std::error::Error + Sync + Send>> {
        let mut builder = syslog::SyslogAppender::builder();

        if let Some(openlog_conf) = config.openlog {
            builder = builder.openlog(
                &openlog_conf.ident,
                openlog_conf.option,
                openlog_conf.facility,
            );
        };

        if let Some(encoder_conf) = config.encoder {
            builder = builder.encoder(deserializers.deserialize(&encoder_conf.kind, encoder_conf.config)?);
        }

        if let Some(level_map) = config.level_map {
            let mut map = std::collections::BTreeMap::new();
            for (level, libc_level) in level_map {
                let libc_level = match libc_level {
                    FakeLibcLogLevel::LOG_EMERG => libc::LOG_EMERG,
                    FakeLibcLogLevel::LOG_ALERT => libc::LOG_ALERT,
                    FakeLibcLogLevel::LOG_CRIT => libc::LOG_CRIT,
                    FakeLibcLogLevel::LOG_ERR => libc::LOG_ERR,
                    FakeLibcLogLevel::LOG_WARNING => libc::LOG_WARNING,
                    FakeLibcLogLevel::LOG_NOTICE => libc::LOG_NOTICE,
                    FakeLibcLogLevel::LOG_INFO => libc::LOG_INFO,
                    FakeLibcLogLevel::LOG_DEBUG => libc::LOG_DEBUG,
                };
                let _ = map.insert(level, libc_level);
            }

            for level in &[
                log::Level::Error,
                log::Level::Warn,
                log::Level::Info,
                log::Level::Debug,
                log::Level::Trace,
            ] {
                let _ = map.get(level)
                    .ok_or_else(|| format!("Log level missing in map: {:?}", level))?;
            }

            builder = builder.level_map(Box::new(move |l| map[&l]));
        }

        Ok(Box::new(builder.build()))
    }
}

/// Register deserializer for creating syslog appender based on log4rs configuration file.
///
/// See `./examples/from_conf.rs` for full example.
///
/// # Examples
///
/// ```
/// extern crate log4rs;
/// extern crate log4rs_syslog;
///
/// let mut deserializers = log4rs::file::Deserializers::new();
/// log4rs_syslog::register(&mut deserializers);
/// let result = log4rs::init_file("/path/to/log-conf.yaml", deserializers);
/// ```
pub fn register(deserializers: &mut log4rs::file::Deserializers) {
    deserializers.insert("libc-syslog", SyslogAppenderDeserializer);
}