use std::{fmt, io, process, result, cell::RefCell, fmt::Write};
use chrono;
use serde;
use serde_json;
use slog;
use serde::ser::SerializeMap;
use slog::{FnValue, Key, OwnedKVList, Record, SendSyncRefUnwindSafeKV, KV};
use util::level_to_severity;
thread_local! {
static TL_BUF: RefCell<String> = RefCell::new(String::with_capacity(128))
}
struct SerdeSerializer<S: serde::Serializer> {
ser_map: S::SerializeMap,
}
impl<S: serde::Serializer> SerdeSerializer<S> {
fn start(ser: S, len: Option<usize>) -> result::Result<Self, slog::Error> {
let ser_map = ser.serialize_map(len)
.map_err(|_| io::Error::new(io::ErrorKind::Other, "serde serialization error"))?;
Ok(SerdeSerializer { ser_map: ser_map })
}
fn end(self) -> result::Result<S::Ok, S::Error> {
self.ser_map.end()
}
}
macro_rules! impl_m(
($s:expr, $key:expr, $val:expr) => ({
let k_s: &str = $key.as_ref();
$s.ser_map.serialize_entry(k_s, $val)
.map_err(|_| io::Error::new(io::ErrorKind::Other, "serde serialization error"))?;
Ok(())
});
);
impl<S> slog::Serializer for SerdeSerializer<S>
where
S: serde::Serializer,
{
fn emit_bool(&mut self, key: Key, val: bool) -> slog::Result {
impl_m!(self, key, &val)
}
fn emit_unit(&mut self, key: Key) -> slog::Result {
impl_m!(self, key, &())
}
fn emit_char(&mut self, key: Key, val: char) -> slog::Result {
impl_m!(self, key, &val)
}
fn emit_none(&mut self, key: Key) -> slog::Result {
let val: Option<()> = None;
impl_m!(self, key, &val)
}
fn emit_u8(&mut self, key: Key, val: u8) -> slog::Result {
impl_m!(self, key, &val)
}
fn emit_i8(&mut self, key: Key, val: i8) -> slog::Result {
impl_m!(self, key, &val)
}
fn emit_u16(&mut self, key: Key, val: u16) -> slog::Result {
impl_m!(self, key, &val)
}
fn emit_i16(&mut self, key: Key, val: i16) -> slog::Result {
impl_m!(self, key, &val)
}
fn emit_usize(&mut self, key: Key, val: usize) -> slog::Result {
impl_m!(self, key, &val)
}
fn emit_isize(&mut self, key: Key, val: isize) -> slog::Result {
impl_m!(self, key, &val)
}
fn emit_u32(&mut self, key: Key, val: u32) -> slog::Result {
impl_m!(self, key, &val)
}
fn emit_i32(&mut self, key: Key, val: i32) -> slog::Result {
impl_m!(self, key, &val)
}
fn emit_f32(&mut self, key: Key, val: f32) -> slog::Result {
impl_m!(self, key, &val)
}
fn emit_u64(&mut self, key: Key, val: u64) -> slog::Result {
impl_m!(self, key, &val)
}
fn emit_i64(&mut self, key: Key, val: i64) -> slog::Result {
impl_m!(self, key, &val)
}
fn emit_f64(&mut self, key: Key, val: f64) -> slog::Result {
impl_m!(self, key, &val)
}
fn emit_str(&mut self, key: Key, val: &str) -> slog::Result {
impl_m!(self, key, &val)
}
fn emit_arguments(&mut self, key: Key, val: &fmt::Arguments) -> slog::Result {
TL_BUF.with(|buf| {
let mut buf = buf.borrow_mut();
buf.write_fmt(*val).unwrap();
let res = { || impl_m!(self, key, &*buf) }();
buf.clear();
res
})
}
#[cfg(feature = "nested-values")]
fn emit_serde(&mut self, key: Key, value: &slog::SerdeValue) -> slog::Result {
impl_m!(self, key, value.as_serde())
}
}
pub struct MozLogJson<W: io::Write> {
newlines: bool,
values: Vec<OwnedKVList>,
io: RefCell<W>,
pretty: bool,
}
impl<W> MozLogJson<W>
where
W: io::Write,
{
pub fn default(io: W) -> MozLogJson<W> {
MozLogJsonBuilder::new(io).build()
}
#[cfg_attr(feature = "cargo-clippy", allow(new_ret_no_self))]
pub fn new(io: W) -> MozLogJsonBuilder<W> {
MozLogJsonBuilder::new(io)
}
fn log_placeholder_impl<F>(
&self,
serializer: &mut serde_json::ser::Serializer<&mut io::Cursor<Vec<u8>>, F>,
rinfo: &Record,
) -> io::Result<()>
where
F: serde_json::ser::Formatter,
{
let mut serializer = SerdeSerializer::start(&mut *serializer, None)?;
for kv in &self.values {
kv.serialize(rinfo, &mut serializer)?;
}
let fields_placeholder = kv!("Fields" => "00PLACEHOLDER00");
fields_placeholder.serialize(rinfo, &mut serializer)?;
let res = serializer.end();
res.map_err(|e| io::Error::new(io::ErrorKind::Other, e))?;
Ok(())
}
fn log_fields_impl<F>(
&self,
serializer: &mut serde_json::ser::Serializer<&mut io::Cursor<Vec<u8>>, F>,
rinfo: &Record,
logger_values: &OwnedKVList,
) -> io::Result<()>
where
F: serde_json::ser::Formatter,
{
let mut serializer = SerdeSerializer::start(&mut *serializer, None)?;
let msg = kv!("msg" => format!("{}", rinfo.msg()));
msg.serialize(rinfo, &mut serializer)?;
logger_values.serialize(rinfo, &mut serializer)?;
rinfo.kv().serialize(rinfo, &mut serializer)?;
Ok(())
}
}
impl<W> slog::Drain for MozLogJson<W>
where
W: io::Write,
{
type Ok = ();
type Err = io::Error;
fn log(&self, rinfo: &Record, logger_values: &OwnedKVList) -> io::Result<()> {
let mut buf = io::Cursor::new(Vec::new());
if self.pretty {
let mut serializer = serde_json::Serializer::pretty(&mut buf);
self.log_placeholder_impl(&mut serializer, &rinfo)?;
} else {
let mut serializer = serde_json::Serializer::new(&mut buf);
self.log_placeholder_impl(&mut serializer, &rinfo)?;
};
let payload = String::from_utf8(buf.into_inner()).unwrap();
let mut buf = io::Cursor::new(Vec::new());
if self.pretty {
let mut serializer = serde_json::Serializer::pretty(&mut buf);
self.log_fields_impl(&mut serializer, &rinfo, &logger_values)?;
} else {
let mut serializer = serde_json::Serializer::new(&mut buf);
self.log_fields_impl(&mut serializer, &rinfo, &logger_values)?;
};
let fields = String::from_utf8(buf.into_inner()).unwrap();
let mut payload = payload.replace("\"00PLACEHOLDER00\"", fields.as_str());
payload.push_str("}");
let mut io = self.io.borrow_mut();
io.write_all(payload.as_bytes())?;
if self.newlines {
io.write_all(b"\n")?;
}
Ok(())
}
}
pub struct MozLogJsonBuilder<W: io::Write> {
newlines: bool,
values: Vec<OwnedKVList>,
io: W,
pretty: bool,
logger_name: Option<String>,
msg_type: Option<String>,
hostname: Option<String>,
}
impl<W> MozLogJsonBuilder<W>
where
W: io::Write,
{
fn new(io: W) -> Self {
MozLogJsonBuilder {
newlines: true,
values: vec![],
io: io,
pretty: false,
logger_name: None,
msg_type: None,
hostname: None,
}
}
pub fn build(mut self) -> MozLogJson<W> {
let mut values: Vec<OwnedKVList> = vec![];
if let Some(ref logger_name) = self.logger_name {
values.push(o!("Logger" => logger_name.to_owned()).into());
}
if let Some(ref msg_type) = self.msg_type {
values.push(o!("Type" => msg_type.to_owned()).into());
}
if let Some(ref hostname) = self.hostname {
values.push(o!("Hostname" => hostname.to_owned()).into());
}
values.push(
o!(
"Timestamp" => FnValue(|_ : &Record| {
let now = chrono::Utc::now();
let nsec: i64 = (now.timestamp() as i64) * 1_000_000_000;
nsec + (now.timestamp_subsec_nanos() as i64)
}),
"Severity" => FnValue(|record : &Record| {
level_to_severity(record.level())
}),
"Pid" => process::id(),
).into(),
);
self.values.extend(values);
MozLogJson {
values: self.values,
newlines: self.newlines,
io: RefCell::new(self.io),
pretty: self.pretty,
}
}
pub fn set_newlines(mut self, enabled: bool) -> Self {
self.newlines = enabled;
self
}
pub fn set_pretty(mut self, enabled: bool) -> Self {
self.pretty = enabled;
self
}
pub fn add_key_value<T>(mut self, value: slog::OwnedKV<T>) -> Self
where
T: SendSyncRefUnwindSafeKV + 'static,
{
self.values.push(value.into());
self
}
pub fn logger_name(mut self, logger_name: String) -> Self {
self.logger_name = Some(logger_name);
self
}
pub fn msg_type(mut self, msg_type: String) -> Self {
self.msg_type = Some(msg_type);
self
}
pub fn hostname(mut self, hostname: String) -> Self {
self.hostname = Some(hostname);
self
}
}