1extern crate log;
55extern crate time;
56
57use std::env;
58#[cfg(not(unix))]
59use std::io;
60use std::io::{BufWriter, ErrorKind};
61use std::net::{TcpStream, ToSocketAddrs, UdpSocket};
62#[cfg(unix)]
63use std::os::unix::net::{UnixDatagram, UnixStream};
64use std::path::Path;
65use std::process;
66
67mod errors;
68mod facility;
69mod format;
70mod logger;
71#[cfg(test)]
72mod tests;
73
74pub use errors::*;
75pub use facility::Facility;
76pub use format::Severity;
77pub use format::{Formatter3164, Formatter5424, LogFormat};
78pub use logger::{Logger, Logger3164, Logger5424, LoggerBackend};
79
80pub type Priority = u8;
81
82#[cfg(unix)]
83const UNIX_SOCK_PATHS: [&str; 3] = ["/dev/log", "/var/run/syslog", "/var/run/log"];
84
85#[cfg(unix)]
87pub fn unix<F: Clone>(formatter: F) -> Result<Logger<LoggerBackend, F>> {
88 UNIX_SOCK_PATHS
89 .iter()
90 .find_map(|path| {
91 unix_connect(formatter.clone(), *path).map_or_else(
92 |e| {
93 if let Error::Io(ref io_err) = e {
94 if io_err.kind() == ErrorKind::NotFound {
95 None } else {
97 Some(Err(e))
98 }
99 } else {
100 Some(Err(e))
101 }
102 },
103 |logger| Some(Ok(logger)),
104 )
105 })
106 .transpose()
107 .map_err(|e| Error::Initialization(Box::new(e)))?
108 .ok_or_else(|| Error::Initialization("unix socket paths not found".into()))
109}
110
111#[cfg(not(unix))]
112pub fn unix<F: Clone>(_formatter: F) -> Result<Logger<LoggerBackend, F>> {
113 Err(io::Error::from(ErrorKind::Unsupported))?
114}
115
116#[cfg(unix)]
118pub fn unix_custom<P: AsRef<Path>, F>(formatter: F, path: P) -> Result<Logger<LoggerBackend, F>> {
119 unix_connect(formatter, path).map_err(|e| Error::Initialization(Box::new(e)))
120}
121
122#[cfg(not(unix))]
123pub fn unix_custom<P: AsRef<Path>, F>(_formatter: F, _path: P) -> Result<Logger<LoggerBackend, F>> {
124 Err(io::Error::from(ErrorKind::Unsupported))?
125}
126
127#[cfg(unix)]
128fn unix_connect<P: AsRef<Path>, F>(formatter: F, path: P) -> Result<Logger<LoggerBackend, F>> {
129 let sock = UnixDatagram::unbound()?;
130 match sock.connect(&path) {
131 Ok(()) => Ok(Logger {
132 formatter,
133 backend: LoggerBackend::Unix(sock),
134 }),
135 Err(ref e) if e.raw_os_error() == Some(libc::EPROTOTYPE) => {
136 let sock = UnixStream::connect(path)?;
137 Ok(Logger {
138 formatter,
139 backend: LoggerBackend::UnixStream(BufWriter::new(sock)),
140 })
141 }
142 Err(e) => Err(e.into()),
143 }
144}
145
146pub fn udp<T: ToSocketAddrs, U: ToSocketAddrs, F>(
148 formatter: F,
149 local: T,
150 server: U,
151) -> Result<Logger<LoggerBackend, F>> {
152 server
153 .to_socket_addrs()
154 .map_err(|e| Error::Initialization(Box::new(e)))
155 .and_then(|mut server_addr_opt| {
156 server_addr_opt
157 .next()
158 .ok_or_else(|| Error::Initialization("no server address".into()))
159 })
160 .and_then(|server_addr| {
161 UdpSocket::bind(local)
162 .map_err(|e| Error::Initialization(Box::new(e)))
163 .map(|socket| Logger {
164 formatter,
165 backend: LoggerBackend::Udp(socket, server_addr),
166 })
167 })
168}
169
170pub fn tcp<T: ToSocketAddrs, F>(formatter: F, server: T) -> Result<Logger<LoggerBackend, F>> {
172 TcpStream::connect(server)
173 .map_err(|e| Error::Initialization(e.into()))
174 .map(|socket| Logger {
175 formatter,
176 backend: LoggerBackend::Tcp(BufWriter::new(socket)),
177 })
178}
179
180#[cfg(unix)]
182pub fn init_unix(facility: Facility, log_level: log::LevelFilter) -> Result<()> {
183 let (process, pid) = get_process_info()?;
184 let formatter = Formatter3164 {
185 facility,
186 hostname: None,
187 process,
188 pid,
189 };
190 unix(formatter).and_then(|logger| {
191 log::set_boxed_logger(Box::new(Logger3164::new(logger)))
192 .map_err(|e| Error::Initialization(Box::new(e)))
193 })?;
194
195 log::set_max_level(log_level);
196 Ok(())
197}
198
199#[cfg(not(unix))]
200pub fn init_unix(_facility: Facility, _log_level: log::LevelFilter) -> Result<()> {
201 Err(io::Error::from(ErrorKind::Unsupported))?
202}
203
204#[cfg(unix)]
206pub fn init_unix_custom<P: AsRef<Path>>(
207 facility: Facility,
208 log_level: log::LevelFilter,
209 path: P,
210) -> Result<()> {
211 let (process, pid) = get_process_info()?;
212 let formatter = Formatter3164 {
213 facility,
214 hostname: None,
215 process,
216 pid,
217 };
218 unix_custom(formatter, path).and_then(|logger| {
219 log::set_boxed_logger(Box::new(Logger3164::new(logger)))
220 .map_err(|e| Error::Initialization(Box::new(e)))
221 })?;
222
223 log::set_max_level(log_level);
224 Ok(())
225}
226
227#[cfg(not(unix))]
228pub fn init_unix_custom<P: AsRef<Path>>(
229 _facility: Facility,
230 _log_level: log::LevelFilter,
231 _path: P,
232) -> Result<()> {
233 Err(io::Error::from(ErrorKind::Unsupported))?
234}
235
236pub fn init_udp<T: ToSocketAddrs>(
238 local: T,
239 server: T,
240 hostname: String,
241 facility: Facility,
242 log_level: log::LevelFilter,
243) -> Result<()> {
244 let (process, pid) = get_process_info()?;
245 let formatter = Formatter3164 {
246 facility,
247 hostname: Some(hostname),
248 process,
249 pid,
250 };
251 udp(formatter, local, server).and_then(|logger| {
252 log::set_boxed_logger(Box::new(Logger3164::new(logger)))
253 .map_err(|e| Error::Initialization(Box::new(e)))
254 })?;
255
256 log::set_max_level(log_level);
257 Ok(())
258}
259
260pub fn init_tcp<T: ToSocketAddrs>(
262 server: T,
263 hostname: String,
264 facility: Facility,
265 log_level: log::LevelFilter,
266) -> Result<()> {
267 let (process, pid) = get_process_info()?;
268 let formatter = Formatter3164 {
269 facility,
270 hostname: Some(hostname),
271 process,
272 pid,
273 };
274
275 tcp(formatter, server).and_then(|logger| {
276 log::set_boxed_logger(Box::new(Logger3164::new(logger)))
277 .map_err(|e| Error::Initialization(Box::new(e)))
278 })?;
279
280 log::set_max_level(log_level);
281 Ok(())
282}
283
284pub fn init(
297 facility: Facility,
298 log_level: log::LevelFilter,
299 application_name: Option<&str>,
300) -> Result<()> {
301 let (process_name, pid) = get_process_info()?;
302 let process = application_name.map(From::from).unwrap_or(process_name);
303 let mut formatter = Formatter3164 {
304 facility,
305 hostname: None,
306 process,
307 pid,
308 };
309
310 let backend = if let Ok(logger) = unix(formatter.clone()) {
311 logger.backend
312 } else {
313 formatter.hostname = get_hostname().ok();
314 if let Ok(tcp_stream) = TcpStream::connect(("127.0.0.1", 601)) {
315 LoggerBackend::Tcp(BufWriter::new(tcp_stream))
316 } else {
317 let udp_addr = "127.0.0.1:514".parse().unwrap();
318 let udp_stream = UdpSocket::bind(("127.0.0.1", 0))?;
319 LoggerBackend::Udp(udp_stream, udp_addr)
320 }
321 };
322 log::set_boxed_logger(Box::new(Logger3164::new(Logger { formatter, backend })))
323 .map_err(|e| Error::Initialization(Box::new(e)))?;
324
325 log::set_max_level(log_level);
326 Ok(())
327}
328
329fn get_process_info() -> Result<(String, u32)> {
330 env::current_exe()
331 .map_err(|e| Error::Initialization(Box::new(e)))
332 .and_then(|path| {
333 path.file_name()
334 .and_then(|os_name| os_name.to_str())
335 .map(|name| name.to_string())
336 .ok_or_else(|| Error::Initialization("process name not found".into()))
337 })
338 .map(|name| (name, process::id()))
339}
340
341fn get_hostname() -> Result<String> {
342 Ok(hostname::get()?.to_string_lossy().to_string())
343}