syslog-4.0.1/.gitignore010064400007670000024000000000301274363045200132440ustar0000000000000000build target Cargo.lock syslog-4.0.1/.travis.yml010064400007670000024000000013361326210445600133740ustar0000000000000000language: 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-4.0.1/Cargo.toml.orig010064400007670000024000000007011334074412300141430ustar0000000000000000[package] name = "syslog" version = "4.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] libc = "^0.2" time = "^0.1" log = { version = "^0.4.1", features = [ "std" ] } error-chain = "^0.11.0" [features] nightly = [] syslog-4.0.1/Cargo.toml0000644000000017610000000000000104200ustar00# 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 believe there's an error in this file please file an # issue against the rust-lang/cargo repository. If you're # editing this file be aware that the upstream Cargo.toml # will likely look very different (and much more reasonable) [package] name = "syslog" version = "4.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.11.0" [dependencies.libc] version = "^0.2" [dependencies.log] version = "^0.4.1" features = ["std"] [dependencies.time] version = "^0.1" [features] nightly = [] syslog-4.0.1/examples/write.rs010064400007670000024000000007221326212621400145730ustar0000000000000000extern 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"); } } } syslog-4.0.1/LICENSE010064400007670000024000000020511326212456000122610ustar0000000000000000Copyright (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-4.0.1/README.md010064400007670000024000000031121326212637300125360ustar0000000000000000# 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 = "^4.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. 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-4.0.1/src/errors.rs010064400007670000024000000002441326210445600137310ustar0000000000000000error_chain! { errors { Initialization Format Write } foreign_links { Io(::std::io::Error) #[doc = "Link to a `std::error::Error` type."]; } } syslog-4.0.1/src/facility.rs010064400007670000024000000040721326210437200142210ustar0000000000000000use 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) } } syslog-4.0.1/src/format.rs010064400007670000024000000070671334074354700137260ustar0000000000000000use time; use std::io::Write; use std::fmt::Display; use std::collections::HashMap; use Priority; use errors::*; use facility::Facility; #[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: i32, } impl LogFormat for Formatter3164 { fn format(&self, w: &mut W, severity: Severity, message: T) -> Result<()> { if let Some(ref hostname) = self.hostname { write!(w, "<{}>{} {} {}[{}]: {}", encode_priority(severity, self.facility), time::now().strftime("%b %d %T").unwrap(), hostname, self.process, self.pid, message).chain_err(|| ErrorKind::Format) } else { write!(w, "<{}>{} {}[{}]: {}", encode_priority(severity, self.facility), time::now().strftime("%b %d %T").unwrap(), self.process, self.pid, message).chain_err(|| ErrorKind::Format) } } } /// 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: i32, } 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<(i32, StructuredData, T)> for Formatter5424 { fn format(&self, w: &mut W, severity: Severity, log_message: (i32, StructuredData, T)) -> Result<()> { let (message_id, data, message) = log_message; write!(w, "<{}> {} {} {} {} {} {} {} {}", encode_priority(severity, self.facility), 1, // version time::now_utc().rfc3339(), 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) } } fn encode_priority(severity: Severity, facility: Facility) -> Priority { facility as u8 | severity as u8 } syslog-4.0.1/src/lib.rs010064400007670000024000000272751334074354700132070ustar0000000000000000//! 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 //! //! ``` //! extern 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"); //! } //! } //! } //! ``` #![crate_type = "lib"] #[macro_use] extern crate error_chain; extern crate libc; extern crate time; extern crate log; use std::env; use std::path::Path; use std::fmt::{self,Arguments,Display}; use std::io::{self, BufWriter, Write}; use std::sync::{Arc,Mutex}; use std::marker::PhantomData; use std::net::{SocketAddr,ToSocketAddrs,UdpSocket,TcpStream}; use std::os::unix::net::{UnixDatagram, UnixStream}; use libc::getpid; use log::{Log, Metadata, Record, Level}; mod facility; mod format; mod errors; pub use facility::Facility; pub use format::Severity; pub use errors::*; use format::{LogFormat}; pub use format::{Formatter3164, Formatter5424}; pub type Priority = u8; /// Main logging structure pub struct Logger> { formatter: Formatter, backend: Backend, phantom: PhantomData, } impl> Logger { pub fn emerg(&mut self, message: T) -> Result<()> { self.formatter.emerg(&mut self.backend, message) } pub fn alert(&mut self, message: T) -> Result<()> { self.formatter.alert(&mut self.backend, message) } pub fn crit(&mut self, message: T) -> Result<()> { self.formatter.crit(&mut self.backend, message) } pub fn err(&mut self, message: T) -> Result<()> { self.formatter.err(&mut self.backend, message) } pub fn warning(&mut self, message: T) -> Result<()> { self.formatter.warning(&mut self.backend, message) } pub fn notice(&mut self, message: T) -> Result<()> { self.formatter.notice(&mut self.backend, message) } pub fn info(&mut self, message: T) -> Result<()> { self.formatter.info(&mut self.backend, message) } pub fn debug(&mut self, message: T) -> Result<()> { self.formatter.debug(&mut self.backend, message) } } pub enum LoggerBackend { /// Unix socket, temp file path, log file path Unix(UnixDatagram), UnixStream(BufWriter), 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 { LoggerBackend::Unix(ref dgram) => { dgram.send(&message[..]) }, 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[..]) } } } fn write_fmt(&mut self, args: Arguments) -> io::Result<()> { match *self { LoggerBackend::Unix(ref dgram) => { let message = fmt::format(args); dgram.send(message.as_bytes()).map(|_| ()) }, 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) } } } fn flush(&mut self) -> io::Result<()> { match *self { LoggerBackend::Unix(_) => { Ok(()) }, LoggerBackend::UnixStream(ref mut socket) => { socket.flush() }, LoggerBackend::Udp(_, _) => { Ok(()) }, LoggerBackend::Tcp(ref mut socket) => { socket.flush() } } } } /// Returns a Logger using unix socket to target local syslog ( using /dev/log or /var/run/syslog) 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) } /// Returns a Logger using unix socket to target local syslog at user provided path pub fn unix_custom, U: Display, F: LogFormat>(formatter: F, path: P) -> Result> { unix_connect(formatter, path).chain_err(|| ErrorKind::Initialization) } fn unix_connect, U: Display, F: LogFormat>(formatter: F, path: P) -> Result> { let sock = UnixDatagram::unbound()?; match sock.connect(&path) { Ok(()) => { Ok(Logger { formatter, backend: LoggerBackend::Unix(sock), phantom: PhantomData, }) }, 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)), phantom: PhantomData, }) }, 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), phantom: PhantomData, }) }) }) } /// 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)), phantom: PhantomData, }) }) } 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 { true } 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 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(()) } /// Unix socket Logger init function compatible with log crate and user provided socket path 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(()) } /// 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: None, 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, phantom: PhantomData, })) ).chain_err(|| ErrorKind::Initialization)?; log::set_max_level(log_level); Ok(()) } fn get_process_info() -> Result<(String,i32)> { 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| { let pid = unsafe { getpid() }; (name, pid) }) }