1use chrono::{
29 format::{DelayedFormat, Fixed, Item},
30 DateTime, Local,
31};
32use log::{Level, Record};
33use serde::ser::{self, Serialize, SerializeMap};
34use std::{fmt, option, thread};
35
36#[cfg(feature = "config_parsing")]
37use crate::config::{Deserialize, Deserializers};
38use crate::encode::{Encode, Write, NEWLINE};
39
40#[cfg(feature = "config_parsing")]
42#[derive(Clone, Eq, PartialEq, Hash, Debug, Default, serde::Deserialize)]
43#[serde(deny_unknown_fields)]
44pub struct JsonEncoderConfig {
45 #[serde(skip_deserializing)]
46 _p: (),
47}
48
49#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug, Default)]
51pub struct JsonEncoder(());
52
53impl JsonEncoder {
54 pub fn new() -> Self {
56 Self::default()
57 }
58}
59
60impl JsonEncoder {
61 fn encode_inner(
62 &self,
63 w: &mut dyn Write,
64 time: DateTime<Local>,
65 record: &Record,
66 ) -> anyhow::Result<()> {
67 let thread = thread::current();
68 let message = Message {
69 time: time.format_with_items(Some(Item::Fixed(Fixed::RFC3339)).into_iter()),
70 message: record.args(),
71 level: record.level(),
72 module_path: record.module_path(),
73 file: record.file(),
74 line: record.line(),
75 target: record.target(),
76 thread: thread.name(),
77 thread_id: thread_id::get(),
78 mdc: Mdc,
79 };
80 message.serialize(&mut serde_json::Serializer::new(&mut *w))?;
81 w.write_all(NEWLINE.as_bytes())?;
82 Ok(())
83 }
84}
85
86impl Encode for JsonEncoder {
87 fn encode(&self, w: &mut dyn Write, record: &Record) -> anyhow::Result<()> {
88 self.encode_inner(w, Local::now(), record)
89 }
90}
91
92#[derive(serde::Serialize)]
93struct Message<'a> {
94 #[serde(serialize_with = "ser_display")]
95 time: DelayedFormat<option::IntoIter<Item<'a>>>,
96 #[serde(serialize_with = "ser_display")]
97 message: &'a fmt::Arguments<'a>,
98 #[serde(skip_serializing_if = "Option::is_none")]
99 module_path: Option<&'a str>,
100 #[serde(skip_serializing_if = "Option::is_none")]
101 file: Option<&'a str>,
102 #[serde(skip_serializing_if = "Option::is_none")]
103 line: Option<u32>,
104 level: Level,
105 target: &'a str,
106 thread: Option<&'a str>,
107 thread_id: usize,
108 mdc: Mdc,
109}
110
111fn ser_display<T, S>(v: &T, s: S) -> Result<S::Ok, S::Error>
112where
113 T: fmt::Display,
114 S: ser::Serializer,
115{
116 s.collect_str(v)
117}
118
119struct Mdc;
120
121impl ser::Serialize for Mdc {
122 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
123 where
124 S: ser::Serializer,
125 {
126 let mut map = serializer.serialize_map(None)?;
127
128 let mut err = Ok(());
129 log_mdc::iter(|k, v| {
130 if let Ok(()) = err {
131 err = map.serialize_key(k).and_then(|()| map.serialize_value(v));
132 }
133 });
134 err?;
135
136 map.end()
137 }
138}
139
140#[cfg(feature = "config_parsing")]
148#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug, Default)]
149pub struct JsonEncoderDeserializer;
150
151#[cfg(feature = "config_parsing")]
152impl Deserialize for JsonEncoderDeserializer {
153 type Trait = dyn Encode;
154
155 type Config = JsonEncoderConfig;
156
157 fn deserialize(
158 &self,
159 _: JsonEncoderConfig,
160 _: &Deserializers,
161 ) -> anyhow::Result<Box<dyn Encode>> {
162 Ok(Box::new(JsonEncoder::default()))
163 }
164}
165
166#[cfg(test)]
167#[cfg(feature = "simple_writer")]
168mod test {
169 #[cfg(feature = "chrono")]
170 use chrono::{DateTime, Local};
171 use log::Level;
172
173 use super::*;
174 use crate::encode::writer::simple::SimpleWriter;
175
176 #[test]
177 fn default() {
178 let time = DateTime::parse_from_rfc3339("2016-03-20T14:22:20.644420340-08:00")
179 .unwrap()
180 .with_timezone(&Local);
181 let level = Level::Debug;
182 let target = "target";
183 let module_path = "module_path";
184 let file = "file";
185 let line = 100;
186 let message = "message";
187 let thread = "encode::json::test::default";
188 log_mdc::insert("foo", "bar");
189
190 let encoder = JsonEncoder::new();
191
192 let mut buf = vec![];
193 encoder
194 .encode_inner(
195 &mut SimpleWriter(&mut buf),
196 time,
197 &Record::builder()
198 .level(level)
199 .target(target)
200 .module_path(Some(module_path))
201 .file(Some(file))
202 .line(Some(line))
203 .args(format_args!("{}", message))
204 .build(),
205 )
206 .unwrap();
207
208 let expected = format!(
209 "{{\"time\":\"{}\",\"message\":\"{}\",\"module_path\":\"{}\",\
210 \"file\":\"{}\",\"line\":{},\"level\":\"{}\",\"target\":\"{}\",\
211 \"thread\":\"{}\",\"thread_id\":{},\"mdc\":{{\"foo\":\"bar\"}}}}",
212 time.to_rfc3339(),
213 message,
214 module_path,
215 file,
216 line,
217 level,
218 target,
219 thread,
220 thread_id::get(),
221 );
222 assert_eq!(expected, String::from_utf8(buf).unwrap().trim());
223 }
224}