#![warn(missing_docs)]
#[macro_use]
extern crate slog;
extern crate chrono;
extern crate serde;
extern crate serde_json;
use serde::ser::SerializeMap;
use slog::{FnValue, PushFnValue};
use slog::{OwnedKVList, KV, ThreadSafeKV};
use slog::Record;
use std::{io, result, fmt};
use std::cell::RefCell;
use std::fmt::Write;
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 = try!(ser.serialize_map(len)
.map_err(|_| {
io::Error::new(io::ErrorKind::Other,
"serde serialization error")
}));
Ok(SerdeSerializer { ser_map: ser_map })
}
fn end(self) -> std::result::Result<S::Ok, S::Error> {
self.ser_map.end()
}
}
macro_rules! impl_m(
($s:expr, $key:expr, $val:expr) => ({
try!($s.ser_map.serialize_entry($key, $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: &str, val: bool) -> slog::Result {
impl_m!(self, key, &val)
}
fn emit_unit(&mut self, key: &str) -> slog::Result {
impl_m!(self, key, &())
}
fn emit_char(&mut self, key: &str, val: char) -> slog::Result {
impl_m!(self, key, &val)
}
fn emit_none(&mut self, key: &str) -> slog::Result {
let val: Option<()> = None;
impl_m!(self, key, &val)
}
fn emit_u8(&mut self, key: &str, val: u8) -> slog::Result {
impl_m!(self, key, &val)
}
fn emit_i8(&mut self, key: &str, val: i8) -> slog::Result {
impl_m!(self, key, &val)
}
fn emit_u16(&mut self, key: &str, val: u16) -> slog::Result {
impl_m!(self, key, &val)
}
fn emit_i16(&mut self, key: &str, val: i16) -> slog::Result {
impl_m!(self, key, &val)
}
fn emit_usize(&mut self, key: &str, val: usize) -> slog::Result {
impl_m!(self, key, &val)
}
fn emit_isize(&mut self, key: &str, val: isize) -> slog::Result {
impl_m!(self, key, &val)
}
fn emit_u32(&mut self, key: &str, val: u32) -> slog::Result {
impl_m!(self, key, &val)
}
fn emit_i32(&mut self, key: &str, val: i32) -> slog::Result {
impl_m!(self, key, &val)
}
fn emit_f32(&mut self, key: &str, val: f32) -> slog::Result {
impl_m!(self, key, &val)
}
fn emit_u64(&mut self, key: &str, val: u64) -> slog::Result {
impl_m!(self, key, &val)
}
fn emit_i64(&mut self, key: &str, val: i64) -> slog::Result {
impl_m!(self, key, &val)
}
fn emit_f64(&mut self, key: &str, val: f64) -> slog::Result {
impl_m!(self, key, &val)
}
fn emit_str(&mut self, key: &str, val: &str) -> slog::Result {
impl_m!(self, key, &val)
}
fn emit_arguments(&mut self,
key: &str,
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
})
}
}
pub struct Json<W: io::Write> {
newlines: bool,
values: Vec<OwnedKVList>,
io: RefCell<W>,
}
impl<W> Json<W>
where W: io::Write
{
pub fn default(io: W) -> Json<W> {
JsonBuilder::new(io).add_default_keys().build()
}
#[cfg_attr(feature = "cargo-clippy", allow(new_ret_no_self))]
pub fn new(io: W) -> JsonBuilder<W> {
JsonBuilder::new(io)
}
}
pub struct JsonBuilder<W: io::Write> {
newlines: bool,
values: Vec<OwnedKVList>,
io: W,
}
impl<W> JsonBuilder<W>
where W: io::Write
{
fn new(io: W) -> Self {
JsonBuilder {
newlines: true,
values: vec![],
io: io,
}
}
pub fn build(self) -> Json<W> {
Json {
values: self.values,
newlines: self.newlines,
io: RefCell::new(self.io),
}
}
pub fn set_newlines(mut self, enabled: bool) -> Self {
self.newlines = enabled;
self
}
pub fn add_key_value<T>(mut self, value: slog::OwnedKV<T>) -> Self
where T: ThreadSafeKV + 'static
{
self.values.push(value.into());
self
}
pub fn add_default_keys(self) -> Self {
self.add_key_value(o!(
"ts" => PushFnValue(move |_ : &Record, ser| {
ser.serialize(chrono::Local::now().to_rfc3339())
}),
"level" => FnValue(move |rinfo : &Record| {
rinfo.level().as_short_str()
}),
"msg" => PushFnValue(move |record : &Record, ser| {
ser.serialize(record.msg())
}),
))
}
}
impl<W> slog::Drain for Json<W>
where W: io::Write
{
type Ok = ();
type Err = io::Error;
fn log(&self,
rinfo: &Record,
logger_values: &OwnedKVList)
-> io::Result<()> {
let mut io = self.io.borrow_mut();
let mut io = {
let mut serializer = serde_json::Serializer::new(&mut *io);
{
let mut serializer =
try!(SerdeSerializer::start(&mut serializer, None));
for kv in &self.values {
try!(kv.serialize(rinfo, &mut serializer));
}
try!(logger_values.serialize(rinfo, &mut serializer));
try!(rinfo.kv().serialize(rinfo, &mut serializer));
let res = serializer.end();
try!(res.map_err(|e| io::Error::new(io::ErrorKind::Other, e)));
}
serializer.into_inner()
};
if self.newlines {
try!(io.write_all("\n".as_bytes()));
}
Ok(())
}
}