1#![warn(missing_docs)]
27
28use slog::{Drain, Level, OwnedKVList, Record};
29use std::cell::RefCell;
30use std::io::{Error, ErrorKind};
31use std::net::SocketAddr;
32use std::path::{Path, PathBuf};
33use std::sync::Mutex;
34use std::{fmt, io};
35
36use slog::KV;
37
38pub use syslog::Facility;
39
40thread_local! {
41 static TL_BUF: RefCell<Vec<u8>> = RefCell::new(Vec::with_capacity(128))
42}
43
44type SysLogger = syslog::Logger<syslog::LoggerBackend, syslog::Formatter3164>;
45
46#[inline]
47fn handle_syslog_error(e: syslog::Error) -> io::Error {
48 Error::new(ErrorKind::Other, e.to_string())
49}
50
51fn log_with_level(
52 level: slog::Level,
53 mut io: std::sync::MutexGuard<Box<SysLogger>>,
54 buf: &str,
55) -> io::Result<()> {
56 let err = match level {
57 Level::Critical => io.crit(&buf),
58 Level::Error => io.err(&buf),
59 Level::Warning => io.warning(&buf),
60 Level::Info => io.notice(&buf),
61 Level::Debug => io.info(&buf),
62 Level::Trace => io.debug(&buf),
63 };
64 err.map_err(handle_syslog_error)
65}
66
67fn syslog_format3164(
72 facility: syslog::Facility,
73 hostname: Option<String>,
74) -> syslog::Formatter3164 {
75 let path = std::env::current_exe().unwrap_or_else(|_| PathBuf::new());
76 let process = path
77 .file_name()
78 .map(|file| file.to_string_lossy().into_owned())
79 .unwrap_or_default();
80
81 syslog::Formatter3164 {
82 facility,
83 hostname,
84 process,
85 pid: std::process::id() as i32,
86 }
87}
88
89pub struct Streamer3164 {
94 io: Mutex<Box<SysLogger>>,
95 format: Format3164,
96 level: Level,
97}
98
99fn get_default_level() -> Level {
100 Level::Info
101}
102
103impl Streamer3164 {
104 pub fn new_with_level(logger: Box<SysLogger>, level: Level) -> Self {
106 Streamer3164 {
107 io: Mutex::new(logger),
108 format: Format3164::new(),
109 level,
110 }
111 }
112
113 pub fn new(logger: Box<SysLogger>) -> Self {
115 let level = get_default_level();
116 Self::new_with_level(logger, level)
117 }
118}
119
120impl Drain for Streamer3164 {
121 type Err = io::Error;
122 type Ok = ();
123
124 fn log(&self, info: &Record, logger_values: &OwnedKVList) -> io::Result<()> {
125 if self.level.as_usize() < info.level().as_usize() {
126 return Ok(());
127 }
128 TL_BUF.with(|buf| {
129 let mut buf = buf.borrow_mut();
130 let res = {
131 || {
132 self.format.format(&mut *buf, info, logger_values)?;
133 let io = self
134 .io
135 .lock()
136 .map_err(|_| Error::new(ErrorKind::Other, "locking error"))?;
137
138 let buf = String::from_utf8_lossy(&buf);
139
140 log_with_level(info.level(), io, &buf)
141 }
142 }();
143 buf.clear();
144 res
145 })
146 }
147}
148
149#[derive(Default)]
151pub struct Format3164;
152
153impl Format3164 {
154 pub fn new() -> Self {
156 Format3164
157 }
158
159 fn format(
160 &self,
161 io: &mut dyn io::Write,
162 record: &Record,
163 logger_kv: &OwnedKVList,
164 ) -> io::Result<()> {
165 write!(io, "{}", record.msg())?;
166
167 let mut ser = KeyValueSerializer::new(io);
168 {
169 logger_kv.serialize(record, &mut ser)?;
170 record.kv().serialize(record, &mut ser)?;
171 }
172 Ok(())
173 }
174}
175
176struct KeyValueSerializer<W: io::Write> {
178 io: W,
179}
180
181impl<W: io::Write> KeyValueSerializer<W> {
182 fn new(io: W) -> Self {
183 KeyValueSerializer { io }
184 }
185}
186
187impl<W: io::Write> slog::Serializer for KeyValueSerializer<W> {
188 fn emit_arguments(&mut self, key: &str, val: &fmt::Arguments) -> slog::Result {
189 write!(self.io, ", {}: {}", key, val)?;
190 Ok(())
191 }
192}
193
194enum SyslogKind {
195 Unix {
196 path: PathBuf,
197 },
198 Tcp {
199 server: SocketAddr,
200 hostname: String,
201 },
202 Udp {
203 local: SocketAddr,
204 host: SocketAddr,
205 hostname: String,
206 },
207}
208
209pub struct SyslogBuilder {
211 facility: Option<syslog::Facility>,
212 level: Level,
213 logkind: Option<SyslogKind>,
214}
215impl Default for SyslogBuilder {
216 fn default() -> Self {
217 Self {
218 facility: None,
219 level: Level::Trace,
220 logkind: None,
221 }
222 }
223}
224impl SyslogBuilder {
225 pub fn new() -> SyslogBuilder {
229 Self::default()
230 }
231
232 pub fn facility(self, facility: syslog::Facility) -> Self {
234 let mut s = self;
235 s.facility = Some(facility);
236 s
237 }
238
239 pub fn level(self, lvl: slog::Level) -> Self {
241 let mut s = self;
242 s.level = lvl;
243 s
244 }
245
246 pub fn udp<S: AsRef<str>>(self, local: SocketAddr, host: SocketAddr, hostname: S) -> Self {
248 let mut s = self;
249 let hostname = hostname.as_ref().to_string();
250 s.logkind = Some(SyslogKind::Udp {
251 local,
252 host,
253 hostname,
254 });
255 s
256 }
257
258 pub fn tcp<S: AsRef<str>>(self, server: SocketAddr, hostname: S) -> Self {
260 let mut s = self;
261 let hostname = hostname.as_ref().to_string();
262 s.logkind = Some(SyslogKind::Tcp { server, hostname });
263 s
264 }
265
266 pub fn unix<P: AsRef<Path>>(self, path: P) -> Self {
268 let mut s = self;
269 let path = path.as_ref().to_path_buf();
270 s.logkind = Some(SyslogKind::Unix { path });
271 s
272 }
273
274 pub fn start(self) -> io::Result<Streamer3164> {
276 let facility = match self.facility {
277 Option::Some(x) => x,
278 Option::None => {
279 return Err(Error::new(
280 ErrorKind::Other,
281 "facility must be provided to the builder",
282 ));
283 }
284 };
285 let logkind = match self.logkind {
286 Option::Some(l) => l,
287 Option::None => {
288 return Err(Error::new(
289 ErrorKind::Other,
290 "no logger kind provided, library does not know what do initialize",
291 ));
292 }
293 };
294 let log = match logkind {
295 SyslogKind::Unix { path } => {
296 let format = syslog_format3164(facility, None);
297 syslog::unix_custom(format, path).map_err(handle_syslog_error)?
298 }
299 SyslogKind::Udp {
300 local,
301 host,
302 hostname,
303 } => {
304 let format = syslog_format3164(facility, Some(hostname));
305 syslog::udp(format, local, host).map_err(handle_syslog_error)?
306 }
307 SyslogKind::Tcp { server, hostname } => {
308 let format = syslog_format3164(facility, Some(hostname));
309 syslog::tcp(format, server).map_err(handle_syslog_error)?
310 }
311 };
312 Ok(Streamer3164::new_with_level(Box::new(log), self.level))
313 }
314}
315
316pub fn unix_3164_with_level(facility: syslog::Facility, level: Level) -> io::Result<Streamer3164> {
318 let format = syslog_format3164(facility, None);
319 syslog::unix(format)
320 .map(Box::new)
321 .map(|logger| Streamer3164::new_with_level(logger, level))
322 .map_err(handle_syslog_error)
323}
324
325pub fn unix_3164(facility: syslog::Facility) -> io::Result<Streamer3164> {
327 let format = syslog_format3164(facility, None);
328 syslog::unix(format)
329 .map(Box::new)
330 .map(Streamer3164::new)
331 .map_err(handle_syslog_error)
332}