Coverage Report

Created: 2025-12-28 06:10

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/rust/registry/src/index.crates.io-1949cf8c6b5b557f/syslog-6.0.1/src/lib.rs
Line
Count
Source
1
//! Syslog
2
//!
3
//! This crate provides facilities to send log messages via syslog.
4
//! It supports Unix sockets for local syslog, UDP and TCP for remote servers.
5
//!
6
//! Messages can be passed directly without modification, or in RFC 3164 or RFC 5424 format
7
//!
8
//! The code is available on [Github](https://github.com/Geal/rust-syslog)
9
//!
10
//! # Example
11
//!
12
//! ```rust
13
//! use syslog::{Facility, Formatter3164};
14
//!
15
//! let formatter = Formatter3164 {
16
//!     facility: Facility::LOG_USER,
17
//!     hostname: None,
18
//!     process: "myprogram".into(),
19
//!     pid: 0,
20
//! };
21
//!
22
//! match syslog::unix(formatter) {
23
//!     Err(e) => println!("impossible to connect to syslog: {:?}", e),
24
//!     Ok(mut writer) => {
25
//!         writer.err("hello world").expect("could not write error message");
26
//!     }
27
//! }
28
//! ```
29
//!
30
//! It can be used directly with the log crate as follows:
31
//!
32
//! ```rust
33
//! extern crate log;
34
//!
35
//! use syslog::{Facility, Formatter3164, BasicLogger};
36
//! use log::{SetLoggerError, LevelFilter, info};
37
//!
38
//! let formatter = Formatter3164 {
39
//!     facility: Facility::LOG_USER,
40
//!     hostname: None,
41
//!     process: "myprogram".into(),
42
//!     pid: 0,
43
//! };
44
//!
45
//! let logger = match syslog::unix(formatter) {
46
//!     Err(e) => { println!("impossible to connect to syslog: {:?}", e); return; },
47
//!     Ok(logger) => logger,
48
//! };
49
//! log::set_boxed_logger(Box::new(BasicLogger::new(logger)))
50
//!         .map(|()| log::set_max_level(LevelFilter::Info));
51
//!
52
//! info!("hello world");
53
//!
54
#![crate_type = "lib"]
55
56
#[macro_use]
57
extern crate error_chain;
58
extern crate log;
59
extern crate time;
60
61
use std::env;
62
use std::fmt::{self, Arguments};
63
use std::io::{self, BufWriter, Write};
64
use std::net::{SocketAddr, TcpStream, ToSocketAddrs, UdpSocket};
65
#[cfg(unix)]
66
use std::os::unix::net::{UnixDatagram, UnixStream};
67
use std::path::Path;
68
use std::process;
69
use std::sync::{Arc, Mutex};
70
71
use log::{Level, Log, Metadata, Record};
72
73
mod errors;
74
mod facility;
75
mod format;
76
pub use errors::*;
77
pub use facility::Facility;
78
pub use format::Severity;
79
80
pub use format::{Formatter3164, Formatter5424, LogFormat};
81
82
pub type Priority = u8;
83
84
/// Main logging structure
85
pub struct Logger<Backend: Write, Formatter> {
86
    pub formatter: Formatter,
87
    pub backend: Backend,
88
}
89
90
impl<W: Write, F> Logger<W, F> {
91
0
    pub fn new(backend: W, formatter: F) -> Self {
92
0
        Logger { backend, formatter }
93
0
    }
94
95
0
    pub fn emerg<T>(&mut self, message: T) -> Result<()>
96
0
    where
97
0
        F: LogFormat<T>,
98
    {
99
0
        self.formatter.emerg(&mut self.backend, message)
100
0
    }
101
102
0
    pub fn alert<T>(&mut self, message: T) -> Result<()>
103
0
    where
104
0
        F: LogFormat<T>,
105
    {
106
0
        self.formatter.alert(&mut self.backend, message)
107
0
    }
108
109
0
    pub fn crit<T>(&mut self, message: T) -> Result<()>
110
0
    where
111
0
        F: LogFormat<T>,
112
    {
113
0
        self.formatter.crit(&mut self.backend, message)
114
0
    }
115
116
0
    pub fn err<T>(&mut self, message: T) -> Result<()>
117
0
    where
118
0
        F: LogFormat<T>,
119
    {
120
0
        self.formatter.err(&mut self.backend, message)
121
0
    }
122
123
0
    pub fn warning<T>(&mut self, message: T) -> Result<()>
124
0
    where
125
0
        F: LogFormat<T>,
126
    {
127
0
        self.formatter.warning(&mut self.backend, message)
128
0
    }
129
130
0
    pub fn notice<T>(&mut self, message: T) -> Result<()>
131
0
    where
132
0
        F: LogFormat<T>,
133
    {
134
0
        self.formatter.notice(&mut self.backend, message)
135
0
    }
136
137
0
    pub fn info<T>(&mut self, message: T) -> Result<()>
138
0
    where
139
0
        F: LogFormat<T>,
140
    {
141
0
        self.formatter.info(&mut self.backend, message)
142
0
    }
143
144
0
    pub fn debug<T>(&mut self, message: T) -> Result<()>
145
0
    where
146
0
        F: LogFormat<T>,
147
    {
148
0
        self.formatter.debug(&mut self.backend, message)
149
0
    }
150
}
151
152
pub enum LoggerBackend {
153
    /// Unix socket, temp file path, log file path
154
    #[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
166
impl Write for LoggerBackend {
167
    /// Sends a message directly, without any formatting
168
0
    fn write(&mut self, message: &[u8]) -> io::Result<usize> {
169
0
        match *self {
170
            #[cfg(unix)]
171
0
            LoggerBackend::Unix(ref dgram) => dgram.send(&message[..]),
172
            #[cfg(unix)]
173
0
            LoggerBackend::UnixStream(ref mut socket) => {
174
0
                let null = [0; 1];
175
0
                socket
176
0
                    .write(&message[..])
177
0
                    .and_then(|sz| socket.write(&null).map(|_| sz))
178
            }
179
0
            LoggerBackend::Udp(ref socket, ref addr) => socket.send_to(&message[..], addr),
180
0
            LoggerBackend::Tcp(ref mut socket) => socket.write(&message[..]),
181
            #[cfg(not(unix))]
182
            LoggerBackend::Unix(_) | LoggerBackend::UnixStream(_) => {
183
                Err(io::Error::new(io::ErrorKind::Other, "unsupported platform"))
184
            }
185
        }
186
0
    }
187
188
0
    fn write_fmt(&mut self, args: Arguments) -> io::Result<()> {
189
0
        match *self {
190
            #[cfg(unix)]
191
0
            LoggerBackend::Unix(ref dgram) => {
192
0
                let message = fmt::format(args);
193
0
                dgram.send(message.as_bytes()).map(|_| ())
194
            }
195
            #[cfg(unix)]
196
0
            LoggerBackend::UnixStream(ref mut socket) => {
197
0
                let null = [0; 1];
198
0
                socket
199
0
                    .write_fmt(args)
200
0
                    .and_then(|_| socket.write(&null).map(|_| ()))
201
            }
202
0
            LoggerBackend::Udp(ref socket, ref addr) => {
203
0
                let message = fmt::format(args);
204
0
                socket.send_to(message.as_bytes(), addr).map(|_| ())
205
            }
206
0
            LoggerBackend::Tcp(ref mut socket) => socket.write_fmt(args),
207
            #[cfg(not(unix))]
208
            LoggerBackend::Unix(_) | LoggerBackend::UnixStream(_) => {
209
                Err(io::Error::new(io::ErrorKind::Other, "unsupported platform"))
210
            }
211
        }
212
0
    }
213
214
0
    fn flush(&mut self) -> io::Result<()> {
215
0
        match *self {
216
            #[cfg(unix)]
217
0
            LoggerBackend::Unix(_) => Ok(()),
218
            #[cfg(unix)]
219
0
            LoggerBackend::UnixStream(ref mut socket) => socket.flush(),
220
0
            LoggerBackend::Udp(_, _) => Ok(()),
221
0
            LoggerBackend::Tcp(ref mut socket) => socket.flush(),
222
            #[cfg(not(unix))]
223
            LoggerBackend::Unix(_) | LoggerBackend::UnixStream(_) => {
224
                Err(io::Error::new(io::ErrorKind::Other, "unsupported platform"))
225
            }
226
        }
227
0
    }
228
}
229
230
/// Returns a Logger using unix socket to target local syslog ( using /dev/log or /var/run/syslog)
231
#[cfg(unix)]
232
0
pub fn unix<F: Clone>(formatter: F) -> Result<Logger<LoggerBackend, F>> {
233
0
    unix_connect(formatter.clone(), "/dev/log")
234
0
        .or_else(|e| {
235
0
            if let ErrorKind::Io(ref io_err) = *e.kind() {
236
0
                if io_err.kind() == io::ErrorKind::NotFound {
237
0
                    return unix_connect(formatter, "/var/run/syslog");
238
0
                }
239
0
            }
240
0
            Err(e)
241
0
        })
Unexecuted instantiation: syslog::unix::<syslog::format::Formatter3164>::{closure#0}
Unexecuted instantiation: syslog::unix::<syslog::format::Formatter3164>::{closure#0}
242
0
        .chain_err(|| ErrorKind::Initialization)
243
0
}
Unexecuted instantiation: syslog::unix::<syslog::format::Formatter3164>
Unexecuted instantiation: syslog::unix::<syslog::format::Formatter3164>
244
245
#[cfg(not(unix))]
246
pub fn unix<F: Clone>(_formatter: F) -> Result<Logger<LoggerBackend, F>> {
247
    Err(ErrorKind::UnsupportedPlatform)?
248
}
249
250
/// Returns a Logger using unix socket to target local syslog at user provided path
251
#[cfg(unix)]
252
0
pub fn unix_custom<P: AsRef<Path>, F>(formatter: F, path: P) -> Result<Logger<LoggerBackend, F>> {
253
0
    unix_connect(formatter, path).chain_err(|| ErrorKind::Initialization)
254
0
}
255
256
#[cfg(not(unix))]
257
pub fn unix_custom<P: AsRef<Path>, F>(_formatter: F, _path: P) -> Result<Logger<LoggerBackend, F>> {
258
    Err(ErrorKind::UnsupportedPlatform)?
259
}
260
261
#[cfg(unix)]
262
0
fn unix_connect<P: AsRef<Path>, F>(formatter: F, path: P) -> Result<Logger<LoggerBackend, F>> {
263
0
    let sock = UnixDatagram::unbound()?;
264
0
    match sock.connect(&path) {
265
0
        Ok(()) => Ok(Logger {
266
0
            formatter,
267
0
            backend: LoggerBackend::Unix(sock),
268
0
        }),
269
0
        Err(ref e) if e.raw_os_error() == Some(libc::EPROTOTYPE) => {
270
0
            let sock = UnixStream::connect(path)?;
271
0
            Ok(Logger {
272
0
                formatter,
273
0
                backend: LoggerBackend::UnixStream(BufWriter::new(sock)),
274
0
            })
275
        }
276
0
        Err(e) => Err(e.into()),
277
    }
278
0
}
Unexecuted instantiation: syslog::unix_connect::<&str, syslog::format::Formatter3164>
Unexecuted instantiation: syslog::unix_connect::<&str, syslog::format::Formatter3164>
279
280
/// returns a UDP logger connecting `local` and `server`
281
0
pub fn udp<T: ToSocketAddrs, F>(
282
0
    formatter: F,
283
0
    local: T,
284
0
    server: T,
285
0
) -> Result<Logger<LoggerBackend, F>> {
286
0
    server
287
0
        .to_socket_addrs()
288
0
        .chain_err(|| ErrorKind::Initialization)
289
0
        .and_then(|mut server_addr_opt| {
290
0
            server_addr_opt
291
0
                .next()
292
0
                .chain_err(|| ErrorKind::Initialization)
293
0
        })
294
0
        .and_then(|server_addr| {
295
0
            UdpSocket::bind(local)
296
0
                .chain_err(|| ErrorKind::Initialization)
297
0
                .and_then(|socket| {
298
0
                    Ok(Logger {
299
0
                        formatter,
300
0
                        backend: LoggerBackend::Udp(socket, server_addr),
301
0
                    })
302
0
                })
303
0
        })
304
0
}
305
306
/// returns a TCP logger connecting `local` and `server`
307
0
pub fn tcp<T: ToSocketAddrs, F>(formatter: F, server: T) -> Result<Logger<LoggerBackend, F>> {
308
0
    TcpStream::connect(server)
309
0
        .chain_err(|| ErrorKind::Initialization)
310
0
        .and_then(|socket| {
311
0
            Ok(Logger {
312
0
                formatter,
313
0
                backend: LoggerBackend::Tcp(BufWriter::new(socket)),
314
0
            })
315
0
        })
316
0
}
317
318
pub struct BasicLogger {
319
    logger: Arc<Mutex<Logger<LoggerBackend, Formatter3164>>>,
320
}
321
322
impl BasicLogger {
323
0
    pub fn new(logger: Logger<LoggerBackend, Formatter3164>) -> BasicLogger {
324
0
        BasicLogger {
325
0
            logger: Arc::new(Mutex::new(logger)),
326
0
        }
327
0
    }
328
}
329
330
#[allow(unused_variables, unused_must_use)]
331
impl Log for BasicLogger {
332
0
    fn enabled(&self, metadata: &Metadata) -> bool {
333
0
        metadata.level() <= log::max_level() && metadata.level() <= log::STATIC_MAX_LEVEL
334
0
    }
335
336
0
    fn log(&self, record: &Record) {
337
        //FIXME: temporary patch to compile
338
0
        let message = format!("{}", record.args());
339
0
        let mut logger = self.logger.lock().unwrap();
340
0
        match record.level() {
341
0
            Level::Error => logger.err(message),
342
0
            Level::Warn => logger.warning(message),
343
0
            Level::Info => logger.info(message),
344
0
            Level::Debug => logger.debug(message),
345
0
            Level::Trace => logger.debug(message),
346
        };
347
0
    }
348
349
0
    fn flush(&self) {
350
0
        let _ = self.logger.lock().unwrap().backend.flush();
351
0
    }
352
}
353
354
/// Unix socket Logger init function compatible with log crate
355
#[cfg(unix)]
356
0
pub fn init_unix(facility: Facility, log_level: log::LevelFilter) -> Result<()> {
357
0
    let (process, pid) = get_process_info()?;
358
0
    let formatter = Formatter3164 {
359
0
        facility,
360
0
        hostname: None,
361
0
        process,
362
0
        pid,
363
0
    };
364
0
    unix(formatter).and_then(|logger| {
365
0
        log::set_boxed_logger(Box::new(BasicLogger::new(logger)))
366
0
            .chain_err(|| ErrorKind::Initialization)
367
0
    })?;
368
369
0
    log::set_max_level(log_level);
370
0
    Ok(())
371
0
}
372
373
#[cfg(not(unix))]
374
pub fn init_unix(_facility: Facility, _log_level: log::LevelFilter) -> Result<()> {
375
    Err(ErrorKind::UnsupportedPlatform)?
376
}
377
378
/// Unix socket Logger init function compatible with log crate and user provided socket path
379
#[cfg(unix)]
380
0
pub fn init_unix_custom<P: AsRef<Path>>(
381
0
    facility: Facility,
382
0
    log_level: log::LevelFilter,
383
0
    path: P,
384
0
) -> Result<()> {
385
0
    let (process, pid) = get_process_info()?;
386
0
    let formatter = Formatter3164 {
387
0
        facility,
388
0
        hostname: None,
389
0
        process,
390
0
        pid,
391
0
    };
392
0
    unix_custom(formatter, path).and_then(|logger| {
393
0
        log::set_boxed_logger(Box::new(BasicLogger::new(logger)))
394
0
            .chain_err(|| ErrorKind::Initialization)
395
0
    })?;
396
397
0
    log::set_max_level(log_level);
398
0
    Ok(())
399
0
}
400
401
#[cfg(not(unix))]
402
pub fn init_unix_custom<P: AsRef<Path>>(
403
    _facility: Facility,
404
    _log_level: log::LevelFilter,
405
    _path: P,
406
) -> Result<()> {
407
    Err(ErrorKind::UnsupportedPlatform)?
408
}
409
410
/// UDP Logger init function compatible with log crate
411
0
pub fn init_udp<T: ToSocketAddrs>(
412
0
    local: T,
413
0
    server: T,
414
0
    hostname: String,
415
0
    facility: Facility,
416
0
    log_level: log::LevelFilter,
417
0
) -> Result<()> {
418
0
    let (process, pid) = get_process_info()?;
419
0
    let formatter = Formatter3164 {
420
0
        facility,
421
0
        hostname: Some(hostname),
422
0
        process,
423
0
        pid,
424
0
    };
425
0
    udp(formatter, local, server).and_then(|logger| {
426
0
        log::set_boxed_logger(Box::new(BasicLogger::new(logger)))
427
0
            .chain_err(|| ErrorKind::Initialization)
428
0
    })?;
429
430
0
    log::set_max_level(log_level);
431
0
    Ok(())
432
0
}
433
434
/// TCP Logger init function compatible with log crate
435
0
pub fn init_tcp<T: ToSocketAddrs>(
436
0
    server: T,
437
0
    hostname: String,
438
0
    facility: Facility,
439
0
    log_level: log::LevelFilter,
440
0
) -> Result<()> {
441
0
    let (process, pid) = get_process_info()?;
442
0
    let formatter = Formatter3164 {
443
0
        facility,
444
0
        hostname: Some(hostname),
445
0
        process,
446
0
        pid,
447
0
    };
448
449
0
    tcp(formatter, server).and_then(|logger| {
450
0
        log::set_boxed_logger(Box::new(BasicLogger::new(logger)))
451
0
            .chain_err(|| ErrorKind::Initialization)
452
0
    })?;
453
454
0
    log::set_max_level(log_level);
455
0
    Ok(())
456
0
}
457
458
/// Initializes logging subsystem for log crate
459
///
460
/// This tries to connect to syslog by following ways:
461
///
462
/// 1. Unix sockets /dev/log and /var/run/syslog (in this order)
463
/// 2. Tcp connection to 127.0.0.1:601
464
/// 3. Udp connection to 127.0.0.1:514
465
///
466
/// Note the last option usually (almost) never fails in this method. So
467
/// this method doesn't return error even if there is no syslog.
468
///
469
/// If `application_name` is `None` name is derived from executable name
470
0
pub fn init(
471
0
    facility: Facility,
472
0
    log_level: log::LevelFilter,
473
0
    application_name: Option<&str>,
474
0
) -> Result<()> {
475
0
    let (process_name, pid) = get_process_info()?;
476
0
    let process = application_name.map(From::from).unwrap_or(process_name);
477
0
    let formatter = Formatter3164 {
478
0
        facility,
479
0
        hostname: get_hostname().ok(),
480
0
        process,
481
0
        pid,
482
0
    };
483
484
0
    let backend = unix(formatter.clone())
485
0
        .map(|logger: Logger<LoggerBackend, Formatter3164>| logger.backend)
486
0
        .or_else(|_| {
487
0
            TcpStream::connect(("127.0.0.1", 601)).map(|s| LoggerBackend::Tcp(BufWriter::new(s)))
488
0
        })
489
0
        .or_else(|_| {
490
0
            let udp_addr = "127.0.0.1:514".parse().unwrap();
491
0
            UdpSocket::bind(("127.0.0.1", 0)).map(|s| LoggerBackend::Udp(s, udp_addr))
492
0
        })?;
493
0
    log::set_boxed_logger(Box::new(BasicLogger::new(Logger { formatter, backend })))
494
0
        .chain_err(|| ErrorKind::Initialization)?;
495
496
0
    log::set_max_level(log_level);
497
0
    Ok(())
498
0
}
499
500
0
fn get_process_info() -> Result<(String, u32)> {
501
0
    env::current_exe()
502
0
        .chain_err(|| ErrorKind::Initialization)
503
0
        .and_then(|path| {
504
0
            path.file_name()
505
0
                .and_then(|os_name| os_name.to_str())
506
0
                .map(|name| name.to_string())
507
0
                .chain_err(|| ErrorKind::Initialization)
508
0
        })
509
0
        .map(|name| (name, process::id()))
510
0
}
511
512
0
fn get_hostname() -> Result<String> {
513
0
    Ok(hostname::get()?.to_string_lossy().to_string())
514
0
}