1extern crate log;
55extern crate time;
56
57use std::env;
58use std::fmt::{self, Arguments};
59use std::io::{self, BufWriter, Write};
60use std::net::{SocketAddr, TcpStream, ToSocketAddrs, UdpSocket};
61#[cfg(unix)]
62use std::os::unix::net::{UnixDatagram, UnixStream};
63use std::path::Path;
64use std::process;
65use std::sync::{Arc, Mutex};
66
67use log::{Level, Log, Metadata, Record};
68
69mod errors;
70mod facility;
71mod format;
72#[cfg(test)]
73mod tests;
74
75pub use errors::*;
76pub use facility::Facility;
77pub use format::Severity;
78pub use format::{Formatter3164, Formatter5424, LogFormat};
79
80pub type Priority = u8;
81
82const UNIX_SOCK_PATHS: [&str; 3] = ["/dev/log", "/var/run/syslog", "/var/run/log"];
83
84pub struct Logger<Backend: Write, Formatter> {
86 pub formatter: Formatter,
87 pub backend: Backend,
88}
89
90impl<W: Write, F> Logger<W, F> {
91 pub fn new(backend: W, formatter: F) -> Self {
92 Logger { backend, formatter }
93 }
94
95 pub fn emerg<T>(&mut self, message: T) -> Result<()>
96 where
97 F: LogFormat<T>,
98 {
99 self.formatter.emerg(&mut self.backend, message)
100 }
101
102 pub fn alert<T>(&mut self, message: T) -> Result<()>
103 where
104 F: LogFormat<T>,
105 {
106 self.formatter.alert(&mut self.backend, message)
107 }
108
109 pub fn crit<T>(&mut self, message: T) -> Result<()>
110 where
111 F: LogFormat<T>,
112 {
113 self.formatter.crit(&mut self.backend, message)
114 }
115
116 pub fn err<T>(&mut self, message: T) -> Result<()>
117 where
118 F: LogFormat<T>,
119 {
120 self.formatter.err(&mut self.backend, message)
121 }
122
123 pub fn warning<T>(&mut self, message: T) -> Result<()>
124 where
125 F: LogFormat<T>,
126 {
127 self.formatter.warning(&mut self.backend, message)
128 }
129
130 pub fn notice<T>(&mut self, message: T) -> Result<()>
131 where
132 F: LogFormat<T>,
133 {
134 self.formatter.notice(&mut self.backend, message)
135 }
136
137 pub fn info<T>(&mut self, message: T) -> Result<()>
138 where
139 F: LogFormat<T>,
140 {
141 self.formatter.info(&mut self.backend, message)
142 }
143
144 pub fn debug<T>(&mut self, message: T) -> Result<()>
145 where
146 F: LogFormat<T>,
147 {
148 self.formatter.debug(&mut self.backend, message)
149 }
150}
151
152pub enum LoggerBackend {
153 #[cfg(unix)]
155 Unix(UnixDatagram),
156 #[cfg(not(unix))]
157 Unix(()),
158 #[cfg(unix)]
159 UnixStream(BufWriter<UnixStream>),
160 #[cfg(not(unix))]
161 UnixStream(()),
162 Udp(UdpSocket, SocketAddr),
163 Tcp(BufWriter<TcpStream>),
164}
165
166impl Write for LoggerBackend {
167 fn write(&mut self, message: &[u8]) -> io::Result<usize> {
169 match *self {
170 #[cfg(unix)]
171 LoggerBackend::Unix(ref dgram) => dgram.send(message),
172 #[cfg(unix)]
173 LoggerBackend::UnixStream(ref mut socket) => {
174 let null = [0; 1];
175 socket
176 .write(message)
177 .and_then(|sz| socket.write(&null).map(|_| sz))
178 .and_then(|sz| socket.flush().map(|_| sz))
179 }
180 LoggerBackend::Udp(ref socket, ref addr) => socket.send_to(message, addr),
181 LoggerBackend::Tcp(ref mut socket) => socket
182 .write(message)
183 .and_then(|sz| socket.flush().map(|_| sz)),
184 #[cfg(not(unix))]
185 LoggerBackend::Unix(_) | LoggerBackend::UnixStream(_) => {
186 Err(io::Error::new(io::ErrorKind::Other, "unsupported platform"))
187 }
188 }
189 }
190
191 fn write_fmt(&mut self, args: Arguments) -> io::Result<()> {
192 match *self {
193 #[cfg(unix)]
194 LoggerBackend::Unix(ref dgram) => {
195 let message = fmt::format(args);
196 dgram.send(message.as_bytes()).map(|_| ())
197 }
198 #[cfg(unix)]
199 LoggerBackend::UnixStream(ref mut socket) => {
200 let null = [0; 1];
201 socket
202 .write_fmt(args)
203 .and_then(|_| socket.write(&null).map(|_| ()))
204 .and_then(|sz| socket.flush().map(|_| sz))
205 }
206 LoggerBackend::Udp(ref socket, ref addr) => {
207 let message = fmt::format(args);
208 socket.send_to(message.as_bytes(), addr).map(|_| ())
209 }
210 LoggerBackend::Tcp(ref mut socket) => socket
211 .write_fmt(args)
212 .and_then(|sz| socket.flush().map(|_| sz)),
213 #[cfg(not(unix))]
214 LoggerBackend::Unix(_) | LoggerBackend::UnixStream(_) => {
215 Err(io::Error::new(io::ErrorKind::Other, "unsupported platform"))
216 }
217 }
218 }
219
220 fn flush(&mut self) -> io::Result<()> {
221 match *self {
222 #[cfg(unix)]
223 LoggerBackend::Unix(_) => Ok(()),
224 #[cfg(unix)]
225 LoggerBackend::UnixStream(ref mut socket) => socket.flush(),
226 LoggerBackend::Udp(_, _) => Ok(()),
227 LoggerBackend::Tcp(ref mut socket) => socket.flush(),
228 #[cfg(not(unix))]
229 LoggerBackend::Unix(_) | LoggerBackend::UnixStream(_) => {
230 Err(io::Error::new(io::ErrorKind::Other, "unsupported platform"))
231 }
232 }
233 }
234}
235
236#[cfg(unix)]
238pub fn unix<F: Clone>(formatter: F) -> Result<Logger<LoggerBackend, F>> {
239 UNIX_SOCK_PATHS
240 .iter()
241 .find_map(|path| {
242 unix_connect(formatter.clone(), *path).map_or_else(
243 |e| {
244 if let Error::Io(ref io_err) = e {
245 if io_err.kind() == io::ErrorKind::NotFound {
246 None } else {
248 Some(Err(e))
249 }
250 } else {
251 Some(Err(e))
252 }
253 },
254 |logger| Some(Ok(logger)),
255 )
256 })
257 .transpose()
258 .map_err(|e| Error::Initialization(Box::new(e)))?
259 .ok_or_else(|| Error::Initialization("unix socket paths not found".into()))
260}
261
262#[cfg(not(unix))]
263pub fn unix<F: Clone>(_formatter: F) -> Result<Logger<LoggerBackend, F>> {
264 Err(ErrorKind::UnsupportedPlatform)?
265}
266
267#[cfg(unix)]
269pub fn unix_custom<P: AsRef<Path>, F>(formatter: F, path: P) -> Result<Logger<LoggerBackend, F>> {
270 unix_connect(formatter, path).map_err(|e| Error::Initialization(Box::new(e)))
271}
272
273#[cfg(not(unix))]
274pub fn unix_custom<P: AsRef<Path>, F>(_formatter: F, _path: P) -> Result<Logger<LoggerBackend, F>> {
275 Err(ErrorKind::UnsupportedPlatform)?
276}
277
278#[cfg(unix)]
279fn unix_connect<P: AsRef<Path>, F>(formatter: F, path: P) -> Result<Logger<LoggerBackend, F>> {
280 let sock = UnixDatagram::unbound()?;
281 match sock.connect(&path) {
282 Ok(()) => Ok(Logger {
283 formatter,
284 backend: LoggerBackend::Unix(sock),
285 }),
286 Err(ref e) if e.raw_os_error() == Some(libc::EPROTOTYPE) => {
287 let sock = UnixStream::connect(path)?;
288 Ok(Logger {
289 formatter,
290 backend: LoggerBackend::UnixStream(BufWriter::new(sock)),
291 })
292 }
293 Err(e) => Err(e.into()),
294 }
295}
296
297pub fn udp<T: ToSocketAddrs, U: ToSocketAddrs, F>(
299 formatter: F,
300 local: T,
301 server: U,
302) -> Result<Logger<LoggerBackend, F>> {
303 server
304 .to_socket_addrs()
305 .map_err(|e| Error::Initialization(Box::new(e)))
306 .and_then(|mut server_addr_opt| {
307 server_addr_opt
308 .next()
309 .ok_or_else(|| Error::Initialization("no server address".into()))
310 })
311 .and_then(|server_addr| {
312 UdpSocket::bind(local)
313 .map_err(|e| Error::Initialization(Box::new(e)))
314 .map(|socket| Logger {
315 formatter,
316 backend: LoggerBackend::Udp(socket, server_addr),
317 })
318 })
319}
320
321pub fn tcp<T: ToSocketAddrs, F>(formatter: F, server: T) -> Result<Logger<LoggerBackend, F>> {
323 TcpStream::connect(server)
324 .map_err(|e| Error::Initialization(e.into()))
325 .map(|socket| Logger {
326 formatter,
327 backend: LoggerBackend::Tcp(BufWriter::new(socket)),
328 })
329}
330
331pub struct BasicLogger {
332 logger: Arc<Mutex<Logger<LoggerBackend, Formatter3164>>>,
333}
334
335impl BasicLogger {
336 pub fn new(logger: Logger<LoggerBackend, Formatter3164>) -> BasicLogger {
337 BasicLogger {
338 logger: Arc::new(Mutex::new(logger)),
339 }
340 }
341}
342
343#[allow(unused_variables, unused_must_use)]
344impl Log for BasicLogger {
345 fn enabled(&self, metadata: &Metadata) -> bool {
346 metadata.level() <= log::max_level() && metadata.level() <= log::STATIC_MAX_LEVEL
347 }
348
349 fn log(&self, record: &Record) {
350 if self.enabled(record.metadata()) {
351 let message = format!("{}", record.args());
353 let mut logger = self.logger.lock().unwrap();
354 match record.level() {
355 Level::Error => logger.err(message),
356 Level::Warn => logger.warning(message),
357 Level::Info => logger.info(message),
358 Level::Debug => logger.debug(message),
359 Level::Trace => logger.debug(message),
360 };
361 }
362 }
363
364 fn flush(&self) {
365 let _ = self.logger.lock().unwrap().backend.flush();
366 }
367}
368
369#[cfg(unix)]
371pub fn init_unix(facility: Facility, log_level: log::LevelFilter) -> Result<()> {
372 let (process, pid) = get_process_info()?;
373 let formatter = Formatter3164 {
374 facility,
375 hostname: None,
376 process,
377 pid,
378 };
379 unix(formatter).and_then(|logger| {
380 log::set_boxed_logger(Box::new(BasicLogger::new(logger)))
381 .map_err(|e| Error::Initialization(Box::new(e)))
382 })?;
383
384 log::set_max_level(log_level);
385 Ok(())
386}
387
388#[cfg(not(unix))]
389pub fn init_unix(_facility: Facility, _log_level: log::LevelFilter) -> Result<()> {
390 Err(ErrorKind::UnsupportedPlatform)?
391}
392
393#[cfg(unix)]
395pub fn init_unix_custom<P: AsRef<Path>>(
396 facility: Facility,
397 log_level: log::LevelFilter,
398 path: P,
399) -> Result<()> {
400 let (process, pid) = get_process_info()?;
401 let formatter = Formatter3164 {
402 facility,
403 hostname: None,
404 process,
405 pid,
406 };
407 unix_custom(formatter, path).and_then(|logger| {
408 log::set_boxed_logger(Box::new(BasicLogger::new(logger)))
409 .map_err(|e| Error::Initialization(Box::new(e)))
410 })?;
411
412 log::set_max_level(log_level);
413 Ok(())
414}
415
416#[cfg(not(unix))]
417pub fn init_unix_custom<P: AsRef<Path>>(
418 _facility: Facility,
419 _log_level: log::LevelFilter,
420 _path: P,
421) -> Result<()> {
422 Err(ErrorKind::UnsupportedPlatform)?
423}
424
425pub fn init_udp<T: ToSocketAddrs>(
427 local: T,
428 server: T,
429 hostname: String,
430 facility: Facility,
431 log_level: log::LevelFilter,
432) -> Result<()> {
433 let (process, pid) = get_process_info()?;
434 let formatter = Formatter3164 {
435 facility,
436 hostname: Some(hostname),
437 process,
438 pid,
439 };
440 udp(formatter, local, server).and_then(|logger| {
441 log::set_boxed_logger(Box::new(BasicLogger::new(logger)))
442 .map_err(|e| Error::Initialization(Box::new(e)))
443 })?;
444
445 log::set_max_level(log_level);
446 Ok(())
447}
448
449pub fn init_tcp<T: ToSocketAddrs>(
451 server: T,
452 hostname: String,
453 facility: Facility,
454 log_level: log::LevelFilter,
455) -> Result<()> {
456 let (process, pid) = get_process_info()?;
457 let formatter = Formatter3164 {
458 facility,
459 hostname: Some(hostname),
460 process,
461 pid,
462 };
463
464 tcp(formatter, server).and_then(|logger| {
465 log::set_boxed_logger(Box::new(BasicLogger::new(logger)))
466 .map_err(|e| Error::Initialization(Box::new(e)))
467 })?;
468
469 log::set_max_level(log_level);
470 Ok(())
471}
472
473pub fn init(
486 facility: Facility,
487 log_level: log::LevelFilter,
488 application_name: Option<&str>,
489) -> Result<()> {
490 let (process_name, pid) = get_process_info()?;
491 let process = application_name.map(From::from).unwrap_or(process_name);
492 let mut formatter = Formatter3164 {
493 facility,
494 hostname: None,
495 process,
496 pid,
497 };
498
499 let backend = if let Ok(logger) = unix(formatter.clone()) {
500 logger.backend
501 } else {
502 formatter.hostname = get_hostname().ok();
503 if let Ok(tcp_stream) = TcpStream::connect(("127.0.0.1", 601)) {
504 LoggerBackend::Tcp(BufWriter::new(tcp_stream))
505 } else {
506 let udp_addr = "127.0.0.1:514".parse().unwrap();
507 let udp_stream = UdpSocket::bind(("127.0.0.1", 0))?;
508 LoggerBackend::Udp(udp_stream, udp_addr)
509 }
510 };
511 log::set_boxed_logger(Box::new(BasicLogger::new(Logger { formatter, backend })))
512 .map_err(|e| Error::Initialization(Box::new(e)))?;
513
514 log::set_max_level(log_level);
515 Ok(())
516}
517
518fn get_process_info() -> Result<(String, u32)> {
519 env::current_exe()
520 .map_err(|e| Error::Initialization(Box::new(e)))
521 .and_then(|path| {
522 path.file_name()
523 .and_then(|os_name| os_name.to_str())
524 .map(|name| name.to_string())
525 .ok_or_else(|| Error::Initialization("process name not found".into()))
526 })
527 .map(|name| (name, process::id()))
528}
529
530fn get_hostname() -> Result<String> {
531 Ok(hostname::get()?.to_string_lossy().to_string())
532}