use crate::NEWLINE;
use chrono::{
format::{DelayedFormat, Fixed, Item},
DateTime, Local,
};
use log::{Level, Record};
use log4rs::encode::{Encode, Write};
use serde::ser::{self, Serialize, SerializeMap};
use serde_json;
use std::{fmt, option, thread};
#[derive(Clone, Eq, PartialEq, Hash, Debug, Default)]
pub struct JsonEncoder(Option<String>);
impl JsonEncoder {
pub fn new(target: Option<String>) -> Self {
JsonEncoder(target)
}
}
impl JsonEncoder {
fn encode_inner(
&self,
w: &mut dyn Write,
time: DateTime<Local>,
record: &Record,
) -> anyhow::Result<()> {
let tmp;
let target = if let Some(t) = self.0.clone() {
tmp = t;
&tmp
} else {
record.target()
};
let thread = thread::current();
let message = Message {
time: time.format_with_items(Some(Item::Fixed(Fixed::RFC3339)).into_iter()),
message: record.args(),
level: record.level(),
module_path: record.module_path(),
file: record.file(),
line: record.line(),
target: target,
thread: thread.name(),
thread_id: thread_id::get(),
};
message.serialize(&mut serde_json::Serializer::new(&mut *w))?;
w.write_all(NEWLINE.as_bytes())?;
Ok(())
}
}
impl Encode for JsonEncoder {
fn encode(&self, w: &mut dyn Write, record: &Record) -> anyhow::Result<()> {
self.encode_inner(w, Local::now(), record)
}
}
#[derive(serde::Serialize)]
struct Message<'a> {
#[serde(serialize_with = "ser_display")]
time: DelayedFormat<option::IntoIter<Item<'a>>>,
#[serde(serialize_with = "ser_display")]
message: &'a fmt::Arguments<'a>,
#[serde(skip_serializing_if = "Option::is_none")]
module_path: Option<&'a str>,
#[serde(skip_serializing_if = "Option::is_none")]
file: Option<&'a str>,
#[serde(skip_serializing_if = "Option::is_none", serialize_with = "ser_option_line")]
line: Option<u32>,
#[serde(serialize_with = "ser_display")]
level: Level,
target: &'a str,
#[serde(skip_serializing_if = "Option::is_none")]
thread: Option<&'a str>,
#[serde(serialize_with = "ser_display")]
thread_id: usize,
}
fn ser_display<T, S>(v: &T, s: S) -> Result<S::Ok, S::Error>
where
T: fmt::Display,
S: ser::Serializer,
{
s.collect_str(v)
}
fn ser_option_line<S>(v: &Option<u32>, s: S) -> Result<S::Ok, S::Error>
where
S: ser::Serializer,
{
match v {
Some(line) => s.serialize_str(&line.to_string()),
None => s.serialize_none(),
}
}