tracing_assert_macros/
lib.rs

1#![cfg_attr(test, allow(non_snake_case))]
2mod readme_doctests;
3
4/// This macro runs the given code block, collects any tracing Events emitted, and returns them as a
5/// `Vec<tracing_assert_core::Event>`.
6///
7/// Invoke it with
8/// ```rust
9/// use tracing;
10/// use tracing_assert_core::Event;
11/// use tracing_assert_macros::tracing_capture_events;
12///
13/// fn foo() {
14///     tracing::trace!("my event data!");
15/// }
16///
17/// let events: Vec<Event> = tracing_capture_events!({foo()});
18/// ```
19#[macro_export]
20macro_rules! tracing_capture_events {
21    ( $code_under_test:block ) => {{
22        let events = {
23            use tracing_assert_core::{
24                lazy_static::lazy_static,
25                tracing_subscriber::{self, layer::SubscriberExt},
26                Event, Layer, Notification,
27            };
28
29            lazy_static! {
30                static ref LAYER: Layer<Notification> = Layer::new();
31            }
32
33            let subscriber = tracing_subscriber::FmtSubscriber::builder()
34                .with_test_writer()
35                .with_max_level(tracing::Level::TRACE)
36                .pretty()
37                .finish()
38                .with(&*LAYER);
39
40            tracing::subscriber::with_default(subscriber, || $code_under_test);
41
42            let events: Vec<Event> = LAYER.drain_events();
43            events
44        };
45        events
46    }};
47}
48
49/// This macro runs the given code block, collects any tracing Events emitted, and returns the
50/// Field/Value pairs carried by each event.
51///
52/// This utility macro is useful for callers only caring about the custom Field/Value data, and not necessarily the
53/// complete `Event` object.
54///
55/// Use it with
56/// ```rust
57/// use tracing;
58/// use tracing_assert_core::FieldValueMap;
59/// use tracing_assert_macros::tracing_capture_event_fields;
60///
61/// fn foo() {
62///     tracing::trace!("my event data!");
63/// }
64///
65/// let events: Vec<FieldValueMap> = tracing_capture_event_fields!({foo()});
66/// ```
67#[macro_export]
68macro_rules! tracing_capture_event_fields {
69    ( $code_under_test:block ) => {{
70        let events = {
71            use tracing_assert_core::FieldValueMap;
72
73            let events = $crate::tracing_capture_events!({ $code_under_test });
74            let events: Vec<FieldValueMap> =
75                events.into_iter().map(|e| e.fields().clone()).collect();
76            events
77        };
78        events
79    }};
80}
81
82#[cfg(test)]
83mod tests {
84    use super::*;
85    use tracing_assert_core::{debug_fmt_ext::DebugFmtExt, FieldValueMap};
86
87    #[derive(Debug)]
88    pub enum MyDomainLogEvent {
89        Foo,
90        Bar,
91    }
92
93    #[test]
94    fn tracing_capture_event_fields__captures_multiple_custom_structs_per_event() {
95        // Given
96        fn emit_2_complex_payload_events() {
97            tracing::info_span!("outermost span").in_scope(|| {
98                tracing::trace!(message1 = ?MyDomainLogEvent::Foo, message2 = ?MyDomainLogEvent::Bar);
99                tracing::trace!(arbitrary_key = ?MyDomainLogEvent::Bar);
100            });
101        }
102
103        // When
104        let events = tracing_capture_event_fields!({
105            emit_2_complex_payload_events();
106        });
107
108        // Then
109        let expected_events: Vec<Vec<(String, String)>> = vec![
110            vec![
111                ("message1".into(), MyDomainLogEvent::Foo.debug_fmt()),
112                ("message2".into(), MyDomainLogEvent::Bar.debug_fmt()),
113            ],
114            vec![("arbitrary_key".into(), MyDomainLogEvent::Bar.debug_fmt())],
115        ];
116
117        assert_eq!(events, expected_events);
118    }
119
120    #[test]
121    fn tracing_capture_event_fields__captures_debug_repr_of_custom_event_struct() {
122        // Given
123        fn emit_2_complex_payload_events() {
124            // Only emit one field per event. Field should be typed Domain event
125            // Always use debug representation of Event type with the `?` prefix
126            tracing::trace!(message = ?MyDomainLogEvent::Foo);
127            // Use consistent key (e.g. "message")
128            tracing::trace!(message = ?MyDomainLogEvent::Bar);
129        }
130
131        // When
132        let events = tracing_capture_event_fields!({
133            emit_2_complex_payload_events();
134        });
135
136        // Then
137        let expected_events: Vec<Vec<(String, String)>> = vec![
138            vec![("message".into(), MyDomainLogEvent::Foo.debug_fmt())],
139            vec![("message".into(), MyDomainLogEvent::Bar.debug_fmt())],
140        ];
141
142        assert_eq!(events, expected_events);
143    }
144
145    #[test]
146    fn tracing_capture_events__can_collect_fields_values_off_of_returned_event() {
147        // Given
148        fn emit_2_complex_payload_events() {
149            tracing::info_span!("outermost span").in_scope(|| {
150                tracing::trace!(message1 = ?MyDomainLogEvent::Foo, message2 = ?MyDomainLogEvent::Bar);
151                tracing::trace!(arbitrary_key = ?MyDomainLogEvent::Bar);
152            });
153        }
154
155        // When
156        let events = tracing_capture_events!({
157            emit_2_complex_payload_events();
158        });
159
160        // Then
161        let expected_events: Vec<Vec<(String, String)>> = vec![
162            vec![
163                ("message1".into(), MyDomainLogEvent::Foo.debug_fmt()),
164                ("message2".into(), MyDomainLogEvent::Bar.debug_fmt()),
165            ],
166            vec![("arbitrary_key".into(), MyDomainLogEvent::Bar.debug_fmt())],
167        ];
168
169        let events: Vec<FieldValueMap> = events.into_iter().map(|e| e.fields().clone()).collect();
170
171        // Assert list of events come back with expected fields
172        assert_eq!(events, expected_events);
173    }
174}