tracing_assert_core/
lib.rs

1#![cfg_attr(test, allow(non_snake_case))]
2use std::{
3    collections::VecDeque,
4    sync::{Arc, Mutex},
5};
6use tracing_core::{event, span, subscriber};
7use tracing_subscriber::layer;
8use visitor::FieldValueVisitor;
9
10pub use visitor::FieldValueMap;
11
12pub use lazy_static;
13pub use tracing;
14pub use tracing_subscriber;
15
16pub mod debug_fmt_ext;
17#[cfg(test)]
18mod tests;
19pub mod visitor;
20
21#[derive(Default)]
22pub struct Layer<T> {
23    log: Arc<Mutex<VecDeque<T>>>,
24}
25
26impl<T> Layer<T> {
27    pub fn new() -> Self {
28        Self {
29            log: Arc::new(Mutex::new(VecDeque::new())),
30        }
31    }
32
33    pub fn capture(&self, entry: T) {
34        let mut log = self.log.lock().expect("log mutex shouldn't be poisoned");
35        log.push_back(entry);
36    }
37}
38
39impl Layer<Notification> {
40    pub fn drain_events(&self) -> Vec<Event> {
41        let mut log = self.log.lock().expect("log mutex shouldn't be poisoned");
42        let events: Vec<Event> = log
43            .drain(..)
44            .filter_map(|val| match val {
45                Notification::Event { event, ctx: _ } => Some(event),
46                _ => None,
47            })
48            .collect();
49        events
50    }
51}
52
53// potential future improvements
54// - add API to encourage composing `Layer` with `tracing_subscriber::filter::EnvFilter`
55// - add API to encourage composing `Layer` with `tracing_subscriber::filter::LevelFilter`
56// - feature flag for integration with insta
57// - feature flag for integration with proptest
58
59pub trait LayerTransform<S> {
60    fn from_new_span(
61        attrs: &span::Attributes<'_>,
62        id: &span::Id,
63        ctx: layer::Context<'_, S>,
64    ) -> Self;
65
66    fn from_on_record(
67        span: &span::Id,
68        values: &span::Record<'_>,
69        ctx: layer::Context<'_, S>,
70    ) -> Self;
71
72    fn from_on_follows_from(
73        span: &span::Id,
74        follows: &span::Id,
75        ctx: layer::Context<'_, S>,
76    ) -> Self;
77
78    fn from_on_event(event: &event::Event<'_>, ctx: layer::Context<'_, S>) -> Self;
79
80    fn from_on_enter(id: &span::Id, ctx: layer::Context<'_, S>) -> Self;
81
82    fn from_on_exit(id: &span::Id, ctx: layer::Context<'_, S>) -> Self;
83
84    fn from_on_close(id: span::Id, ctx: layer::Context<'_, S>) -> Self;
85
86    fn from_on_id_change(old: &span::Id, new: &span::Id, ctx: layer::Context<'_, S>) -> Self;
87}
88
89#[derive(Debug)]
90pub struct FieldSet {}
91
92impl From<&tracing_core::field::FieldSet> for FieldSet {
93    fn from(_fields: &tracing_core::field::FieldSet) -> Self {
94        // Note: Is there context here worth collecting?
95        // _fields contains all _keys_ passed to trace!() macro, but not the corresponding Value data.
96        Self {}
97    }
98}
99
100#[derive(Debug)]
101pub struct ValueSet {}
102
103impl From<&tracing_core::field::ValueSet<'_>> for ValueSet {
104    fn from(_values: &tracing_core::field::ValueSet<'_>) -> Self {
105        // NOTE: Is there context here worth collecting?
106        // Cannot call `_values.record()` here to see actual Values themselves
107        // because that method is pub(crate) in the tracing lib. Must access data through `Event::record()`.
108        Self {}
109    }
110}
111
112#[derive(Debug)]
113#[allow(dead_code)] // Adding this because for some reason clippy is not recognizing that these fields are read in the From impl below
114pub struct Metadata {
115    name: String,
116    target: String,
117    level: tracing_core::Level,
118    module_path: Option<String>,
119    file: Option<String>,
120    line: Option<u32>,
121    fields: FieldSet,
122}
123
124impl From<&'static tracing_core::Metadata<'static>> for Metadata {
125    fn from(metadata: &'static tracing_core::Metadata<'static>) -> Self {
126        Self {
127            name: metadata.name().to_string(),
128            target: metadata.target().to_string(),
129            level: *metadata.level(),
130            module_path: metadata.module_path().map(ToString::to_string),
131            file: metadata.file().map(ToString::to_string),
132            line: metadata.line(),
133            fields: metadata.fields().into(),
134        }
135    }
136}
137
138#[derive(Debug)]
139#[allow(dead_code)] // Adding this because for some reason clippy is not recognizing that these fields are read in the From impl below
140pub struct Attributes {
141    metadata: Metadata,
142    values: ValueSet,
143    parent: Option<span::Id>,
144}
145
146impl From<&span::Attributes<'_>> for Attributes {
147    fn from(attrs: &span::Attributes<'_>) -> Self {
148        Self {
149            metadata: attrs.metadata().into(),
150            values: attrs.values().into(),
151            parent: attrs.parent().map(Clone::clone),
152        }
153    }
154}
155
156#[derive(Debug)]
157pub struct Context {}
158
159impl<S> From<layer::Context<'_, S>> for Context {
160    fn from(_ctx: layer::Context<'_, S>) -> Self {
161        // TODO: gather more context
162        // dbg!(ctx);
163
164        Self {}
165    }
166}
167
168#[derive(Debug)]
169#[allow(dead_code)] // Adding this because for some reason clippy is not recognizing that these fields are read in the From impl below
170pub struct Event {
171    fields: FieldValueMap,
172    metadata: Metadata,
173    parent: Option<span::Id>,
174}
175
176impl Event {
177    pub fn fields(&self) -> &FieldValueMap {
178        &self.fields
179    }
180}
181
182impl From<&event::Event<'_>> for Event {
183    fn from(event: &event::Event<'_>) -> Self {
184        // Visitor is mutated to capture field-value pairs from Event
185        let mut visitor = FieldValueVisitor::default();
186        event.record(&mut visitor);
187
188        Self {
189            fields: visitor.take_data(),
190            metadata: event.metadata().into(),
191            parent: event.parent().map(Clone::clone),
192        }
193    }
194}
195
196#[derive(Debug)]
197pub struct RecordValues {}
198
199impl From<&span::Record<'_>> for RecordValues {
200    fn from(_record: &span::Record<'_>) -> Self {
201        // TODO: gather more context
202        Self {}
203    }
204}
205
206#[derive(Debug)]
207pub enum Notification {
208    NewSpan {
209        id: span::Id,
210        attributes: Attributes,
211        ctx: Context,
212    },
213    Record {
214        id: span::Id,
215        values: RecordValues,
216        ctx: Context,
217    },
218    Enter {
219        id: span::Id,
220        ctx: Context,
221    },
222    Exit {
223        id: span::Id,
224        ctx: Context,
225    },
226    Close {
227        id: span::Id,
228        ctx: Context,
229    },
230    FollowsFrom {
231        span: span::Id,
232        follows: span::Id,
233        ctx: Context,
234    },
235    Event {
236        event: Event,
237        ctx: Context,
238    },
239    IdChange {
240        old: span::Id,
241        new: span::Id,
242        ctx: Context,
243    },
244}
245
246impl<S> LayerTransform<S> for Notification {
247    fn from_new_span(
248        attrs: &span::Attributes<'_>,
249        id: &span::Id,
250        ctx: layer::Context<'_, S>,
251    ) -> Self {
252        Notification::NewSpan {
253            id: id.clone(),
254            attributes: attrs.into(),
255            ctx: ctx.into(),
256        }
257    }
258
259    fn from_on_record(
260        span: &span::Id,
261        values: &span::Record<'_>,
262        ctx: layer::Context<'_, S>,
263    ) -> Self {
264        Notification::Record {
265            id: span.clone(),
266            values: values.into(),
267            ctx: ctx.into(),
268        }
269    }
270
271    fn from_on_follows_from(
272        span: &span::Id,
273        follows: &span::Id,
274        ctx: layer::Context<'_, S>,
275    ) -> Self {
276        Notification::FollowsFrom {
277            span: span.clone(),
278            follows: follows.clone(),
279            ctx: ctx.into(),
280        }
281    }
282
283    fn from_on_event(event: &event::Event<'_>, ctx: layer::Context<'_, S>) -> Self {
284        Notification::Event {
285            event: event.into(),
286            ctx: ctx.into(),
287        }
288    }
289
290    fn from_on_enter(id: &span::Id, ctx: layer::Context<'_, S>) -> Self {
291        Notification::Enter {
292            id: id.clone(),
293            ctx: ctx.into(),
294        }
295    }
296
297    fn from_on_exit(id: &span::Id, ctx: layer::Context<'_, S>) -> Self {
298        Notification::Exit {
299            id: id.clone(),
300            ctx: ctx.into(),
301        }
302    }
303
304    fn from_on_close(id: span::Id, ctx: layer::Context<'_, S>) -> Self {
305        Notification::Close {
306            id,
307            ctx: ctx.into(),
308        }
309    }
310
311    fn from_on_id_change(old: &span::Id, new: &span::Id, ctx: layer::Context<'_, S>) -> Self {
312        Notification::IdChange {
313            old: old.clone(),
314            new: new.clone(),
315            ctx: ctx.into(),
316        }
317    }
318}
319
320impl<S, T> layer::Layer<S> for &'static Layer<T>
321where
322    S: subscriber::Subscriber,
323    T: LayerTransform<S>,
324{
325    // TODO: consider overriding default methods to retain/manipulate context from them too.
326
327    fn on_record(&self, span: &span::Id, values: &span::Record<'_>, ctx: layer::Context<'_, S>) {
328        self.capture(T::from_on_record(span, values, ctx))
329    }
330
331    fn on_follows_from(&self, span: &span::Id, follows: &span::Id, ctx: layer::Context<'_, S>) {
332        self.capture(T::from_on_follows_from(span, follows, ctx))
333    }
334
335    fn on_event(&self, event: &event::Event<'_>, ctx: layer::Context<'_, S>) {
336        self.capture(T::from_on_event(event, ctx))
337    }
338
339    fn on_enter(&self, id: &span::Id, ctx: layer::Context<'_, S>) {
340        self.capture(T::from_on_enter(id, ctx))
341    }
342
343    fn on_exit(&self, id: &span::Id, ctx: layer::Context<'_, S>) {
344        self.capture(T::from_on_exit(id, ctx))
345    }
346
347    fn on_close(&self, id: span::Id, ctx: layer::Context<'_, S>) {
348        self.capture(T::from_on_close(id, ctx))
349    }
350
351    fn on_id_change(&self, old: &span::Id, new: &span::Id, ctx: layer::Context<'_, S>) {
352        self.capture(T::from_on_id_change(old, new, ctx))
353    }
354}