use 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<T> {
fn format<W: Write>(&self, w: &mut W, severity: Severity, message: T) -> Result<()>;
fn emerg<W: Write>(&mut self, w: &mut W, message: T) -> Result<()> {
self.format(w, Severity::LOG_EMERG, message)
}
fn alert<W: Write>(&mut self, w: &mut W, message: T) -> Result<()> {
self.format(w, Severity::LOG_ALERT, message)
}
fn crit<W: Write>(&mut self, w: &mut W, message: T) -> Result<()> {
self.format(w, Severity::LOG_CRIT, message)
}
fn err<W: Write>(&mut self, w: &mut W, message: T) -> Result<()> {
self.format(w, Severity::LOG_ERR, message)
}
fn warning<W: Write>(&mut self, w: &mut W, message: T) -> Result<()> {
self.format(w, Severity::LOG_WARNING, message)
}
fn notice<W: Write>(&mut self, w: &mut W, message: T) -> Result<()> {
self.format(w, Severity::LOG_NOTICE, message)
}
fn info<W: Write>(&mut self, w: &mut W, message: T) -> Result<()> {
self.format(w, Severity::LOG_INFO, message)
}
fn debug<W: Write>(&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<String>,
pub process: String,
pub pid: u32,
}
impl<T: Display> LogFormat<T> for Formatter3164 {
fn format<W: Write>(&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 {
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,
}
}
}
pub type StructuredData = HashMap<String, HashMap<String, String>>;
#[derive(Clone, Debug)]
pub struct Formatter5424 {
pub facility: Facility,
pub hostname: Option<String>,
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<T: Display> LogFormat<(u32, StructuredData, T)> for Formatter5424 {
fn format<W: Write>(
&self,
w: &mut W,
severity: Severity,
log_message: (u32, StructuredData, T),
) -> Result<()> {
let (message_id, data, message) = log_message;
write!(
w,
"<{}>1 {} {} {} {} {} {} {}", 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 {
fn default() -> Self {
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)]
fn now_local() -> std::result::Result<time::OffsetDateTime, time::error::IndeterminateOffset> {
Ok(time::OffsetDateTime::now_utc())
}
#[cfg(not(unix))]
fn now_local() -> std::result::Result<time::OffsetDateTime, time::error::IndeterminateOffset> {
time::OffsetDateTime::now_local()
}
#[test]
fn test_formatter3164_defaults() {
let d = Formatter3164::default();
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());
}
#[test]
fn test_formatter5424_defaults() {
let d = Formatter5424::default();
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());
}