1use std;
2
3use libc;
4use log;
5use log4rs;
6#[cfg(feature = "file")]
7use serde;
8
9const DEFAULT_BUF_SIZE: usize = 4096;
10
11type PersistentBuf = std::io::Cursor<Vec<u8>>;
12
13thread_local! {
14 static PERSISTENT_BUF: std::cell::RefCell<PersistentBuf> =
15 std::cell::RefCell::new(PersistentBuf::new(Vec::with_capacity(DEFAULT_BUF_SIZE)));
16}
17
18struct BufWriter {}
19
20impl BufWriter {
21 fn new() -> Self {
22 PERSISTENT_BUF.with(|pers_buf| pers_buf.borrow_mut().set_position(0));
23 Self {}
24 }
25
26 fn as_c_str(&mut self) -> *const libc::c_char {
27 use std::io::Write;
28
29 PERSISTENT_BUF.with(|pers_buf| {
30 let mut pers_buf = pers_buf.borrow_mut();
31 pers_buf.write_all(&[0; 1]).unwrap();
32 pers_buf.get_ref().as_ptr() as *const libc::c_char
33 })
34 }
35}
36
37impl std::io::Write for BufWriter {
38 fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
39 PERSISTENT_BUF.with(|pers_buf| pers_buf.borrow_mut().write(buf))
40 }
41
42 fn flush(&mut self) -> std::io::Result<()> {
43 PERSISTENT_BUF.with(|pers_buf| pers_buf.borrow_mut().flush())
44 }
45}
46
47impl log4rs::encode::Write for BufWriter {}
48
49pub type LevelMap = Fn(log::Level) -> libc::c_int + Send + Sync;
51
52pub struct SyslogAppender {
54 encoder: Box<log4rs::encode::Encode>,
55 level_map: Option<Box<LevelMap>>,
56}
57
58impl std::fmt::Debug for SyslogAppender {
59 fn fmt(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
60 write!(
61 formatter,
62 "SyslogAppender {{encoder: {:?}, level_map: {}}}",
63 self.encoder,
64 match self.level_map {
65 Some(_) => "Some(_)",
66 None => "None",
67 }
68 )
69 }
70}
71
72impl SyslogAppender {
73 pub fn builder() -> SyslogAppenderBuilder {
75 SyslogAppenderBuilder {
76 encoder: None,
77 openlog_args: None,
78 level_map: None,
79 }
80 }
81}
82
83impl log4rs::append::Append for SyslogAppender {
84 fn append(&self, record: &log::Record) -> std::result::Result<(), Box<std::error::Error + Sync + Send>> {
85 let mut buf = BufWriter::new();
86
87 self.encoder.encode(&mut buf, record)?;
88
89 let level = match self.level_map {
90 Some(ref level_map) => level_map(record.level()),
91
92 None => match record.level() {
93 log::Level::Error => libc::LOG_ERR,
94 log::Level::Warn => libc::LOG_WARNING,
95 log::Level::Info => libc::LOG_INFO,
96 log::Level::Debug | log::Level::Trace => libc::LOG_DEBUG,
97 },
98 };
99
100 unsafe {
101 libc::syslog(
102 level,
103 b"%s\0".as_ptr() as *const libc::c_char,
104 buf.as_c_str(),
105 );
106 }
107
108 Ok(())
109 }
110
111 fn flush(&self) {}
112}
113
114bitflags! {
115 pub struct LogOption: libc::c_int {
117 const LOG_CONS = libc::LOG_CONS;
119 const LOG_NDELAY = libc::LOG_NDELAY;
121 const LOG_NOWAIT = libc::LOG_NOWAIT;
124 const LOG_ODELAY = libc::LOG_ODELAY;
127 const LOG_PERROR = libc::LOG_PERROR;
129 const LOG_PID = libc::LOG_PID;
131 }
132}
133
134#[cfg(feature = "file")]
135struct LogOptionVisitor;
136
137#[cfg(feature = "file")]
138impl<'de> serde::de::Visitor<'de> for LogOptionVisitor {
139 type Value = LogOption;
140
141 fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
142 formatter.write_str("list of flags separated by \"|\"")
143 }
144
145 fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
146 where
147 E: serde::de::Error,
148 {
149 let mut flags = LogOption::empty();
150
151 let value = value.trim();
152 if !value.is_empty() {
153 for str_flag in value.split('|') {
154 let str_flag = str_flag.trim();
155 match str_flag {
156 "LOG_CONS" => flags |= LogOption::LOG_CONS,
157 "LOG_NDELAY" => flags |= LogOption::LOG_NDELAY,
158 "LOG_NOWAIT" => flags |= LogOption::LOG_NOWAIT,
159 "LOG_ODELAY" => flags |= LogOption::LOG_ODELAY,
160 "LOG_PERROR" => flags |= LogOption::LOG_PERROR,
161 "LOG_PID" => flags |= LogOption::LOG_PID,
162 unknown => return Err(E::custom(format!("Unknown syslog flag: \"{}\"", unknown))),
163 }
164 }
165 }
166
167 Ok(flags)
168 }
169}
170
171#[cfg(feature = "file")]
172impl<'de> serde::de::Deserialize<'de> for LogOption {
173 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
174 where
175 D: serde::de::Deserializer<'de>,
176 {
177 deserializer.deserialize_str(LogOptionVisitor)
178 }
179}
180
181#[derive(Debug)]
182#[cfg_attr(feature = "file", derive(Deserialize))]
183pub enum Facility {
185 Auth,
187 AuthPriv,
189 Cron,
191 Daemon,
193 Ftp,
195 Kern,
197 Local0,
199 Local1,
201 Local2,
203 Local3,
205 Local4,
207 Local5,
209 Local6,
211 Local7,
213 Lpr,
215 Mail,
217 News,
219 Syslog,
221 User,
223 Uucp,
225}
226
227impl Into<libc::c_int> for Facility {
228 fn into(self) -> libc::c_int {
229 match self {
230 Facility::Auth => libc::LOG_AUTH,
231 Facility::AuthPriv => libc::LOG_AUTHPRIV,
232 Facility::Cron => libc::LOG_CRON,
233 Facility::Daemon => libc::LOG_DAEMON,
234 Facility::Ftp => libc::LOG_FTP,
235 Facility::Kern => libc::LOG_KERN,
236 Facility::Local0 => libc::LOG_LOCAL0,
237 Facility::Local1 => libc::LOG_LOCAL1,
238 Facility::Local2 => libc::LOG_LOCAL2,
239 Facility::Local3 => libc::LOG_LOCAL3,
240 Facility::Local4 => libc::LOG_LOCAL4,
241 Facility::Local5 => libc::LOG_LOCAL5,
242 Facility::Local6 => libc::LOG_LOCAL6,
243 Facility::Local7 => libc::LOG_LOCAL7,
244 Facility::Lpr => libc::LOG_LPR,
245 Facility::Mail => libc::LOG_MAIL,
246 Facility::News => libc::LOG_NEWS,
247 Facility::Syslog => libc::LOG_SYSLOG,
248 Facility::User => libc::LOG_USER,
249 Facility::Uucp => libc::LOG_UUCP,
250 }
251 }
252}
253
254struct OpenLogArgs {
255 ident: String,
256 log_option: LogOption,
257 facility: Facility,
258}
259
260struct IdentHolder {
261 ident: Option<String>,
262}
263
264impl IdentHolder {
265 fn new() -> Self {
266 Self { ident: None }
267 }
268
269 fn openlog(&mut self, mut args: OpenLogArgs) {
270 args.ident.push('\0');
271
272 unsafe {
273 libc::openlog(
274 args.ident.as_ptr() as *const libc::c_char,
275 args.log_option.bits(),
276 args.facility.into(),
277 );
278 }
279
280 self.ident = Some(args.ident);
282 }
283
284 fn closelog(&mut self) {
285 if self.ident.is_some() {
286 unsafe {
287 libc::closelog();
288 }
289 }
290 }
291
292 fn no_openlog(&mut self) {
293 self.closelog();
294 self.ident = None;
295 }
296}
297
298impl Drop for IdentHolder {
299 fn drop(&mut self) {
300 self.closelog();
302 }
303}
304
305lazy_static! {
306 static ref IDENT_HOLDER: std::sync::Mutex<IdentHolder> = std::sync::Mutex::new(IdentHolder::new());
307}
308
309pub struct SyslogAppenderBuilder {
311 encoder: Option<Box<log4rs::encode::Encode>>,
312 openlog_args: Option<OpenLogArgs>,
313 level_map: Option<Box<LevelMap>>,
314}
315
316impl SyslogAppenderBuilder {
317 pub fn encoder(mut self, encoder: Box<log4rs::encode::Encode>) -> Self {
319 self.encoder = Some(encoder);
320 self
321 }
322
323 pub fn openlog(mut self, ident: &str, option: LogOption, facility: Facility) -> Self {
325 self.openlog_args = Some(OpenLogArgs {
326 ident: String::from(ident),
327 log_option: option,
328 facility,
329 });
330 self
331 }
332
333 pub fn level_map(mut self, level_map: Box<LevelMap>) -> Self {
335 self.level_map = Some(level_map);
336 self
337 }
338
339 pub fn build(self) -> SyslogAppender {
341 self.openlog_args.map_or_else(
342 || IDENT_HOLDER.lock().unwrap().no_openlog(),
343 |openlog_args| IDENT_HOLDER.lock().unwrap().openlog(openlog_args),
344 );
345
346 SyslogAppender {
347 encoder: self.encoder
348 .unwrap_or_else(|| Box::new(log4rs::encode::pattern::PatternEncoder::default())),
349 level_map: self.level_map,
350 }
351 }
352}
353
354#[cfg(all(feature = "unstable", test))]
355mod bench {
356 use test;
357
358 fn bench(bencher: &mut test::Bencher, data: &[u8]) {
359 use std::io::Write;
360
361 bencher.iter(|| {
362 let mut buf = super::BufWriter::new();
363 buf.write_all(data).unwrap();
364 buf.as_c_str()
365 })
366 }
367
368 #[bench]
369 fn buf_writer_no_realloc(bencher: &mut test::Bencher) {
370 bench(bencher, &[b'x'; super::DEFAULT_BUF_SIZE - 1])
371 }
372
373 #[bench]
374 fn buf_writer_realloc(bencher: &mut test::Bencher) {
375 bench(bencher, &[b'x'; super::DEFAULT_BUF_SIZE])
376 }
377}