1use chrono::{
34 format::{DelayedFormat, Fixed, Item},
35 DateTime, Local,
36};
37use log::{Level, Record};
38use serde::ser::{self, Serialize, SerializeMap};
39use std::{fmt, option, thread};
40
41#[cfg(feature = "config_parsing")]
42use crate::config::{Deserialize, Deserializers};
43use crate::encode::{Encode, Write, NEWLINE};
44
45#[cfg(feature = "config_parsing")]
47#[derive(Clone, Eq, PartialEq, Hash, Debug, Default, serde::Deserialize)]
48#[serde(deny_unknown_fields)]
49pub struct JsonEncoderConfig {
50 #[serde(skip_deserializing)]
51 _p: (),
52}
53
54#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug, Default)]
56pub struct JsonEncoder(());
57
58impl JsonEncoder {
59 pub fn new() -> Self {
61 Self::default()
62 }
63}
64
65impl JsonEncoder {
66 fn encode_inner(
67 &self,
68 w: &mut dyn Write,
69 time: DateTime<Local>,
70 record: &Record,
71 ) -> anyhow::Result<()> {
72 let thread = thread::current();
73 let message = Message {
74 time: time.format_with_items(Some(Item::Fixed(Fixed::RFC3339)).into_iter()),
75 level: record.level(),
76 message: record.args(),
77 module_path: record.module_path(),
78 file: record.file(),
79 line: record.line(),
80 target: record.target(),
81 thread: thread.name(),
82 thread_id: thread_id::get(),
83 mdc: Mdc,
84 #[cfg(feature = "log_kv")]
85 attributes: kv::Attributes(record.key_values()),
86 };
87 message.serialize(&mut serde_json::Serializer::new(&mut *w))?;
88 w.write_all(NEWLINE.as_bytes())?;
89 Ok(())
90 }
91}
92
93impl Encode for JsonEncoder {
94 fn encode(&self, w: &mut dyn Write, record: &Record) -> anyhow::Result<()> {
95 self.encode_inner(w, Local::now(), record)
96 }
97}
98
99#[derive(serde::Serialize)]
100struct Message<'a> {
101 #[serde(serialize_with = "ser_display")]
102 time: DelayedFormat<option::IntoIter<Item<'a>>>,
103 level: Level,
104 #[serde(serialize_with = "ser_display")]
105 message: &'a fmt::Arguments<'a>,
106 #[serde(skip_serializing_if = "Option::is_none")]
107 module_path: Option<&'a str>,
108 #[serde(skip_serializing_if = "Option::is_none")]
109 file: Option<&'a str>,
110 #[serde(skip_serializing_if = "Option::is_none")]
111 line: Option<u32>,
112 target: &'a str,
113 thread: Option<&'a str>,
114 thread_id: usize,
115 mdc: Mdc,
116 #[cfg(feature = "log_kv")]
117 attributes: kv::Attributes<'a>,
118}
119
120fn ser_display<T, S>(v: &T, s: S) -> Result<S::Ok, S::Error>
121where
122 T: fmt::Display,
123 S: ser::Serializer,
124{
125 s.collect_str(v)
126}
127
128struct Mdc;
129
130impl ser::Serialize for Mdc {
131 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
132 where
133 S: ser::Serializer,
134 {
135 let mut map = serializer.serialize_map(None)?;
136
137 let mut err = Ok(());
138 log_mdc::iter(|k, v| {
139 if let Ok(()) = err {
140 err = map.serialize_key(k).and_then(|()| map.serialize_value(v));
141 }
142 });
143 err?;
144
145 map.end()
146 }
147}
148
149#[cfg(feature = "config_parsing")]
157#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug, Default)]
158pub struct JsonEncoderDeserializer;
159
160#[cfg(feature = "config_parsing")]
161impl Deserialize for JsonEncoderDeserializer {
162 type Trait = dyn Encode;
163
164 type Config = JsonEncoderConfig;
165
166 fn deserialize(
167 &self,
168 _: JsonEncoderConfig,
169 _: &Deserializers,
170 ) -> anyhow::Result<Box<dyn Encode>> {
171 Ok(Box::<JsonEncoder>::default())
172 }
173}
174#[cfg(feature = "log_kv")]
175mod kv {
176 use log::kv::VisitSource;
177 use serde::ser::{self, Error, SerializeMap};
178
179 pub(crate) struct Attributes<'a>(pub &'a dyn log::kv::Source);
180
181 pub(crate) struct SerializerVisitor<T: ser::SerializeMap>(pub T);
182
183 impl<'kvs, T: ser::SerializeMap> VisitSource<'kvs> for SerializerVisitor<T> {
184 fn visit_pair(
185 &mut self,
186 key: log::kv::Key<'kvs>,
187 value: log::kv::Value<'kvs>,
188 ) -> Result<(), log::kv::Error> {
189 self.0
190 .serialize_entry(key.as_str(), &value.to_string())
191 .map_err(|e| log::kv::Error::boxed(e.to_string()))?;
192 Ok(())
193 }
194 }
195
196 impl<'a> ser::Serialize for Attributes<'a> {
197 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
198 where
199 S: serde::Serializer,
200 {
201 let map = serializer.serialize_map(Some(self.0.count()))?;
202 let mut visitor = SerializerVisitor(map);
203 self.0.visit(&mut visitor).map_err(S::Error::custom)?;
204 visitor.0.end()
205 }
206 }
207}
208
209#[cfg(test)]
210#[cfg(feature = "simple_writer")]
211mod test {
212 #[cfg(feature = "chrono")]
213 use chrono::{DateTime, Local};
214 use log::Level;
215
216 use super::*;
217 use crate::encode::writer::simple::SimpleWriter;
218
219 #[test]
220 fn default() {
221 let time = DateTime::parse_from_rfc3339("2016-03-20T14:22:20.644420340-08:00")
222 .unwrap()
223 .with_timezone(&Local);
224 let level = Level::Debug;
225 let target = "target";
226 let module_path = "module_path";
227 let file = "file";
228 let line = 100;
229 let message = "message";
230 let thread = "encode::json::test::default";
231 log_mdc::insert("foo", "bar");
232
233 let encoder = JsonEncoder::new();
234
235 let mut record_builder = Record::builder();
236 record_builder
237 .level(level)
238 .target(target)
239 .module_path(Some(module_path))
240 .file(Some(file))
241 .line(Some(line));
242
243 #[cfg(feature = "log_kv")]
244 record_builder.key_values(&[("log_foo", "log_bar")]);
245
246 let mut buf = vec![];
247 encoder
248 .encode_inner(
249 &mut SimpleWriter(&mut buf),
250 time,
251 &record_builder.args(format_args!("{}", message)).build(),
252 )
253 .unwrap();
254
255 #[cfg(feature = "log_kv")]
256 let expected_attributes = ",\"attributes\":{\"log_foo\":\"log_bar\"}";
257 #[cfg(not(feature = "log_kv"))]
258 let expected_attributes = "";
259
260 let expected = format!(
261 "{{\"time\":\"{}\",\"level\":\"{}\",\"message\":\"{}\",\"module_path\":\"{}\",\
262 \"file\":\"{}\",\"line\":{},\"target\":\"{}\",\
263 \"thread\":\"{}\",\"thread_id\":{},\"mdc\":{{\"foo\":\"bar\"}}{}}}",
264 time.to_rfc3339(),
265 level,
266 message,
267 module_path,
268 file,
269 line,
270 target,
271 thread,
272 thread_id::get(),
273 expected_attributes
274 );
275 assert_eq!(expected, String::from_utf8(buf).unwrap().trim());
276 }
277}