tracing_tunnel/
sender.rs

1//! Client-side subscriber.
2
3use core::sync::atomic::{AtomicU32, Ordering};
4
5use tracing_core::{
6    span::{Attributes, Id, Record},
7    Event, Interest, Metadata, Subscriber,
8};
9
10use crate::{CallSiteData, MetadataId, RawSpanId, TracedValues, TracingEvent};
11
12impl TracingEvent {
13    fn new_span(span: &Attributes<'_>, metadata_id: MetadataId, id: RawSpanId) -> Self {
14        Self::NewSpan {
15            id,
16            parent_id: span.parent().map(Id::into_u64),
17            metadata_id,
18            values: TracedValues::from_values(span.values()),
19        }
20    }
21
22    fn values_recorded(id: RawSpanId, values: &Record<'_>) -> Self {
23        Self::ValuesRecorded {
24            id,
25            values: TracedValues::from_record(values),
26        }
27    }
28
29    fn new_event(event: &Event<'_>, metadata_id: MetadataId) -> Self {
30        Self::NewEvent {
31            metadata_id,
32            parent: event.parent().map(Id::into_u64),
33            values: TracedValues::from_event(event),
34        }
35    }
36}
37
38/// Tracing [`Subscriber`] that converts tracing events into (de)serializable [presentation]
39/// that can be sent elsewhere using a customizable hook.
40///
41/// As an example, this subscriber is used in the [Tardigrade client library] to send
42/// workflow traces to the host via a WASM import function.
43///
44/// # Examples
45///
46/// See [crate-level docs](index.html) for an example of usage.
47///
48/// [presentation]: TracingEvent
49/// [Tardigrade client library]: https://github.com/slowli/tardigrade
50#[derive(Debug)]
51pub struct TracingEventSender<F = fn(TracingEvent)> {
52    next_span_id: AtomicU32,
53    on_event: F,
54}
55
56impl<F: Fn(TracingEvent) + 'static> TracingEventSender<F> {
57    /// Creates a subscriber with the specified "on event" hook.
58    pub fn new(on_event: F) -> Self {
59        Self {
60            next_span_id: AtomicU32::new(1), // 0 is invalid span ID
61            on_event,
62        }
63    }
64
65    fn metadata_id(metadata: &'static Metadata<'static>) -> MetadataId {
66        metadata as *const _ as MetadataId
67    }
68
69    fn send(&self, event: TracingEvent) {
70        (self.on_event)(event);
71    }
72}
73
74impl<F: Fn(TracingEvent) + 'static> Subscriber for TracingEventSender<F> {
75    fn register_callsite(&self, metadata: &'static Metadata<'static>) -> Interest {
76        let id = Self::metadata_id(metadata);
77        self.send(TracingEvent::NewCallSite {
78            id,
79            data: CallSiteData::from(metadata),
80        });
81        Interest::always()
82    }
83
84    fn enabled(&self, _metadata: &Metadata<'_>) -> bool {
85        true
86    }
87
88    fn new_span(&self, span: &Attributes<'_>) -> Id {
89        let metadata_id = Self::metadata_id(span.metadata());
90        let span_id = u64::from(self.next_span_id.fetch_add(1, Ordering::SeqCst));
91        self.send(TracingEvent::new_span(span, metadata_id, span_id));
92        Id::from_u64(span_id)
93    }
94
95    fn record(&self, span: &Id, values: &Record<'_>) {
96        self.send(TracingEvent::values_recorded(span.into_u64(), values));
97    }
98
99    fn record_follows_from(&self, span: &Id, follows: &Id) {
100        self.send(TracingEvent::FollowsFrom {
101            id: span.into_u64(),
102            follows_from: follows.into_u64(),
103        });
104    }
105
106    fn event(&self, event: &Event<'_>) {
107        let metadata_id = Self::metadata_id(event.metadata());
108        self.send(TracingEvent::new_event(event, metadata_id));
109    }
110
111    fn enter(&self, span: &Id) {
112        self.send(TracingEvent::SpanEntered {
113            id: span.into_u64(),
114        });
115    }
116
117    fn exit(&self, span: &Id) {
118        self.send(TracingEvent::SpanExited {
119            id: span.into_u64(),
120        });
121    }
122
123    fn clone_span(&self, span: &Id) -> Id {
124        self.send(TracingEvent::SpanCloned {
125            id: span.into_u64(),
126        });
127        span.clone()
128    }
129
130    fn try_close(&self, span: Id) -> bool {
131        self.send(TracingEvent::SpanDropped {
132            id: span.into_u64(),
133        });
134        false
135    }
136}