1#![warn(missing_docs)]
27
28use slog::{Drain, Level, OwnedKVList, Record};
29use std::{fmt, io};
30use std::sync::Mutex;
31use std::cell::RefCell;
32use std::path::{Path, PathBuf};
33use std::net::SocketAddr;
34use std::io::{Error, ErrorKind};
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{
49 Error::new(ErrorKind::Other, e.to_string())
50}
51
52fn log_with_level(level: slog::Level, mut io: std::sync::MutexGuard<Box<SysLogger>>, buf: &str) -> io::Result<()> {
53 let err = match level {
54 Level::Critical => io.crit(&buf),
55 Level::Error => io.err(&buf),
56 Level::Warning => io.warning(&buf),
57 Level::Info => io.notice(&buf),
58 Level::Debug => io.info(&buf),
59 Level::Trace => io.debug(&buf),
60 };
61 err.map_err(handle_syslog_error)
62}
63
64fn syslog_format3164(facility: syslog::Facility, hostname: Option<String>) -> syslog::Formatter3164 {
69 let path = std::env::current_exe()
70 .unwrap_or_else(|_| PathBuf::new());
71 let process = path.file_name()
72 .map(|file| file.to_string_lossy().into_owned())
73 .unwrap_or_else(|| String::new());
74
75 syslog::Formatter3164 {
76 facility,
77 hostname,
78 process,
79 pid: std::process::id() as i32,
80 }
81}
82
83pub struct Streamer3164 {
88 io: Mutex<Box<SysLogger>>,
89 format: Format3164,
90 level: Level,
91}
92
93#[cfg(debug_assertions)]
94fn get_default_level() -> Level {
95 if cfg!(feature = "max_level_trace") {
96 Level::Trace
97 } else if cfg!(feature = "max_level_debug") {
98 Level::Debug
99 } else if cfg!(feature = "max_level_info") {
100 Level::Info
101 } else if cfg!(feature = "max_level_warn") {
102 Level::Warning
103 } else if cfg!(feature = "max_level_error") {
104 Level::Error
105 } else { Level::Critical
107 }
108}
109
110#[cfg(not(debug_assertions))]
111fn get_default_level() -> Level {
112 if cfg!(feature = "release_max_level_trace") {
113 Level::Trace
114 } else if cfg!(feature = "release_max_level_debug") {
115 Level::Debug
116 } else if cfg!(feature = "release_max_level_info") {
117 Level::Info
118 } else if cfg!(feature = "release_max_level_warn") {
119 Level::Warning
120 } else if cfg!(feature = "release_max_level_error") {
121 Level::Error
122 } else { Level::Critical
124 }
125}
126
127impl Streamer3164 {
128 pub fn new_with_level(logger: Box<SysLogger>, level: Level) -> Self {
130 Streamer3164 {
131 io: Mutex::new(logger),
132 format: Format3164::new(),
133 level,
134 }
135 }
136
137 pub fn new(logger: Box<SysLogger>) -> Self {
139 let level = get_default_level();
140 Self::new_with_level(logger, level)
141 }
142}
143
144impl Drain for Streamer3164 {
145 type Err = io::Error;
146 type Ok = ();
147
148 fn log(&self, info: &Record, logger_values: &OwnedKVList) -> io::Result<()> {
149 if self.level > info.level() {
150 return Ok(())
151 }
152 TL_BUF.with(|buf| {
153 let mut buf = buf.borrow_mut();
154 let res = {
155 || {
156 self.format.format(&mut *buf, info, logger_values)?;
157 let io =
158 self.io
159 .lock()
160 .map_err(|_| Error::new(ErrorKind::Other, "locking error"))?;
161
162 let buf = String::from_utf8_lossy(&buf);
163
164 log_with_level(info.level(), io, &buf)
165 }
166 }();
167 buf.clear();
168 res
169 })
170 }
171}
172
173pub struct Format3164;
175
176impl Format3164 {
177 pub fn new() -> Self {
179 Format3164
180 }
181
182 fn format(
183 &self,
184 io: &mut dyn io::Write,
185 record: &Record,
186 logger_kv: &OwnedKVList,
187 ) -> io::Result<()> {
188 write!(io, "{}", record.msg())?;
189
190 let mut ser = KSV::new(io);
191 {
192 logger_kv.serialize(record, &mut ser)?;
193 record.kv().serialize(record, &mut ser)?;
194 }
195 Ok(())
196 }
197}
198
199struct KSV<W: io::Write> {
201 io: W,
202}
203
204impl<W: io::Write> KSV<W> {
205 fn new(io: W) -> Self {
206 KSV { io: io }
207 }
208}
209
210impl<W: io::Write> slog::Serializer for KSV<W> {
211 fn emit_arguments(&mut self, key: &str, val: &fmt::Arguments) -> slog::Result {
212 write!(self.io, ", {}: {}", key, val)?;
213 Ok(())
214 }
215}
216
217enum SyslogKind {
218 Unix {
219 path: PathBuf,
220 },
221 Tcp {
222 server: SocketAddr,
223 hostname: String,
224 },
225 Udp {
226 local: SocketAddr,
227 host: SocketAddr,
228 hostname: String,
229 },
230}
231
232pub struct SyslogBuilder {
234 facility: Option<syslog::Facility>,
235 level: Level,
236 logkind: Option<SyslogKind>,
237}
238impl Default for SyslogBuilder {
239 fn default() -> Self {
240 Self {
241 facility: None,
242 level: Level::Trace,
243 logkind: None,
244 }
245 }
246}
247impl SyslogBuilder {
248 pub fn new() -> SyslogBuilder {
252 Self::default()
253 }
254
255 pub fn facility(self, facility: syslog::Facility) -> Self {
257 let mut s = self;
258 s.facility = Some(facility);
259 s
260 }
261
262 pub fn level(self, lvl: slog::Level) -> Self {
264 let mut s = self;
265 s.level = lvl;
266 s
267 }
268
269 pub fn udp<S: AsRef<str>>(self, local: SocketAddr, host: SocketAddr, hostname: S) -> Self {
271 let mut s = self;
272 let hostname = hostname.as_ref().to_string();
273 s.logkind = Some(SyslogKind::Udp {
274 local,
275 host,
276 hostname,
277 });
278 s
279 }
280
281 pub fn tcp<S: AsRef<str>>(self, server: SocketAddr, hostname: S) -> Self {
283 let mut s = self;
284 let hostname = hostname.as_ref().to_string();
285 s.logkind = Some(SyslogKind::Tcp { server, hostname });
286 s
287 }
288
289 pub fn unix<P: AsRef<Path>>(self, path: P) -> Self {
291 let mut s = self;
292 let path = path.as_ref().to_path_buf();
293 s.logkind = Some(SyslogKind::Unix { path });
294 s
295 }
296
297 pub fn start(self) -> io::Result<Streamer3164> {
299 let facility = match self.facility {
300 Option::Some(x) => x,
301 Option::None => {
302 return Err(Error::new(
303 ErrorKind::Other,
304 "facility must be provided to the builder",
305 ));
306 }
307 };
308 let logkind = match self.logkind {
309 Option::Some(l) => l,
310 Option::None => {
311 return Err(Error::new(
312 ErrorKind::Other,
313 "no logger kind provided, library does not know what do initialize",
314 ));
315 }
316 };
317 let log = match logkind {
318 SyslogKind::Unix { path } => {
319 let format = syslog_format3164(facility, None);
320 syslog::unix_custom(format, path).map_err(handle_syslog_error)?
321 }
322 SyslogKind::Udp {
323 local,
324 host,
325 hostname,
326 } => {
327 let format = syslog_format3164(facility, Some(hostname));
328 syslog::udp(format, local, host).map_err(handle_syslog_error)?
329 },
330 SyslogKind::Tcp { server, hostname } => {
331 let format = syslog_format3164(facility, Some(hostname));
332 syslog::tcp(format, server).map_err(handle_syslog_error)?
333 },
334 };
335 Ok(Streamer3164::new_with_level(Box::new(log), self.level))
336 }
337}
338
339pub fn unix_3164_with_level(facility: syslog::Facility, level: Level) -> io::Result<Streamer3164> {
341 let format = syslog_format3164(facility, None);
342 syslog::unix(format)
343 .map(Box::new)
344 .map(|logger| Streamer3164::new_with_level(logger, level))
345 .map_err(handle_syslog_error)
346}
347
348pub fn unix_3164(facility: syslog::Facility) -> io::Result<Streamer3164> {
350 let format = syslog_format3164(facility, None);
351 syslog::unix(format)
352 .map(Box::new)
353 .map(Streamer3164::new)
354 .map_err(handle_syslog_error)
355}