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#[derive(Debug)]
20pub enum MessageFormat {
21 Plain(plain::Format),
23 Rfc5424(rfc5424::Format),
25 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#[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 self.msg_format.format(&mut v, &record)?;
57
58 self.writer.send(v)?;
59
60 Ok(())
61 }
62
63 fn flush(&self) {}
64}
65
66pub struct SyslogAppenderBuilder {
68 addrs: String,
69 msg_format: MessageFormat,
70}
71
72impl SyslogAppenderBuilder {
73 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 pub fn address(mut self, addrs: String) -> Self {
87 self.addrs = addrs;
88 self
89 }
90
91 pub fn format(mut self, mf: MessageFormat) -> Self {
95 self.msg_format = mf;
96 self
97 }
98
99 pub fn build(mut self) -> std::io::Result<SyslogAppender> {
101 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 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}