Skip to main content

slog_cloud/
serializer.rs

1use std::{
2    fmt::{self, Write},
3    io,
4};
5
6use bytes::Bytes;
7use indexmap::IndexMap;
8use slog::{Key, OwnedKVList, Record};
9
10/// Log message serializer.
11pub trait LogMessageSerializer {
12    type Serialized;
13
14    /// Serialize a given log record.
15    fn serialize(
16        &self,
17        record: &Record,
18        logger_values: &OwnedKVList,
19    ) -> slog::Result<Self::Serialized>;
20}
21
22/// Key-value pair filter.
23pub trait KVFilter {
24    /// Check if a given key should be accepted.
25    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
38/// Accept all key-value pairs.
39pub struct AcceptAll;
40
41impl KVFilter for AcceptAll {
42    #[inline]
43    fn is_accepted(&self, _: &Key) -> bool {
44        true
45    }
46}
47
48/// JSON log message builder.
49pub 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    /// Create a new log message builder.
58    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    /// Set the field filter.
77    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    /// Finish the message.
91    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    /// Serialize filtered fields.
103    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    /// Emit a given serde_json::Value key-value pair.
137    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    /// Emit a null key-value pair.
148    #[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    /// Emit a boolean key-value pair.
154    #[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    /// Emit a numeric key-value pair.
160    fn emit_serde_json_number<V>(&mut self, key: Key, value: V) -> slog::Result
161    where
162        serde_json::Number: From<V>,
163    {
164        // convert a given number into serde_json::Number
165        let num = serde_json::Number::from(value);
166
167        self.emit_serde_json_value(key, serde_json::Value::Number(num))
168    }
169
170    /// Emit a string key-value pair.
171    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    /// Log collecting drain.
290    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    /// Serialize all records logged by a given function using a given
313    /// serializer and parse them back as JSON.
314    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        // a key colliding with the fallback field must not replace it
391        builder.emit_str("misc", "user").unwrap();
392        // the first value wins in the fallback field as well
393        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}