1use std::borrow::Cow;
11use std::ptr::NonNull;
12use std::sync::Arc;
13
14use super::collector::SpanCollector;
15use super::context::{SpanContext, SpanEnterGuard};
16use super::ids::{SpanId, TraceId};
17
18#[derive(Clone, Copy)]
32pub(crate) struct CollectorRef(NonNull<SpanCollector>);
33
34unsafe impl Send for CollectorRef {}
38unsafe impl Sync for CollectorRef {}
39
40impl CollectorRef {
41 pub(crate) fn from_arc(arc: &Arc<SpanCollector>) -> Self {
43 Self(NonNull::from(arc.as_ref()))
44 }
45
46 #[inline]
53 pub(crate) fn as_ref(&self) -> &SpanCollector {
54 unsafe { self.0.as_ref() }
56 }
57}
58
59static CLOCK_ANCHOR: std::sync::LazyLock<(std::time::Instant, u64)> =
65 std::sync::LazyLock::new(|| {
66 let wall = std::time::SystemTime::now()
67 .duration_since(std::time::UNIX_EPOCH)
68 .unwrap_or_default()
69 .as_nanos() as u64;
70 (std::time::Instant::now(), wall)
71 });
72
73#[inline]
78pub(crate) fn now_nanos() -> u64 {
79 let (ref mono, wall) = *CLOCK_ANCHOR;
80 wall + mono.elapsed().as_nanos() as u64
81}
82
83#[derive(Clone, Copy, Debug, PartialEq, Eq)]
89pub enum SpanKind {
90 Internal,
92 Server,
94 Client,
96 Producer,
98 Consumer,
100}
101
102#[derive(Clone, Debug, Default, PartialEq, Eq)]
104pub enum SpanStatus {
105 #[default]
107 Unset,
108 Ok,
110 Error { message: Cow<'static, str> },
112}
113
114pub struct SpanAttribute {
120 pub key: Cow<'static, str>,
121 pub value: SpanValue,
122}
123
124impl SpanAttribute {
125 pub fn new(key: impl Into<Cow<'static, str>>, value: impl Into<SpanValue>) -> Self {
127 Self {
128 key: key.into(),
129 value: value.into(),
130 }
131 }
132}
133
134pub enum SpanValue {
136 String(Cow<'static, str>),
137 I64(i64),
138 F64(f64),
139 Bool(bool),
140 Uuid(uuid::Uuid),
142}
143
144impl From<&'static str> for SpanValue {
145 fn from(s: &'static str) -> Self {
146 Self::String(Cow::Borrowed(s))
147 }
148}
149
150impl From<String> for SpanValue {
151 fn from(s: String) -> Self {
152 Self::String(Cow::Owned(s))
153 }
154}
155
156impl From<Cow<'static, str>> for SpanValue {
157 fn from(s: Cow<'static, str>) -> Self {
158 Self::String(s)
159 }
160}
161
162impl From<i64> for SpanValue {
163 fn from(v: i64) -> Self {
164 Self::I64(v)
165 }
166}
167
168impl From<f64> for SpanValue {
169 fn from(v: f64) -> Self {
170 Self::F64(v)
171 }
172}
173
174impl From<bool> for SpanValue {
175 fn from(v: bool) -> Self {
176 Self::Bool(v)
177 }
178}
179
180impl From<uuid::Uuid> for SpanValue {
181 fn from(v: uuid::Uuid) -> Self {
182 Self::Uuid(v)
183 }
184}
185
186pub struct SpanEvent {
188 pub name: Cow<'static, str>,
189 pub time_ns: u64,
190 pub attributes: Vec<SpanAttribute>,
191}
192
193pub struct CompletedSpan {
199 pub trace_id: TraceId,
200 pub span_id: SpanId,
201 pub parent_span_id: SpanId,
202 pub name: Cow<'static, str>,
203 pub kind: SpanKind,
204 pub start_time_ns: u64,
205 pub end_time_ns: u64,
206 pub status: SpanStatus,
207 pub attributes: Vec<SpanAttribute>,
208 pub events: Vec<SpanEvent>,
209}
210
211#[must_use = "span is submitted to collector on drop — bind it to a variable"]
225pub struct Span {
226 data: Option<CompletedSpan>,
227 collector: CollectorRef,
228 _enter_guard: Option<SpanEnterGuard>,
229}
230
231impl Span {
232 #[inline]
236 pub(crate) fn noop(collector: CollectorRef) -> Self {
237 Self {
238 data: None,
239 collector,
240 _enter_guard: None,
241 }
242 }
243
244 #[inline]
246 pub(crate) fn new_root(
247 name: impl Into<Cow<'static, str>>,
248 kind: SpanKind,
249 collector: CollectorRef,
250 ) -> Self {
251 let data = CompletedSpan {
252 trace_id: TraceId::random(),
253 span_id: SpanId::random(),
254 parent_span_id: SpanId::INVALID,
255 name: name.into(),
256 kind,
257 start_time_ns: now_nanos(),
258 end_time_ns: 0,
259 status: SpanStatus::Unset,
260 attributes: Vec::new(),
261 events: Vec::new(),
262 };
263 Self {
264 data: Some(data),
265 collector,
266 _enter_guard: None,
267 }
268 }
269
270 pub(crate) fn new_from_remote(
272 name: impl Into<Cow<'static, str>>,
273 kind: SpanKind,
274 parent_ctx: SpanContext,
275 collector: CollectorRef,
276 ) -> Self {
277 let data = CompletedSpan {
278 trace_id: parent_ctx.trace_id,
279 span_id: SpanId::random(),
280 parent_span_id: parent_ctx.span_id,
281 name: name.into(),
282 kind,
283 start_time_ns: now_nanos(),
284 end_time_ns: 0,
285 status: SpanStatus::Unset,
286 attributes: Vec::new(),
287 events: Vec::new(),
288 };
289 Self {
290 data: Some(data),
291 collector,
292 _enter_guard: None,
293 }
294 }
295
296 #[inline]
301 pub fn child(&self, name: impl Into<Cow<'static, str>>, kind: SpanKind) -> Span {
302 let Some(parent_data) = self.data.as_ref() else {
303 return Span::noop(self.collector);
304 };
305 let data = CompletedSpan {
306 trace_id: parent_data.trace_id,
307 span_id: SpanId::random(),
308 parent_span_id: parent_data.span_id,
309 name: name.into(),
310 kind,
311 start_time_ns: now_nanos(),
312 end_time_ns: 0,
313 status: SpanStatus::Unset,
314 attributes: Vec::new(),
315 events: Vec::new(),
316 };
317 Span {
318 data: Some(data),
319 collector: self.collector,
320 _enter_guard: None,
321 }
322 }
323
324 pub fn enter(&mut self) -> &mut Self {
329 if self._enter_guard.is_none()
330 && let Some(data) = self.data.as_ref()
331 {
332 self._enter_guard = Some(SpanEnterGuard::enter(SpanContext {
333 trace_id: data.trace_id,
334 span_id: data.span_id,
335 trace_flags: 0x01, }));
337 }
338 self
339 }
340
341 pub fn add_event(
345 &mut self,
346 name: impl Into<Cow<'static, str>>,
347 attributes: Vec<SpanAttribute>,
348 ) {
349 if let Some(data) = self.data.as_mut() {
350 data.events.push(SpanEvent {
351 name: name.into(),
352 time_ns: now_nanos(),
353 attributes,
354 });
355 }
356 }
357
358 pub fn add_simple_event(&mut self, name: impl Into<Cow<'static, str>>) {
360 if let Some(data) = self.data.as_mut() {
361 data.events.push(SpanEvent {
362 name: name.into(),
363 time_ns: now_nanos(),
364 attributes: Vec::new(),
365 });
366 }
367 }
368
369 #[inline]
371 pub fn set_attribute(
372 &mut self,
373 key: impl Into<Cow<'static, str>>,
374 value: impl Into<SpanValue>,
375 ) {
376 if let Some(data) = self.data.as_mut() {
377 data.attributes.push(SpanAttribute {
378 key: key.into(),
379 value: value.into(),
380 });
381 }
382 }
383
384 #[inline]
386 pub fn set_status(&mut self, status: SpanStatus) {
387 if let Some(data) = self.data.as_mut() {
388 data.status = status;
389 }
390 }
391
392 pub fn trace_id(&self) -> TraceId {
394 self.data.as_ref().map_or(TraceId::INVALID, |d| d.trace_id)
395 }
396
397 pub fn span_id(&self) -> SpanId {
399 self.data.as_ref().map_or(SpanId::INVALID, |d| d.span_id)
400 }
401
402 pub fn traceparent(&self) -> String {
406 let Some(data) = self.data.as_ref() else {
407 return String::new();
408 };
409 let ctx = SpanContext {
410 trace_id: data.trace_id,
411 span_id: data.span_id,
412 trace_flags: 0x01, };
414 ctx.to_traceparent()
415 }
416
417 pub fn end(self) {
421 drop(self);
423 }
424}
425
426impl Drop for Span {
427 fn drop(&mut self) {
428 if let Some(mut completed) = self.data.take() {
429 completed.end_time_ns = now_nanos();
430 self.collector.as_ref().submit(completed);
431 }
432 }
434}
435
436#[cfg(test)]
437mod tests {
438 use super::*;
439
440 fn test_collector() -> Arc<SpanCollector> {
441 Arc::new(SpanCollector::new(1, 64))
442 }
443
444 fn flush_and_drain(collector: &SpanCollector, buf: &mut Vec<CompletedSpan>) {
446 collector.flush_local();
447 collector.drain_into(buf);
448 }
449
450 #[test]
451 fn root_span_lifecycle() {
452 let collector = test_collector();
453 {
454 let mut span = Span::new_root(
455 "test_op",
456 SpanKind::Server,
457 CollectorRef::from_arc(&collector),
458 );
459 span.set_attribute("key", "value");
460 span.add_simple_event("checkpoint");
461 span.set_status(SpanStatus::Ok);
462 } let mut buf = Vec::new();
465 flush_and_drain(&collector, &mut buf);
466 assert_eq!(buf.len(), 1);
467
468 let completed = &buf[0];
469 assert!(!completed.trace_id.is_invalid());
470 assert!(!completed.span_id.is_invalid());
471 assert!(completed.parent_span_id.is_invalid()); assert_eq!(completed.name, "test_op");
473 assert_eq!(completed.kind, SpanKind::Server);
474 assert_eq!(completed.status, SpanStatus::Ok);
475 assert_eq!(completed.attributes.len(), 1);
476 assert_eq!(completed.events.len(), 1);
477 assert!(completed.end_time_ns >= completed.start_time_ns);
478 }
479
480 #[test]
481 fn child_inherits_trace_id() {
482 let collector = test_collector();
483 let root_trace_id;
484 let root_span_id;
485 {
486 let root = Span::new_root(
487 "parent",
488 SpanKind::Server,
489 CollectorRef::from_arc(&collector),
490 );
491 root_trace_id = root.trace_id();
492 root_span_id = root.span_id();
493 {
494 let _child = root.child("child_op", SpanKind::Client);
495 }
497 }
499
500 let mut buf = Vec::new();
501 flush_and_drain(&collector, &mut buf);
502 assert_eq!(buf.len(), 2);
503
504 let child = &buf[0];
506 let parent = &buf[1];
507
508 assert_eq!(child.trace_id, root_trace_id);
509 assert_eq!(child.parent_span_id, root_span_id);
510 assert_eq!(parent.trace_id, root_trace_id);
511 assert!(parent.parent_span_id.is_invalid());
512 }
513
514 #[test]
515 fn explicit_end() {
516 let collector = test_collector();
517 let span = Span::new_root(
518 "explicit",
519 SpanKind::Internal,
520 CollectorRef::from_arc(&collector),
521 );
522 span.end();
523
524 let mut buf = Vec::new();
525 flush_and_drain(&collector, &mut buf);
526 assert_eq!(buf.len(), 1);
527 }
528
529 #[test]
530 fn traceparent_encoding() {
531 let collector = test_collector();
532 let span = Span::new_root(
533 "tp_test",
534 SpanKind::Server,
535 CollectorRef::from_arc(&collector),
536 );
537 let tp = span.traceparent();
538
539 assert!(tp.starts_with("00-"));
541 assert!(tp.ends_with("-01"));
542 assert_eq!(tp.len(), 55);
543 }
544
545 #[test]
546 fn from_remote_parent() {
547 let collector = test_collector();
548 let remote_ctx = SpanContext::from_traceparent(
549 "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01",
550 )
551 .expect("valid traceparent");
552
553 let span = Span::new_from_remote(
554 "server_handler",
555 SpanKind::Server,
556 remote_ctx,
557 CollectorRef::from_arc(&collector),
558 );
559 assert_eq!(span.trace_id(), remote_ctx.trace_id);
560 drop(span);
561
562 let mut buf = Vec::new();
563 flush_and_drain(&collector, &mut buf);
564 let completed = &buf[0];
565 assert_eq!(completed.trace_id, remote_ctx.trace_id);
566 assert_eq!(completed.parent_span_id, remote_ctx.span_id);
567 }
568}