syslog-6.0.1/.cargo_vcs_info.json0000644000000001360000000000100123600ustar { "git": { "sha1": "c43aa2d3dc03247bf8424f4a0a1b416843f9d16d" }, "path_in_vcs": "" }syslog-6.0.1/.gitignore000064400000000000000000000000300072674642500131610ustar 00000000000000build target Cargo.lock syslog-6.0.1/.travis.yml000064400000000000000000000013360072674642500133140ustar 00000000000000language: rust addons: apt: packages: - libcurl4-openssl-dev - libelf-dev - libdw-dev rust: - nightly - beta - stable before_script: - pip install 'travis-cargo<0.2' --user && export PATH=$HOME/.local/bin:$PATH script: - travis-cargo build - travis-cargo test - travis-cargo bench - travis-cargo --only stable doc after_success: - travis-cargo coveralls --no-sudo notifications: webhooks: urls: - https://webhooks.gitter.im/e/9c035a194ac4fd4cc061 on_success: change on_failure: always on_start: false env: global: # override the default `--features unstable` used for the nightly branch (optional) - TRAVIS_CARGO_NIGHTLY_FEATURE=nightly sudo: false syslog-6.0.1/Cargo.lock0000644000000053560000000000100103440ustar # This file is automatically @generated by Cargo. # It is not intended for manual editing. version = 3 [[package]] name = "cfg-if" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "error-chain" version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2d2f06b9cac1506ece98fe3231e3cc9c4410ec3d5b1f24ae1c8946f0742cdefc" dependencies = [ "version_check", ] [[package]] name = "hostname" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c731c3e10504cc8ed35cfe2f1db4c9274c3d35fa486e3b31df46f068ef3e867" dependencies = [ "libc", "match_cfg", "winapi", ] [[package]] name = "itoa" version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4" [[package]] name = "libc" version = "0.2.112" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b03d17f364a3a042d5e5d46b053bbbf82c92c9430c592dd4c064dc6ee997125" [[package]] name = "log" version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710" dependencies = [ "cfg-if", ] [[package]] name = "match_cfg" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ffbee8634e0d45d258acb448e7eaab3fce7a0a467395d4d9f228e3c1f01fb2e4" [[package]] name = "syslog" version = "6.0.1" dependencies = [ "error-chain", "hostname", "libc", "log", "time", ] [[package]] name = "time" version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "41effe7cfa8af36f439fac33861b66b049edc6f9a32331e2312660529c1c24ad" dependencies = [ "itoa", "libc", ] [[package]] name = "version_check" version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe" [[package]] name = "winapi" version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" dependencies = [ "winapi-i686-pc-windows-gnu", "winapi-x86_64-pc-windows-gnu", ] [[package]] name = "winapi-i686-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" syslog-6.0.1/Cargo.toml0000644000000020170000000000100103560ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] name = "syslog" version = "6.0.1" authors = ["contact@geoffroycouprie.com"] description = "Send log messages to syslog" documentation = "https://docs.rs/syslog" keywords = ["syslog", "logs", "logging"] license = "MIT" repository = "https://github.com/Geal/rust-syslog" [dependencies.error-chain] version = "0.12.2" default-features = false [dependencies.hostname] version = "0.3.1" [dependencies.libc] version = "0.2.112" [dependencies.log] version = "0.4.8" features = ["std"] [dependencies.time] version = "0.3.5" features = ["local-offset", "formatting"] syslog-6.0.1/Cargo.toml.orig000064400000000000000000000010310072674642500140620ustar 00000000000000[package] name = "syslog" version = "6.0.1" authors = [ "contact@geoffroycouprie.com" ] description = "Send log messages to syslog" license = "MIT" repository = "https://github.com/Geal/rust-syslog" documentation = "https://docs.rs/syslog" keywords = ["syslog", "logs", "logging"] [dependencies] hostname = "0.3.1" time = { version = "0.3.5", features = ["local-offset", "formatting"] } log = { version = "0.4.8", features = [ "std" ] } error-chain = { version = "0.12.2", default-features = false } libc = "0.2.112" syslog-6.0.1/LICENSE000064400000000000000000000020510072674642500122030ustar 00000000000000Copyright (c) 2014-2018 Geoffroy Couprie Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. syslog-6.0.1/README.md000064400000000000000000000041760072674642500124670ustar 00000000000000# Sending to Syslog in Rust [![Build Status](https://travis-ci.org/Geal/rust-syslog.png?branch=master)](https://travis-ci.org/Geal/rust-syslog) [![Coverage Status](https://coveralls.io/repos/Geal/rust-syslog/badge.svg?branch=master&service=github)](https://coveralls.io/github/Geal/rust-syslog?branch=master) A small library to write to local syslog. ## Installation syslog is available on [crates.io](https://crates.io/crates/syslog) and can be included in your Cargo enabled project like this: ```toml [dependencies] syslog = "^6.0" ``` ## documentation Reference documentation is available [here](https://docs.rs/syslog). ## Example ```rust extern crate syslog; use syslog::{Facility, Formatter3164}; fn main() { let formatter = Formatter3164 { facility: Facility::LOG_USER, hostname: None, process: "myprogram".into(), pid: 42, }; match syslog::unix(formatter) { Err(e) => println!("impossible to connect to syslog: {:?}", e), Ok(mut writer) => { writer.err("hello world").expect("could not write error message"); } } } ``` The struct `syslog::Logger` implements `Log` from the `log` crate, so it can be used as backend for other logging systems: ```rust extern crate syslog; #[macro_use] extern crate log; use syslog::{Facility, Formatter3164, BasicLogger}; use log::{SetLoggerError, LevelFilter}; fn main() { let formatter = Formatter3164 { facility: Facility::LOG_USER, hostname: None, process: "myprogram".into(), pid: 0, }; let logger = syslog::unix(formatter).expect("could not connect to syslog"); log::set_boxed_logger(Box::new(BasicLogger::new(logger))) .map(|()| log::set_max_level(LevelFilter::Info)); info!("hello world"); } ``` There are 3 functions to create loggers: * the `unix` function sends to the local syslog through a Unix socket: `syslog::unix(formatter)` * the `tcp` function takes an address for a remote TCP syslog server: `tcp(formatter, "127.0.0.1:4242")` * the `udp` function takes an address for a local port, and the address remote UDP syslog server: `udp(formatter, "127.0.0.1:1234", "127.0.0.1:4242")` syslog-6.0.1/examples/log.rs000064400000000000000000000011410072674642500141420ustar 00000000000000//! using syslog with the log crate extern crate syslog; #[macro_use] extern crate log; use log::LevelFilter; use syslog::{BasicLogger, Facility, Formatter3164}; fn main() { let formatter = Formatter3164 { facility: Facility::LOG_USER, hostname: None, process: "myprogram".into(), pid: 0, }; let logger = syslog::unix(formatter).expect("could not connect to syslog"); log::set_boxed_logger(Box::new(BasicLogger::new(logger))) .map(|()| log::set_max_level(LevelFilter::Info)) .expect("could not register logger"); info!("hello world"); } syslog-6.0.1/examples/rfc5424.rs000064400000000000000000000010500072674642500144510ustar 00000000000000extern crate syslog; use std::collections::HashMap; use syslog::{Facility, Formatter5424}; fn main() { let formatter = Formatter5424 { facility: Facility::LOG_USER, hostname: None, process: "myprogram".into(), pid: 0, }; match syslog::unix(formatter) { Err(e) => println!("impossible to connect to syslog: {:?}", e), Ok(mut writer) => { writer .err((1, HashMap::new(), "hello world")) .expect("could not write error message"); } } } syslog-6.0.1/examples/write.rs000064400000000000000000000011570072674642500145220ustar 00000000000000extern crate syslog; use syslog::{Facility, Formatter3164}; fn main() { let formatter = Formatter3164 { facility: Facility::LOG_USER, hostname: None, process: "myprogram".into(), pid: 0, }; match syslog::unix(formatter) { Err(e) => println!("impossible to connect to syslog: {:?}", e), Ok(mut writer) => { writer .err("hello world") .expect("could not write error message"); writer .err("hello all".to_string()) .expect("could not write error message"); } } } syslog-6.0.1/src/errors.rs000064400000000000000000000002700072674642500136500ustar 00000000000000error_chain! { errors { Initialization UnsupportedPlatform Format Write } foreign_links { Io(::std::io::Error) #[doc = "Link to a `std::error::Error` type."]; } } syslog-6.0.1/src/facility.rs000064400000000000000000000043430072674642500141450ustar 00000000000000use std::str::FromStr; #[allow(non_camel_case_types)] #[derive(Copy, Clone, Debug)] pub enum Facility { LOG_KERN = 0 << 3, LOG_USER = 1 << 3, LOG_MAIL = 2 << 3, LOG_DAEMON = 3 << 3, LOG_AUTH = 4 << 3, LOG_SYSLOG = 5 << 3, LOG_LPR = 6 << 3, LOG_NEWS = 7 << 3, LOG_UUCP = 8 << 3, LOG_CRON = 9 << 3, LOG_AUTHPRIV = 10 << 3, LOG_FTP = 11 << 3, LOG_LOCAL0 = 16 << 3, LOG_LOCAL1 = 17 << 3, LOG_LOCAL2 = 18 << 3, LOG_LOCAL3 = 19 << 3, LOG_LOCAL4 = 20 << 3, LOG_LOCAL5 = 21 << 3, LOG_LOCAL6 = 22 << 3, LOG_LOCAL7 = 23 << 3, } impl FromStr for Facility { type Err = (); fn from_str(s: &str) -> Result { let result = match &s.to_lowercase()[..] { "log_kern" | "kern" => Facility::LOG_KERN, "log_user" | "user" => Facility::LOG_USER, "log_mail" | "mail" => Facility::LOG_MAIL, "log_daemon" | "daemon" => Facility::LOG_DAEMON, "log_auth" | "auth" => Facility::LOG_AUTH, "log_syslog" | "syslog" => Facility::LOG_SYSLOG, "log_lpr" | "lpr" => Facility::LOG_LPR, "log_news" | "news" => Facility::LOG_NEWS, "log_uucp" | "uucp" => Facility::LOG_UUCP, "log_cron" | "cron" => Facility::LOG_CRON, "log_authpriv" | "authpriv" => Facility::LOG_AUTHPRIV, "log_ftp" | "ftp" => Facility::LOG_FTP, "log_local0" | "local0" => Facility::LOG_LOCAL0, "log_local1" | "local1" => Facility::LOG_LOCAL1, "log_local2" | "local2" => Facility::LOG_LOCAL2, "log_local3" | "local3" => Facility::LOG_LOCAL3, "log_local4" | "local4" => Facility::LOG_LOCAL4, "log_local5" | "local5" => Facility::LOG_LOCAL5, "log_local6" | "local6" => Facility::LOG_LOCAL6, "log_local7" | "local7" => Facility::LOG_LOCAL7, _ => return Err(()), }; Ok(result) } } impl Default for Facility { /// Returns the default `Facility`, which is `LOG_USER` (as [specified by POSIX]). /// /// [specified by POSIX]: https://pubs.opengroup.org/onlinepubs/9699919799/functions/closelog.html fn default() -> Self { Facility::LOG_USER } } syslog-6.0.1/src/format.rs000064400000000000000000000202300072674642500136220ustar 00000000000000use std::collections::HashMap; use std::fmt::Display; use std::io::Write; use time; use errors::*; use facility::Facility; use get_hostname; use get_process_info; use Priority; #[allow(non_camel_case_types)] #[derive(Copy, Clone)] pub enum Severity { LOG_EMERG, LOG_ALERT, LOG_CRIT, LOG_ERR, LOG_WARNING, LOG_NOTICE, LOG_INFO, LOG_DEBUG, } pub trait LogFormat { fn format(&self, w: &mut W, severity: Severity, message: T) -> Result<()>; fn emerg(&mut self, w: &mut W, message: T) -> Result<()> { self.format(w, Severity::LOG_EMERG, message) } fn alert(&mut self, w: &mut W, message: T) -> Result<()> { self.format(w, Severity::LOG_ALERT, message) } fn crit(&mut self, w: &mut W, message: T) -> Result<()> { self.format(w, Severity::LOG_CRIT, message) } fn err(&mut self, w: &mut W, message: T) -> Result<()> { self.format(w, Severity::LOG_ERR, message) } fn warning(&mut self, w: &mut W, message: T) -> Result<()> { self.format(w, Severity::LOG_WARNING, message) } fn notice(&mut self, w: &mut W, message: T) -> Result<()> { self.format(w, Severity::LOG_NOTICE, message) } fn info(&mut self, w: &mut W, message: T) -> Result<()> { self.format(w, Severity::LOG_INFO, message) } fn debug(&mut self, w: &mut W, message: T) -> Result<()> { self.format(w, Severity::LOG_DEBUG, message) } } #[derive(Clone, Debug)] pub struct Formatter3164 { pub facility: Facility, pub hostname: Option, pub process: String, pub pid: u32, } impl LogFormat for Formatter3164 { fn format(&self, w: &mut W, severity: Severity, message: T) -> Result<()> { let format = time::format_description::parse("[month repr:short] [day] [hour]:[minute]:[second]") .unwrap(); if let Some(ref hostname) = self.hostname { write!( w, "<{}>{} {} {}[{}]: {}", encode_priority(severity, self.facility), now_local() .map(|timestamp| timestamp.format(&format).unwrap()) .unwrap(), hostname, self.process, self.pid, message ) .chain_err(|| ErrorKind::Format) } else { write!( w, "<{}>{} {}[{}]: {}", encode_priority(severity, self.facility), now_local() .map(|timestamp| timestamp.format(&format).unwrap()) .unwrap(), self.process, self.pid, message ) .chain_err(|| ErrorKind::Format) } } } impl Default for Formatter3164 { /// Returns a `Formatter3164` with default settings. /// /// The default settings are as follows: /// /// * `facility`: `LOG_USER`, as [specified by POSIX]. /// * `hostname`: Automatically detected using [the `hostname` crate], if possible. /// * `process`: Automatically detected using [`std::env::current_exe`], or if that fails, an empty string. /// * `pid`: Automatically detected using [`libc::getpid`]. /// /// [`libc::getpid`]: https://docs.rs/libc/0.2/libc/fn.getpid.html /// [specified by POSIX]: https://pubs.opengroup.org/onlinepubs/9699919799/functions/closelog.html /// [`std::env::current_exe`]: https://doc.rust-lang.org/std/env/fn.current_exe.html /// [the `hostname` crate]: https://crates.io/crates/hostname fn default() -> Self { let (process, pid) = get_process_info().unwrap_or((String::new(), std::process::id())); let hostname = get_hostname().ok(); Self { facility: Default::default(), hostname, process, pid, } } } /// RFC 5424 structured data pub type StructuredData = HashMap>; #[derive(Clone, Debug)] pub struct Formatter5424 { pub facility: Facility, pub hostname: Option, pub process: String, pub pid: u32, } impl Formatter5424 { pub fn format_5424_structured_data(&self, data: StructuredData) -> String { if data.is_empty() { "-".to_string() } else { let mut res = String::new(); for (id, params) in &data { res = res + "[" + id; for (name, value) in params { res = res + " " + name + "=\"" + value + "\""; } res += "]"; } res } } } impl LogFormat<(u32, StructuredData, T)> for Formatter5424 { fn format( &self, w: &mut W, severity: Severity, log_message: (u32, StructuredData, T), ) -> Result<()> { let (message_id, data, message) = log_message; write!( w, "<{}>1 {} {} {} {} {} {} {}", // v1 encode_priority(severity, self.facility), time::OffsetDateTime::now_utc() .format(&time::format_description::well_known::Rfc3339) .unwrap(), self.hostname .as_ref() .map(|x| &x[..]) .unwrap_or("localhost"), self.process, self.pid, message_id, self.format_5424_structured_data(data), message ) .chain_err(|| ErrorKind::Format) } } impl Default for Formatter5424 { /// Returns a `Formatter5424` with default settings. /// /// The default settings are as follows: /// /// * `facility`: `LOG_USER`, as [specified by POSIX]. /// * `hostname`: Automatically detected using [the `hostname` crate], if possible. /// * `process`: Automatically detected using [`std::env::current_exe`], or if that fails, an empty string. /// * `pid`: Automatically detected using [`libc::getpid`]. /// /// [`libc::getpid`]: https://docs.rs/libc/0.2/libc/fn.getpid.html /// [specified by POSIX]: https://pubs.opengroup.org/onlinepubs/9699919799/functions/closelog.html /// [`std::env::current_exe`]: https://doc.rust-lang.org/std/env/fn.current_exe.html /// [the `hostname` crate]: https://crates.io/crates/hostname fn default() -> Self { // Get the defaults from `Formatter3164` and move them over. let Formatter3164 { facility, hostname, process, pid, } = Default::default(); Self { facility, hostname, process, pid, } } } fn encode_priority(severity: Severity, facility: Facility) -> Priority { facility as u8 | severity as u8 } #[cfg(unix)] // On unix platforms, time::OffsetDateTime::now_local always returns an error so use UTC instead // https://github.com/time-rs/time/issues/380 fn now_local() -> std::result::Result { Ok(time::OffsetDateTime::now_utc()) } #[cfg(not(unix))] fn now_local() -> std::result::Result { time::OffsetDateTime::now_local() } #[test] fn test_formatter3164_defaults() { let d = Formatter3164::default(); // `Facility` doesn't implement `PartialEq`, so we use a `match` instead. assert!(match d.facility { Facility::LOG_USER => true, _ => false, }); assert!(match &d.hostname { Some(hostname) => !hostname.is_empty(), None => false, }); assert!(!d.process.is_empty()); // Can't really make any assertions about the pid. } #[test] fn test_formatter5424_defaults() { let d = Formatter5424::default(); // `Facility` doesn't implement `PartialEq`, so we use a `match` instead. assert!(match d.facility { Facility::LOG_USER => true, _ => false, }); assert!(match &d.hostname { Some(hostname) => !hostname.is_empty(), None => false, }); assert!(!d.process.is_empty()); // Can't really make any assertions about the pid. } syslog-6.0.1/src/lib.rs000064400000000000000000000355770072674642500131240ustar 00000000000000//! Syslog //! //! This crate provides facilities to send log messages via syslog. //! It supports Unix sockets for local syslog, UDP and TCP for remote servers. //! //! Messages can be passed directly without modification, or in RFC 3164 or RFC 5424 format //! //! The code is available on [Github](https://github.com/Geal/rust-syslog) //! //! # Example //! //! ```rust //! use syslog::{Facility, Formatter3164}; //! //! let formatter = Formatter3164 { //! facility: Facility::LOG_USER, //! hostname: None, //! process: "myprogram".into(), //! pid: 0, //! }; //! //! match syslog::unix(formatter) { //! Err(e) => println!("impossible to connect to syslog: {:?}", e), //! Ok(mut writer) => { //! writer.err("hello world").expect("could not write error message"); //! } //! } //! ``` //! //! It can be used directly with the log crate as follows: //! //! ```rust //! extern crate log; //! //! use syslog::{Facility, Formatter3164, BasicLogger}; //! use log::{SetLoggerError, LevelFilter, info}; //! //! let formatter = Formatter3164 { //! facility: Facility::LOG_USER, //! hostname: None, //! process: "myprogram".into(), //! pid: 0, //! }; //! //! let logger = match syslog::unix(formatter) { //! Err(e) => { println!("impossible to connect to syslog: {:?}", e); return; }, //! Ok(logger) => logger, //! }; //! log::set_boxed_logger(Box::new(BasicLogger::new(logger))) //! .map(|()| log::set_max_level(LevelFilter::Info)); //! //! info!("hello world"); //! #![crate_type = "lib"] #[macro_use] extern crate error_chain; extern crate log; extern crate time; use std::env; use std::fmt::{self, Arguments}; use std::io::{self, BufWriter, Write}; use std::net::{SocketAddr, TcpStream, ToSocketAddrs, UdpSocket}; #[cfg(unix)] use std::os::unix::net::{UnixDatagram, UnixStream}; use std::path::Path; use std::process; use std::sync::{Arc, Mutex}; use log::{Level, Log, Metadata, Record}; mod errors; mod facility; mod format; pub use errors::*; pub use facility::Facility; pub use format::Severity; pub use format::{Formatter3164, Formatter5424, LogFormat}; pub type Priority = u8; /// Main logging structure pub struct Logger { pub formatter: Formatter, pub backend: Backend, } impl Logger { pub fn new(backend: W, formatter: F) -> Self { Logger { backend, formatter } } pub fn emerg(&mut self, message: T) -> Result<()> where F: LogFormat, { self.formatter.emerg(&mut self.backend, message) } pub fn alert(&mut self, message: T) -> Result<()> where F: LogFormat, { self.formatter.alert(&mut self.backend, message) } pub fn crit(&mut self, message: T) -> Result<()> where F: LogFormat, { self.formatter.crit(&mut self.backend, message) } pub fn err(&mut self, message: T) -> Result<()> where F: LogFormat, { self.formatter.err(&mut self.backend, message) } pub fn warning(&mut self, message: T) -> Result<()> where F: LogFormat, { self.formatter.warning(&mut self.backend, message) } pub fn notice(&mut self, message: T) -> Result<()> where F: LogFormat, { self.formatter.notice(&mut self.backend, message) } pub fn info(&mut self, message: T) -> Result<()> where F: LogFormat, { self.formatter.info(&mut self.backend, message) } pub fn debug(&mut self, message: T) -> Result<()> where F: LogFormat, { self.formatter.debug(&mut self.backend, message) } } pub enum LoggerBackend { /// Unix socket, temp file path, log file path #[cfg(unix)] Unix(UnixDatagram), #[cfg(not(unix))] Unix(()), #[cfg(unix)] UnixStream(BufWriter), #[cfg(not(unix))] UnixStream(()), Udp(UdpSocket, SocketAddr), Tcp(BufWriter), } impl Write for LoggerBackend { /// Sends a message directly, without any formatting fn write(&mut self, message: &[u8]) -> io::Result { match *self { #[cfg(unix)] LoggerBackend::Unix(ref dgram) => dgram.send(&message[..]), #[cfg(unix)] LoggerBackend::UnixStream(ref mut socket) => { let null = [0; 1]; socket .write(&message[..]) .and_then(|sz| socket.write(&null).map(|_| sz)) } LoggerBackend::Udp(ref socket, ref addr) => socket.send_to(&message[..], addr), LoggerBackend::Tcp(ref mut socket) => socket.write(&message[..]), #[cfg(not(unix))] LoggerBackend::Unix(_) | LoggerBackend::UnixStream(_) => { Err(io::Error::new(io::ErrorKind::Other, "unsupported platform")) } } } fn write_fmt(&mut self, args: Arguments) -> io::Result<()> { match *self { #[cfg(unix)] LoggerBackend::Unix(ref dgram) => { let message = fmt::format(args); dgram.send(message.as_bytes()).map(|_| ()) } #[cfg(unix)] LoggerBackend::UnixStream(ref mut socket) => { let null = [0; 1]; socket .write_fmt(args) .and_then(|_| socket.write(&null).map(|_| ())) } LoggerBackend::Udp(ref socket, ref addr) => { let message = fmt::format(args); socket.send_to(message.as_bytes(), addr).map(|_| ()) } LoggerBackend::Tcp(ref mut socket) => socket.write_fmt(args), #[cfg(not(unix))] LoggerBackend::Unix(_) | LoggerBackend::UnixStream(_) => { Err(io::Error::new(io::ErrorKind::Other, "unsupported platform")) } } } fn flush(&mut self) -> io::Result<()> { match *self { #[cfg(unix)] LoggerBackend::Unix(_) => Ok(()), #[cfg(unix)] LoggerBackend::UnixStream(ref mut socket) => socket.flush(), LoggerBackend::Udp(_, _) => Ok(()), LoggerBackend::Tcp(ref mut socket) => socket.flush(), #[cfg(not(unix))] LoggerBackend::Unix(_) | LoggerBackend::UnixStream(_) => { Err(io::Error::new(io::ErrorKind::Other, "unsupported platform")) } } } } /// Returns a Logger using unix socket to target local syslog ( using /dev/log or /var/run/syslog) #[cfg(unix)] pub fn unix(formatter: F) -> Result> { unix_connect(formatter.clone(), "/dev/log") .or_else(|e| { if let ErrorKind::Io(ref io_err) = *e.kind() { if io_err.kind() == io::ErrorKind::NotFound { return unix_connect(formatter, "/var/run/syslog"); } } Err(e) }) .chain_err(|| ErrorKind::Initialization) } #[cfg(not(unix))] pub fn unix(_formatter: F) -> Result> { Err(ErrorKind::UnsupportedPlatform)? } /// Returns a Logger using unix socket to target local syslog at user provided path #[cfg(unix)] pub fn unix_custom, F>(formatter: F, path: P) -> Result> { unix_connect(formatter, path).chain_err(|| ErrorKind::Initialization) } #[cfg(not(unix))] pub fn unix_custom, F>(_formatter: F, _path: P) -> Result> { Err(ErrorKind::UnsupportedPlatform)? } #[cfg(unix)] fn unix_connect, F>(formatter: F, path: P) -> Result> { let sock = UnixDatagram::unbound()?; match sock.connect(&path) { Ok(()) => Ok(Logger { formatter, backend: LoggerBackend::Unix(sock), }), Err(ref e) if e.raw_os_error() == Some(libc::EPROTOTYPE) => { let sock = UnixStream::connect(path)?; Ok(Logger { formatter, backend: LoggerBackend::UnixStream(BufWriter::new(sock)), }) } Err(e) => Err(e.into()), } } /// returns a UDP logger connecting `local` and `server` pub fn udp( formatter: F, local: T, server: T, ) -> Result> { server .to_socket_addrs() .chain_err(|| ErrorKind::Initialization) .and_then(|mut server_addr_opt| { server_addr_opt .next() .chain_err(|| ErrorKind::Initialization) }) .and_then(|server_addr| { UdpSocket::bind(local) .chain_err(|| ErrorKind::Initialization) .and_then(|socket| { Ok(Logger { formatter, backend: LoggerBackend::Udp(socket, server_addr), }) }) }) } /// returns a TCP logger connecting `local` and `server` pub fn tcp(formatter: F, server: T) -> Result> { TcpStream::connect(server) .chain_err(|| ErrorKind::Initialization) .and_then(|socket| { Ok(Logger { formatter, backend: LoggerBackend::Tcp(BufWriter::new(socket)), }) }) } pub struct BasicLogger { logger: Arc>>, } impl BasicLogger { pub fn new(logger: Logger) -> BasicLogger { BasicLogger { logger: Arc::new(Mutex::new(logger)), } } } #[allow(unused_variables, unused_must_use)] impl Log for BasicLogger { fn enabled(&self, metadata: &Metadata) -> bool { metadata.level() <= log::max_level() && metadata.level() <= log::STATIC_MAX_LEVEL } fn log(&self, record: &Record) { //FIXME: temporary patch to compile let message = format!("{}", record.args()); let mut logger = self.logger.lock().unwrap(); match record.level() { Level::Error => logger.err(message), Level::Warn => logger.warning(message), Level::Info => logger.info(message), Level::Debug => logger.debug(message), Level::Trace => logger.debug(message), }; } fn flush(&self) { let _ = self.logger.lock().unwrap().backend.flush(); } } /// Unix socket Logger init function compatible with log crate #[cfg(unix)] pub fn init_unix(facility: Facility, log_level: log::LevelFilter) -> Result<()> { let (process, pid) = get_process_info()?; let formatter = Formatter3164 { facility, hostname: None, process, pid, }; unix(formatter).and_then(|logger| { log::set_boxed_logger(Box::new(BasicLogger::new(logger))) .chain_err(|| ErrorKind::Initialization) })?; log::set_max_level(log_level); Ok(()) } #[cfg(not(unix))] pub fn init_unix(_facility: Facility, _log_level: log::LevelFilter) -> Result<()> { Err(ErrorKind::UnsupportedPlatform)? } /// Unix socket Logger init function compatible with log crate and user provided socket path #[cfg(unix)] pub fn init_unix_custom>( facility: Facility, log_level: log::LevelFilter, path: P, ) -> Result<()> { let (process, pid) = get_process_info()?; let formatter = Formatter3164 { facility, hostname: None, process, pid, }; unix_custom(formatter, path).and_then(|logger| { log::set_boxed_logger(Box::new(BasicLogger::new(logger))) .chain_err(|| ErrorKind::Initialization) })?; log::set_max_level(log_level); Ok(()) } #[cfg(not(unix))] pub fn init_unix_custom>( _facility: Facility, _log_level: log::LevelFilter, _path: P, ) -> Result<()> { Err(ErrorKind::UnsupportedPlatform)? } /// UDP Logger init function compatible with log crate pub fn init_udp( local: T, server: T, hostname: String, facility: Facility, log_level: log::LevelFilter, ) -> Result<()> { let (process, pid) = get_process_info()?; let formatter = Formatter3164 { facility, hostname: Some(hostname), process, pid, }; udp(formatter, local, server).and_then(|logger| { log::set_boxed_logger(Box::new(BasicLogger::new(logger))) .chain_err(|| ErrorKind::Initialization) })?; log::set_max_level(log_level); Ok(()) } /// TCP Logger init function compatible with log crate pub fn init_tcp( server: T, hostname: String, facility: Facility, log_level: log::LevelFilter, ) -> Result<()> { let (process, pid) = get_process_info()?; let formatter = Formatter3164 { facility, hostname: Some(hostname), process, pid, }; tcp(formatter, server).and_then(|logger| { log::set_boxed_logger(Box::new(BasicLogger::new(logger))) .chain_err(|| ErrorKind::Initialization) })?; log::set_max_level(log_level); Ok(()) } /// Initializes logging subsystem for log crate /// /// This tries to connect to syslog by following ways: /// /// 1. Unix sockets /dev/log and /var/run/syslog (in this order) /// 2. Tcp connection to 127.0.0.1:601 /// 3. Udp connection to 127.0.0.1:514 /// /// Note the last option usually (almost) never fails in this method. So /// this method doesn't return error even if there is no syslog. /// /// If `application_name` is `None` name is derived from executable name pub fn init( facility: Facility, log_level: log::LevelFilter, application_name: Option<&str>, ) -> Result<()> { let (process_name, pid) = get_process_info()?; let process = application_name.map(From::from).unwrap_or(process_name); let formatter = Formatter3164 { facility, hostname: get_hostname().ok(), process, pid, }; let backend = unix(formatter.clone()) .map(|logger: Logger| logger.backend) .or_else(|_| { TcpStream::connect(("127.0.0.1", 601)).map(|s| LoggerBackend::Tcp(BufWriter::new(s))) }) .or_else(|_| { let udp_addr = "127.0.0.1:514".parse().unwrap(); UdpSocket::bind(("127.0.0.1", 0)).map(|s| LoggerBackend::Udp(s, udp_addr)) })?; log::set_boxed_logger(Box::new(BasicLogger::new(Logger { formatter, backend }))) .chain_err(|| ErrorKind::Initialization)?; log::set_max_level(log_level); Ok(()) } fn get_process_info() -> Result<(String, u32)> { env::current_exe() .chain_err(|| ErrorKind::Initialization) .and_then(|path| { path.file_name() .and_then(|os_name| os_name.to_str()) .map(|name| name.to_string()) .chain_err(|| ErrorKind::Initialization) }) .map(|name| (name, process::id())) } fn get_hostname() -> Result { Ok(hostname::get()?.to_string_lossy().to_string()) }