log4rs_syslog_net/
lib.rs

1use log::Record;
2use log4rs::append::Append;
3use log4rs::encode::Encode;
4use std::error::Error;
5use std::io::Write;
6use std::net::TcpStream;
7use std::sync::mpsc::{sync_channel, SyncSender};
8use std::sync::Arc;
9
10pub mod consts;
11pub mod plain;
12pub mod rfc5424;
13pub mod rfc5425;
14
15const DEFAULT_PORT: u16 = 514;
16const DEFAULT_ADDRESS: &str = "localhost:514";
17
18/// Syslog message format.
19#[derive(Debug)]
20pub enum MessageFormat {
21    /// No formatting is applied.
22    Plain(plain::Format),
23    /// Formatting according to RFC5424 is applied.
24    Rfc5424(rfc5424::Format),
25    /// Formatting according to RFC5425 is applied (use with telegraf).
26    Rfc5425(rfc5425::Format),
27}
28
29impl MessageFormat {
30    fn format(
31        &self,
32        w: &mut dyn std::io::Write,
33        rec: &Record<'_>,
34    ) -> Result<(), Box<dyn Error + Sync + Send>> {
35        let mut w = log4rs::encode::writer::simple::SimpleWriter(w);
36        match self {
37            MessageFormat::Plain(fmt) => fmt.encode(&mut w, &rec),
38            MessageFormat::Rfc5424(fmt) => fmt.encode(&mut w, &rec),
39            MessageFormat::Rfc5425(fmt) => fmt.encode(&mut w, &rec),
40        }
41    }
42}
43
44/// Appender that sends log messages to syslog.
45#[derive(Debug)]
46pub struct SyslogAppender {
47    addr: String,
48    writer: SyncSender<Vec<u8>>,
49    msg_format: MessageFormat,
50}
51
52impl<'a> Append for SyslogAppender {
53    fn append(&self, record: &Record<'_>) -> Result<(), Box<dyn Error + Sync + Send>> {
54        let mut v = vec![];
55        // Format the message, which will be different based on the chosen MsgFormat
56        self.msg_format.format(&mut v, &record)?;
57
58        self.writer.send(v)?;
59
60        Ok(())
61    }
62
63    fn flush(&self) {}
64}
65
66/// Builder for `SyslogAppender`.
67pub struct SyslogAppenderBuilder {
68    addrs: String,
69    msg_format: MessageFormat,
70}
71
72impl SyslogAppenderBuilder {
73    /// Creates a `SyslogAppenderBuilder` for constructing new `SyslogAppender`.
74    pub fn new() -> SyslogAppenderBuilder {
75        SyslogAppenderBuilder {
76            addrs: DEFAULT_ADDRESS.to_string(),
77            msg_format: MessageFormat::Plain(plain::Format(Arc::new(
78                log4rs::encode::pattern::PatternEncoder::default(),
79            ))),
80        }
81    }
82
83    /// Sets network address of syslog server.
84    ///
85    /// Defaults to "localhost:514".
86    pub fn address(mut self, addrs: String) -> Self {
87        self.addrs = addrs;
88        self
89    }
90
91    /// Sets type of log message formatter.
92    ///
93    /// Defaults to `Plain`.
94    pub fn format(mut self, mf: MessageFormat) -> Self {
95        self.msg_format = mf;
96        self
97    }
98
99    /// Produces a `SyslogAppender` with parameters, supplied to the builder.
100    pub fn build(mut self) -> std::io::Result<SyslogAppender> {
101        // norm_addrs(&mut self.addrs);
102        if self.addrs.find(':').is_none() {
103            self.addrs.push(':');
104            self.addrs.push_str(&DEFAULT_PORT.to_string())
105        }
106        let (tx, rx) = sync_channel(12);
107
108        let mut conn = TcpStream::connect(&self.addrs)?;
109        let addrs = self.addrs.clone();
110
111        std::thread::spawn(move || 'outer: loop {
112            let v: Vec<u8> = rx.recv().unwrap();
113
114            // TODO: fix later
115            if let Err(e) = conn.write_all(&v) {
116                match e.kind() {
117                    std::io::ErrorKind::BrokenPipe => {
118                        'inner: loop {
119                            let new_conn = TcpStream::connect(&addrs);
120
121                            if let Ok(new_conn) = new_conn {
122                                conn = new_conn;
123                                conn.write_all(&v);
124                                conn.flush();
125                                break 'inner;
126                            } else {
127                                std::thread::sleep(std::time::Duration::from_secs(60));
128                            }
129                        }
130                    }
131                    _ => {}
132                }
133                drop(e);
134            };
135        });
136
137        let appender = SyslogAppender {
138            addr: self.addrs,
139            writer: tx,
140            msg_format: self.msg_format,
141        };
142
143        Ok(appender)
144    }
145}