tracing_actions/
action_span.rs

1use std::{collections::HashMap, time::SystemTime};
2
3use tracing::{field::Visit, span::Attributes, Metadata};
4
5pub trait Resettable {
6    fn reset(&mut self);
7}
8
9#[derive(Debug, Clone, Copy)]
10pub enum TraceKind {
11    Client,
12    Server,
13}
14impl Default for TraceKind {
15    fn default() -> Self {
16        Self::Server
17    }
18}
19
20#[derive(Debug, Clone, Copy)]
21pub enum SpanStatus {
22    Ok,
23    Error,
24}
25impl Default for SpanStatus {
26    fn default() -> Self {
27        Self::Ok
28    }
29}
30
31#[derive(Debug, Clone)]
32pub struct ActionSpan {
33    pub ref_count: usize,
34
35    /// A unique identifier for a trace. All spans from the same trace share
36    /// the same `trace_id`. The ID is a 16-byte array. An ID with all zeroes is considered invalid.
37    pub trace_id: [u8; 16],
38
39    /// A unique identifier for a span within a trace, assigned when the span
40    /// is created. The ID is an 8-byte array. An ID with all zeroes is considered invalid.
41    pub span_id: [u8; 8],
42
43    /// trace_state conveys information about request position in multiple distributed tracing graphs.
44    /// It is a trace_state in w3c-trace-context format: <https://www.w3.org/TR/trace-context/#tracestate-header>
45    /// See also <https://github.com/w3c/distributed-tracing> for more details about this field.
46    pub trace_state: String,
47
48    /// The `span_id` of this span's parent span. If this is a root span, then this
49    /// field must be empty.
50    pub parent_span_id: Option<[u8; 8]>,
51
52    /// A description of the span, with its name inside.
53    pub metadata: Option<&'static Metadata<'static>>,
54
55    /// Distinguishes between spans generated in a particular context. For example,
56    /// two spans with the same name may be distinguished using `CLIENT` (caller)
57    /// and `SERVER` (callee) to identify queueing latency associated with the span.
58    pub kind: TraceKind,
59
60    /// start_time_unix_nano is the start time of the span. On the client side, this is the time
61    /// kept by the local machine where the span execution starts. On the server side, this
62    /// is the time when the server's application handler starts running.
63    /// Value is UNIX Epoch time in nanoseconds since 00:00:00 UTC on 1 January 1970.
64    ///
65    /// This field is semantically required and it is expected that end_time >= start_time.
66    pub start: SystemTime,
67
68    /// end_time_unix_nano is the end time of the span. On the client side, this is the time
69    /// kept by the local machine where the span execution ends. On the server side, this
70    /// is the time when the server application handler stops running.
71    /// Value is UNIX Epoch time in nanoseconds since 00:00:00 UTC on 1 January 1970.
72    ///
73    /// This field is semantically required and it is expected that end_time >= start_time.
74    pub end: SystemTime,
75
76    /// attributes is a collection of key/value pairs.
77    ///
78    /// The OpenTelemetry API specification further restricts the allowed value types:
79    /// <https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/common/README.md#attribute>
80    /// Attribute keys MUST be unique (it is not allowed to have more than one
81    /// attribute with the same key).
82    pub attributes: HashMap<&'static str, AttributeValue>,
83
84    /// events is a collection of Event items.
85    pub events: Vec<ActionEvent>,
86
87    pub status: SpanStatus,
88}
89
90impl Default for ActionSpan {
91    fn default() -> Self {
92        Self {
93            ref_count: 0,
94            trace_id: Default::default(),
95            span_id: Default::default(),
96            trace_state: Default::default(),
97            parent_span_id: Default::default(),
98            metadata: Default::default(),
99            kind: Default::default(),
100            start: SystemTime::now(),
101            end: SystemTime::now(),
102            attributes: Default::default(),
103            events: Default::default(),
104            status: Default::default(),
105        }
106    }
107}
108
109impl Resettable for ActionSpan {
110    fn reset(&mut self) {
111        self.ref_count = 0;
112        self.trace_id.fill(0);
113        self.span_id.fill(0);
114        self.trace_state = Default::default();
115        self.parent_span_id = None;
116        self.metadata = Default::default();
117        self.kind = Default::default();
118        self.attributes.clear();
119        self.events.clear();
120        self.status = Default::default();
121    }
122}
123
124impl ActionSpan {
125    pub fn start_root(&mut self, attributes: &Attributes) {
126        self.trace_id = rand::random();
127        self.span_id = rand::random();
128
129        self.start = SystemTime::now();
130
131        self.attach_attributes(attributes);
132    }
133
134    pub fn start_child(
135        &mut self,
136        attributes: &Attributes,
137        trace_id: &[u8; 16],
138        parent_span_id: &[u8; 8],
139    ) {
140        self.trace_id.copy_from_slice(trace_id);
141        self.span_id = rand::random();
142        self.parent_span_id = Some(*parent_span_id); // We can use the Copy trait here
143
144        self.start = SystemTime::now();
145
146        self.attach_attributes(attributes);
147    }
148
149    pub fn end(&mut self) {
150        self.end = SystemTime::now();
151    }
152
153    fn attach_attributes(&mut self, attributes: &Attributes) {
154        let metadata = attributes.metadata();
155        self.metadata = Some(metadata);
156        attributes.values().record(self)
157    }
158}
159
160#[derive(Debug, Clone)]
161pub struct ActionEvent {
162    pub metadata: &'static Metadata<'static>,
163    pub attributes: HashMap<&'static str, AttributeValue>,
164    pub timestamp: SystemTime,
165}
166
167impl<'a> From<&'a tracing::Event<'a>> for ActionEvent {
168    fn from(event: &'a tracing::Event<'a>) -> Self {
169        let mut selff = Self {
170            metadata: event.metadata(),
171            attributes: HashMap::new(),
172            timestamp: SystemTime::now(),
173        };
174        event.record(&mut selff);
175        selff
176    }
177}
178
179#[derive(Debug, Clone)]
180pub enum AttributeValue {
181    String(String),
182    F64(f64),
183    I64(i64),
184    U64(u64),
185    I128(i128),
186    U128(u128),
187    Bool(bool),
188    Error(String),
189}
190
191impl Visit for ActionSpan {
192    fn record_debug(&mut self, field: &tracing::field::Field, value: &dyn std::fmt::Debug) {
193        self.attributes
194            .insert(field.name(), AttributeValue::String(format!("{value:?}")));
195    }
196
197    fn record_f64(&mut self, field: &tracing::field::Field, value: f64) {
198        self.attributes
199            .insert(field.name(), AttributeValue::F64(value));
200    }
201
202    fn record_i64(&mut self, field: &tracing::field::Field, value: i64) {
203        self.attributes
204            .insert(field.name(), AttributeValue::I64(value));
205    }
206
207    fn record_u64(&mut self, field: &tracing::field::Field, value: u64) {
208        self.attributes
209            .insert(field.name(), AttributeValue::U64(value));
210    }
211
212    fn record_i128(&mut self, field: &tracing::field::Field, value: i128) {
213        self.attributes
214            .insert(field.name(), AttributeValue::I128(value));
215    }
216
217    fn record_u128(&mut self, field: &tracing::field::Field, value: u128) {
218        self.attributes
219            .insert(field.name(), AttributeValue::U128(value));
220    }
221
222    fn record_bool(&mut self, field: &tracing::field::Field, value: bool) {
223        self.attributes
224            .insert(field.name(), AttributeValue::Bool(value));
225    }
226
227    fn record_str(&mut self, field: &tracing::field::Field, value: &str) {
228        self.attributes
229            .insert(field.name(), AttributeValue::String(value.to_owned()));
230    }
231
232    fn record_error(
233        &mut self,
234        field: &tracing::field::Field,
235        value: &(dyn std::error::Error + 'static),
236    ) {
237        // This defaults to ok. If you want to make a span error, you just record at least 1 error on the span.
238        self.status = SpanStatus::Error;
239        self.attributes
240            .insert(field.name(), AttributeValue::Error(format!("{value:?}")));
241    }
242}
243
244impl Visit for ActionEvent {
245    fn record_debug(&mut self, field: &tracing::field::Field, value: &dyn std::fmt::Debug) {
246        self.attributes
247            .insert(field.name(), AttributeValue::String(format!("{value:?}")));
248    }
249
250    fn record_f64(&mut self, field: &tracing::field::Field, value: f64) {
251        self.attributes
252            .insert(field.name(), AttributeValue::F64(value));
253    }
254
255    fn record_i64(&mut self, field: &tracing::field::Field, value: i64) {
256        self.attributes
257            .insert(field.name(), AttributeValue::I64(value));
258    }
259
260    fn record_u64(&mut self, field: &tracing::field::Field, value: u64) {
261        self.attributes
262            .insert(field.name(), AttributeValue::U64(value));
263    }
264
265    fn record_i128(&mut self, field: &tracing::field::Field, value: i128) {
266        self.attributes
267            .insert(field.name(), AttributeValue::I128(value));
268    }
269
270    fn record_u128(&mut self, field: &tracing::field::Field, value: u128) {
271        self.attributes
272            .insert(field.name(), AttributeValue::U128(value));
273    }
274
275    fn record_bool(&mut self, field: &tracing::field::Field, value: bool) {
276        self.attributes
277            .insert(field.name(), AttributeValue::Bool(value));
278    }
279
280    fn record_str(&mut self, field: &tracing::field::Field, value: &str) {
281        self.attributes
282            .insert(field.name(), AttributeValue::String(value.to_owned()));
283    }
284
285    fn record_error(
286        &mut self,
287        field: &tracing::field::Field,
288        value: &(dyn std::error::Error + 'static),
289    ) {
290        self.attributes
291            .insert(field.name(), AttributeValue::Error(format!("{value:?}")));
292    }
293}