Skip to main content

elfo_logger/capture_layer/
mod.rs

1use std::sync::Arc;
2
3use tracing::{span, Event, Subscriber};
4use tracing_subscriber::layer::{Context, Layer};
5
6use elfo_core::scope;
7use elfo_utils::time::SystemTime;
8
9use self::visitor::Visitor;
10use crate::{stats, PreparedEvent, Shared, SpanData, StringId};
11
12mod visitor;
13
14/// A tracing layer that captures spans and events, and sends them to the actor.
15pub struct CaptureLayer {
16    shared: Arc<Shared>,
17}
18
19impl CaptureLayer {
20    pub(crate) fn new(shared: Arc<Shared>) -> Self {
21        Self { shared }
22    }
23
24    fn prepare(
25        &self,
26        simplify_message: bool,
27        f: impl FnOnce(&mut Visitor<'_>),
28    ) -> Option<StringId> {
29        self.shared.pool.create_with(|payload| {
30            let mut visitor = Visitor::new(&self.shared, payload, simplify_message);
31            f(&mut visitor);
32        })
33    }
34}
35
36impl<S: Subscriber> Layer<S> for CaptureLayer {
37    fn on_new_span(&self, attrs: &span::Attributes<'_>, id: &span::Id, ctx: Context<'_, S>) {
38        let parent_id = if attrs.is_root() {
39            None
40        } else {
41            let current_span = ctx.current_span();
42            attrs.parent().or_else(|| current_span.id()).cloned()
43        };
44        let payload_id = ward!(self.prepare(false, |visitor| attrs.record(visitor)));
45        let span = SpanData::new(parent_id, payload_id);
46        self.shared.spans.insert(id.clone(), span);
47    }
48
49    fn on_record(&self, id: &span::Id, record: &span::Record<'_>, _: Context<'_, S>) {
50        let mut data = ward!(self.shared.spans.get_mut(id));
51        let old_payload_id = data.payload_id;
52        let old_payload = ward!(self.shared.pool.get(old_payload_id));
53
54        let payload_id = ward!(self.prepare(false, |visitor| {
55            visitor.push(&old_payload);
56            record.record(visitor);
57        }));
58
59        self.shared.pool.clear(old_payload_id);
60        data.payload_id = payload_id;
61    }
62
63    fn on_event(&self, event: &Event<'_>, ctx: Context<'_, S>) {
64        let current_span = ctx.current_span();
65        let level = *event.metadata().level();
66        let payload_id = ward!(self.prepare(true, |visitor| event.record(visitor)), {
67            stats::counter_per_level("elfo_lost_events_total", level);
68            return;
69        });
70
71        let data = scope::try_with(|scope| (scope.meta().clone(), scope.trace_id()));
72        let (object, trace_id) = match data {
73            Some((meta, trace_id)) => (Some(meta), Some(trace_id)),
74            None => (None, None),
75        };
76
77        let event = PreparedEvent {
78            timestamp: SystemTime::now(),
79            trace_id,
80            metadata: event.metadata(),
81            object,
82            span_id: event.parent().or_else(|| current_span.id()).cloned(),
83            payload_id,
84        };
85
86        let is_lost = self.shared.channel.try_send(event).is_err();
87        if is_lost {
88            self.shared.pool.clear(payload_id);
89            stats::counter_per_level("elfo_lost_events_total", level);
90        } else {
91            stats::counter_per_level("elfo_emitted_events_total", level);
92        }
93    }
94
95    fn on_close(&self, id: span::Id, _: Context<'_, S>) {
96        if let Some((_, data)) = self.shared.spans.remove(&id) {
97            self.shared.pool.clear(data.payload_id);
98        }
99    }
100}