1use std::{
2 fmt::{self, Write},
3 io,
4};
5
6use bytes::Bytes;
7use indexmap::IndexMap;
8use slog::{Key, OwnedKVList, Record};
9
10pub trait LogMessageSerializer {
12 type Serialized;
13
14 fn serialize(
16 &self,
17 record: &Record,
18 logger_values: &OwnedKVList,
19 ) -> slog::Result<Self::Serialized>;
20}
21
22pub trait KVFilter {
24 fn is_accepted(&self, key: &Key) -> bool;
26}
27
28impl<T> KVFilter for T
29where
30 T: Fn(&Key) -> bool,
31{
32 #[inline]
33 fn is_accepted(&self, key: &Key) -> bool {
34 (self)(key)
35 }
36}
37
38pub struct AcceptAll;
40
41impl KVFilter for AcceptAll {
42 #[inline]
43 fn is_accepted(&self, _: &Key) -> bool {
44 true
45 }
46}
47
48pub struct JsonMessageBuilder<'a, F = AcceptAll> {
50 field_map: IndexMap<Key, serde_json::Value>,
51 misc_map: IndexMap<Key, serde_json::Value>,
52 misc_name: Key,
53 field_filter: &'a F,
54}
55
56impl JsonMessageBuilder<'static> {
57 pub fn new() -> Self {
59 Self {
60 field_map: IndexMap::new(),
61 misc_map: IndexMap::new(),
62 misc_name: "misc",
63 field_filter: &AcceptAll,
64 }
65 }
66}
67
68impl Default for JsonMessageBuilder<'static> {
69 #[inline]
70 fn default() -> Self {
71 Self::new()
72 }
73}
74
75impl<'a, F> JsonMessageBuilder<'a, F> {
76 pub fn with_field_filter<'b, T>(
78 self,
79 fallback_field: Key,
80 filter: &'b T,
81 ) -> JsonMessageBuilder<'b, T> {
82 JsonMessageBuilder {
83 field_map: self.field_map,
84 misc_map: self.misc_map,
85 misc_name: fallback_field,
86 field_filter: filter,
87 }
88 }
89
90 pub fn finish(mut self) -> slog::Result<Bytes> {
92 if let Some(val) = self.serialize_misc_fields()? {
93 self.field_map.insert(self.misc_name, val.into());
94 }
95
96 let json = serde_json::to_vec(&self.field_map)
97 .map_err(|_| io::Error::other("unable to finalize a log message"))?;
98
99 Ok(Bytes::from(json))
100 }
101
102 fn serialize_misc_fields(&self) -> slog::Result<Option<String>> {
104 if self.misc_map.is_empty() {
105 return Ok(None);
106 }
107
108 let mut res = String::new();
109
110 let mut iter = self.misc_map.iter();
111
112 if let Some((k, v)) = iter.next() {
113 if let Some(s) = v.as_str() {
114 write!(res, "{}: {}", k, s)?;
115 } else {
116 write!(res, "{}: {}", k, v)?;
117 }
118 }
119
120 for (k, v) in iter {
121 if let Some(s) = v.as_str() {
122 write!(res, ", {}: {}", k, s)?;
123 } else {
124 write!(res, ", {}: {}", k, v)?;
125 }
126 }
127
128 Ok(Some(res))
129 }
130}
131
132impl<'a, F> JsonMessageBuilder<'a, F>
133where
134 F: KVFilter,
135{
136 fn emit_serde_json_value(&mut self, key: Key, val: serde_json::Value) -> slog::Result {
138 if self.field_filter.is_accepted(&key) && key != self.misc_name {
139 self.field_map.entry(key).or_insert(val);
140 } else {
141 self.misc_map.entry(key).or_insert(val);
142 }
143
144 Ok(())
145 }
146
147 #[inline]
149 fn emit_serde_json_null(&mut self, key: Key) -> slog::Result {
150 self.emit_serde_json_value(key, serde_json::Value::Null)
151 }
152
153 #[inline]
155 fn emit_serde_json_bool(&mut self, key: Key, val: bool) -> slog::Result {
156 self.emit_serde_json_value(key, serde_json::Value::Bool(val))
157 }
158
159 fn emit_serde_json_number<V>(&mut self, key: Key, value: V) -> slog::Result
161 where
162 serde_json::Number: From<V>,
163 {
164 let num = serde_json::Number::from(value);
166
167 self.emit_serde_json_value(key, serde_json::Value::Number(num))
168 }
169
170 fn emit_serde_json_string<T>(&mut self, key: Key, val: T) -> slog::Result
172 where
173 T: ToString,
174 {
175 self.emit_serde_json_value(key, serde_json::Value::String(val.to_string()))
176 }
177}
178
179impl<'a, F> slog::Serializer for JsonMessageBuilder<'a, F>
180where
181 F: KVFilter,
182{
183 #[inline]
184 fn emit_bool(&mut self, key: Key, val: bool) -> slog::Result {
185 self.emit_serde_json_bool(key, val)
186 }
187
188 #[inline]
189 fn emit_unit(&mut self, key: Key) -> slog::Result {
190 self.emit_serde_json_null(key)
191 }
192
193 #[inline]
194 fn emit_char(&mut self, key: Key, val: char) -> slog::Result {
195 self.emit_serde_json_string(key, val)
196 }
197
198 #[inline]
199 fn emit_none(&mut self, key: Key) -> slog::Result {
200 self.emit_serde_json_null(key)
201 }
202
203 #[inline]
204 fn emit_u8(&mut self, key: Key, val: u8) -> slog::Result {
205 self.emit_serde_json_number(key, val)
206 }
207
208 #[inline]
209 fn emit_i8(&mut self, key: Key, val: i8) -> slog::Result {
210 self.emit_serde_json_number(key, val)
211 }
212
213 #[inline]
214 fn emit_u16(&mut self, key: Key, val: u16) -> slog::Result {
215 self.emit_serde_json_number(key, val)
216 }
217
218 #[inline]
219 fn emit_i16(&mut self, key: Key, val: i16) -> slog::Result {
220 self.emit_serde_json_number(key, val)
221 }
222
223 #[inline]
224 fn emit_usize(&mut self, key: Key, val: usize) -> slog::Result {
225 self.emit_serde_json_number(key, val)
226 }
227
228 #[inline]
229 fn emit_isize(&mut self, key: Key, val: isize) -> slog::Result {
230 self.emit_serde_json_number(key, val)
231 }
232
233 #[inline]
234 fn emit_u32(&mut self, key: Key, val: u32) -> slog::Result {
235 self.emit_serde_json_number(key, val)
236 }
237
238 #[inline]
239 fn emit_i32(&mut self, key: Key, val: i32) -> slog::Result {
240 self.emit_serde_json_number(key, val)
241 }
242
243 #[inline]
244 fn emit_f32(&mut self, key: Key, val: f32) -> slog::Result {
245 self.emit_f64(key, f64::from(val))
246 }
247
248 #[inline]
249 fn emit_u64(&mut self, key: Key, val: u64) -> slog::Result {
250 self.emit_serde_json_number(key, val)
251 }
252
253 #[inline]
254 fn emit_i64(&mut self, key: Key, val: i64) -> slog::Result {
255 self.emit_serde_json_number(key, val)
256 }
257
258 fn emit_f64(&mut self, key: Key, val: f64) -> slog::Result {
259 if let Some(num) = serde_json::Number::from_f64(val) {
260 self.emit_serde_json_value(key, serde_json::Value::Number(num))
261 } else {
262 self.emit_serde_json_null(key)
263 }
264 }
265
266 #[inline]
267 fn emit_str(&mut self, key: Key, val: &str) -> slog::Result {
268 self.emit_serde_json_string(key, val)
269 }
270
271 #[inline]
272 fn emit_arguments(&mut self, key: Key, val: &fmt::Arguments) -> slog::Result {
273 self.emit_serde_json_string(key, val)
274 }
275}
276
277#[cfg(all(test, any(feature = "loggly", feature = "better-stack")))]
278pub mod test_utils {
279 use std::{
280 panic::{RefUnwindSafe, UnwindSafe},
281 sync::{Arc, Mutex},
282 };
283
284 use bytes::Bytes;
285 use slog::{Drain, Logger, OwnedKVList, Record, o};
286
287 use super::LogMessageSerializer;
288
289 struct CollectingDrain<S> {
291 serializer: S,
292 messages: Arc<Mutex<Vec<serde_json::Value>>>,
293 }
294
295 impl<S> Drain for CollectingDrain<S>
296 where
297 S: LogMessageSerializer<Serialized = Bytes>,
298 {
299 type Ok = ();
300 type Err = slog::Never;
301
302 fn log(&self, record: &Record, values: &OwnedKVList) -> Result<(), slog::Never> {
303 let msg = self.serializer.serialize(record, values).unwrap();
304 let msg = serde_json::from_slice(&msg).unwrap();
305
306 self.messages.lock().unwrap().push(msg);
307
308 Ok(())
309 }
310 }
311
312 pub fn serialize_logs<S, F>(serializer: S, f: F) -> Vec<serde_json::Value>
315 where
316 S: LogMessageSerializer<Serialized = Bytes>
317 + Send
318 + Sync
319 + RefUnwindSafe
320 + UnwindSafe
321 + 'static,
322 F: FnOnce(&Logger),
323 {
324 let messages = Arc::new(Mutex::new(Vec::new()));
325
326 let drain = CollectingDrain {
327 serializer,
328 messages: messages.clone(),
329 };
330
331 f(&Logger::root(drain, o!()));
332
333 let mut messages = messages.lock().unwrap();
334
335 std::mem::take(&mut *messages)
336 }
337}
338
339#[cfg(test)]
340mod tests {
341 use bytes::Bytes;
342 use slog::{Key, Serializer};
343
344 use super::JsonMessageBuilder;
345
346 fn parse(msg: Bytes) -> serde_json::Value {
347 serde_json::from_slice(&msg).unwrap()
348 }
349
350 #[test]
351 fn serializes_value_types() {
352 let mut builder = JsonMessageBuilder::new();
353
354 builder.emit_bool("bool", true).unwrap();
355 builder.emit_unit("unit").unwrap();
356 builder.emit_none("none").unwrap();
357 builder.emit_char("char", 'c').unwrap();
358 builder.emit_i64("i64", i64::MIN).unwrap();
359 builder.emit_u64("u64", u64::MAX).unwrap();
360 builder.emit_f32("f32", 0.5).unwrap();
361 builder.emit_f64("nan", f64::NAN).unwrap();
362 builder
363 .emit_arguments("args", &format_args!("{}-{}", 1, 2))
364 .unwrap();
365
366 let expected = serde_json::json!({
367 "bool": true,
368 "unit": null,
369 "none": null,
370 "char": "c",
371 "i64": i64::MIN,
372 "u64": u64::MAX,
373 "f32": 0.5,
374 "nan": null,
375 "args": "1-2",
376 });
377
378 assert_eq!(parse(builder.finish().unwrap()), expected);
379 }
380
381 #[test]
382 fn collects_rejected_keys_in_fallback_field() {
383 let filter = |key: &Key| key.starts_with('a');
384
385 let mut builder = JsonMessageBuilder::new().with_field_filter("misc", &filter);
386
387 builder.emit_u32("a", 1).unwrap();
388 builder.emit_str("b", "x").unwrap();
389 builder.emit_bool("c", true).unwrap();
390 builder.emit_str("misc", "user").unwrap();
392 builder.emit_str("b", "y").unwrap();
394
395 let expected = serde_json::json!({
396 "a": 1,
397 "misc": "b: x, c: true, misc: user",
398 });
399
400 assert_eq!(parse(builder.finish().unwrap()), expected);
401 }
402}