veecle_telemetry/span.rs
1//! Distributed tracing spans for tracking units of work.
2//!
3//! This module provides the core span implementation for distributed tracing.
4//! Spans represent units of work within a trace and can be nested to show
5//! relationships between different operations.
6//!
7//! # Key Concepts
8//!
9//! - **Span**: A unit of work within a trace, with a name and optional attributes
10//! - **Span Context**: The trace and span IDs that identify a span within a trace
11//! - **Span Guards**: RAII guards that automatically handle span entry/exit
12//! - **Current Span**: Thread-local tracking of the currently active span
13//!
14//! # Basic Usage
15//!
16//! ```rust
17//! use veecle_telemetry::{CurrentSpan, span};
18//!
19//! // Create and enter a span
20//! let span = span!("operation", user_id = 123);
21//! let _guard = span.entered();
22//!
23//! // Add events to the current span
24//! CurrentSpan::add_event("checkpoint", &[]);
25//!
26//! // Span is automatically exited when guard is dropped
27//! ```
28//!
29//! # Span Lifecycle
30//!
31//! 1. **Creation**: Spans are created with a name and optional attributes
32//! 2. **Entry**: Spans are entered to make them the current active span
33//! 3. **Events**: Events and attributes can be added to active spans
34//! 4. **Exit**: Spans are exited when no longer active
35//! 5. **Close**: Spans are closed when their work is complete
36//!
37//! # Nesting
38//!
39//! Spans can be nested to show relationships:
40//!
41//! ```rust
42//! use veecle_telemetry::span;
43//!
44//! let parent = span!("parent_operation");
45//! let _parent_guard = parent.entered();
46//!
47//! // This span will automatically be a child of the parent
48//! let child = span!("child_operation");
49//! let _child_guard = child.entered();
50//! ```
51
52#[cfg(feature = "enable")]
53use core::cell::Cell;
54use core::marker::PhantomData;
55#[cfg(all(feature = "std", feature = "enable"))]
56use std::thread_local;
57
58use crate::SpanContext;
59#[cfg(feature = "enable")]
60use crate::collector::get_collector;
61#[cfg(feature = "enable")]
62use crate::id::SpanId;
63#[cfg(feature = "enable")]
64use crate::protocol::{
65 SpanAddEventMessage, SpanAddLinkMessage, SpanCloseMessage, SpanCreateMessage, SpanEnterMessage,
66 SpanExitMessage, SpanSetAttributeMessage,
67};
68#[cfg(feature = "enable")]
69use crate::time::now;
70use crate::value::KeyValue;
71
72#[cfg(feature = "enable")]
73thread_local! {
74 pub(crate) static CURRENT_SPAN: Cell<Option<SpanId>> = const { Cell::new(None) };
75}
76
77/// A distributed tracing span representing a unit of work.
78///
79/// Spans are the fundamental building blocks of distributed tracing.
80/// They represent a unit of work within a trace and can be nested to show relationships between different operations.
81///
82/// # Examples
83///
84/// ```rust
85/// use veecle_telemetry::{KeyValue, Span, Value};
86///
87/// // Create a span with attributes
88/// let span = Span::new("database_query", &[
89/// KeyValue::new("table", Value::String("users".into())),
90/// KeyValue::new("operation", Value::String("SELECT".into())),
91/// ]);
92///
93/// // Enter the span to make it active
94/// let _guard = span.enter();
95///
96/// // Add events to the span
97/// span.add_event("query_executed", &[]);
98/// ```
99///
100/// # Conditional Compilation
101///
102/// When the `enable` feature is disabled, spans compile to no-ops with zero runtime overhead.
103#[must_use]
104#[derive(Default, Debug)]
105pub struct Span {
106 #[cfg(feature = "enable")]
107 pub(crate) span_id: Option<SpanId>,
108}
109
110/// Utilities for working with the currently active span.
111///
112/// This struct provides static methods for interacting with the current span
113/// in the thread-local context.
114/// It allows adding events, links, and attributes to the currently active span without needing a direct reference to
115/// it.
116///
117/// # Examples
118///
119/// ```rust
120/// use veecle_telemetry::{CurrentSpan, span};
121///
122/// let span = span!("operation");
123/// let _guard = span.entered();
124///
125/// // Add an event to the current span
126/// CurrentSpan::add_event("milestone", &[]);
127/// ```
128#[derive(Default, Debug)]
129pub struct CurrentSpan;
130
131impl Span {
132 /// Creates a no-op span that performs no tracing operations.
133 ///
134 /// This is useful for creating spans that may be conditionally enabled
135 /// or when telemetry is completely disabled.
136 #[inline]
137 pub fn noop() -> Self {
138 Self {
139 #[cfg(feature = "enable")]
140 span_id: None,
141 }
142 }
143
144 /// Creates a new span as a child of the current span.
145 ///
146 /// If there is no current span, this returns a new root span.
147 ///
148 /// # Arguments
149 ///
150 /// * `name` - The name of the span
151 /// * `attributes` - Key-value attributes to attach to the span
152 ///
153 /// # Examples
154 ///
155 /// ```rust
156 /// use veecle_telemetry::{KeyValue, Span, Value};
157 ///
158 /// let span = Span::new("operation", &[KeyValue::new("user_id", Value::I64(123))]);
159 /// ```
160 pub fn new(name: &'static str, attributes: &'_ [KeyValue<'static>]) -> Self {
161 #[cfg(not(feature = "enable"))]
162 {
163 let _ = (name, attributes);
164 Self::noop()
165 }
166
167 #[cfg(feature = "enable")]
168 {
169 Self::new_inner(name, attributes)
170 }
171 }
172
173 /// Creates a [`SpanContext`] from this [`Span`].
174 /// For a noop span, this function will return `None`.
175 ///
176 /// # Examples
177 ///
178 /// ```
179 /// use veecle_telemetry::Span;
180 ///
181 /// let span = Span::new("root_span", &[]);
182 /// assert!(span.context().is_some());
183 /// ```
184 pub fn context(&self) -> Option<SpanContext> {
185 #[cfg(not(feature = "enable"))]
186 {
187 None
188 }
189
190 #[cfg(feature = "enable")]
191 {
192 self.span_id
193 .map(|span_id| SpanContext::new(get_collector().process_id(), span_id))
194 }
195 }
196
197 /// Enters this span, making it the current active span.
198 ///
199 /// This method returns a guard that will automatically exit the span when dropped.
200 /// The guard borrows the span, so the span must remain alive while the guard exists.
201 ///
202 /// # Examples
203 ///
204 /// ```rust
205 /// use veecle_telemetry::Span;
206 ///
207 /// let span = Span::new("operation", &[]);
208 /// let _guard = span.enter();
209 /// // span is now active
210 /// // span is automatically exited when _guard is dropped
211 /// ```
212 pub fn enter(&'_ self) -> SpanGuardRef<'_> {
213 #[cfg(not(feature = "enable"))]
214 {
215 SpanGuardRef::noop()
216 }
217
218 #[cfg(feature = "enable")]
219 {
220 let Some(span_id) = self.span_id else {
221 return SpanGuardRef::noop();
222 };
223
224 self.do_enter();
225 CURRENT_SPAN
226 .try_with(|current| {
227 let parent = current.get();
228 current.set(Some(span_id));
229
230 SpanGuardRef::new(self, parent)
231 })
232 .unwrap_or(SpanGuardRef::noop())
233 }
234 }
235
236 /// Enters this span by taking ownership of it.
237 ///
238 /// This method consumes the span and returns a guard that owns the span.
239 /// The span will be automatically exited and closed when the guard is dropped.
240 ///
241 /// # Examples
242 ///
243 /// ```rust
244 /// use veecle_telemetry::Span;
245 ///
246 /// let span = Span::new("operation", &[]);
247 /// let _guard = span.entered();
248 /// // span is now active and owned by the guard
249 /// // span is automatically exited and closed when _guard is dropped
250 /// ```
251 pub fn entered(self) -> SpanGuard {
252 #[cfg(not(feature = "enable"))]
253 {
254 SpanGuard::noop()
255 }
256
257 #[cfg(feature = "enable")]
258 {
259 let Some(span_id) = self.span_id else {
260 return SpanGuard::noop();
261 };
262
263 self.do_enter();
264 CURRENT_SPAN
265 .try_with(|current| {
266 let parent = current.get();
267 current.set(Some(span_id));
268
269 SpanGuard::new(self, parent)
270 })
271 .unwrap_or(SpanGuard::noop())
272 }
273 }
274
275 /// Adds an event to this span.
276 ///
277 /// Events represent point-in-time occurrences within a span's lifetime.
278 /// They can include additional attributes for context.
279 ///
280 /// # Arguments
281 ///
282 /// * `name` - The name of the event
283 /// * `attributes` - Key-value attributes providing additional context
284 ///
285 /// # Examples
286 ///
287 /// ```rust
288 /// use veecle_telemetry::{KeyValue, Span, Value};
289 ///
290 /// let span = Span::new("database_query", &[]);
291 /// span.add_event("query_started", &[]);
292 /// span.add_event("query_completed", &[KeyValue::new("rows_returned", Value::I64(42))]);
293 /// ```
294 pub fn add_event(&self, name: &'static str, attributes: &'_ [KeyValue<'static>]) {
295 #[cfg(not(feature = "enable"))]
296 {
297 let _ = (name, attributes);
298 }
299
300 #[cfg(feature = "enable")]
301 {
302 if let Some(span_id) = self.span_id {
303 get_collector().span_event(SpanAddEventMessage {
304 span_id,
305 name: name.into(),
306 time_unix_nano: now().as_nanos(),
307 attributes: attributes.into(),
308 });
309 }
310 }
311 }
312
313 /// Creates a link from this span to another span.
314 ///
315 /// Links connect spans across different traces, allowing you to represent
316 /// relationships between spans that are not parent-child relationships.
317 ///
318 /// # Examples
319 ///
320 /// ```
321 /// use veecle_telemetry::{Span, SpanContext, SpanId, ProcessId};
322 ///
323 /// let span = Span::new("my_span", &[]);
324 /// let external_context = SpanContext::new(ProcessId::from_raw(0x123), SpanId(0x456));
325 /// span.add_link(external_context);
326 /// ```
327 pub fn add_link(&self, link: SpanContext) {
328 #[cfg(not(feature = "enable"))]
329 {
330 let _ = link;
331 }
332
333 #[cfg(feature = "enable")]
334 {
335 if let Some(span_id) = self.span_id {
336 get_collector().span_link(SpanAddLinkMessage { span_id, link });
337 }
338 }
339 }
340
341 /// Adds an attribute to this span.
342 ///
343 /// Attributes provide additional context about the work being performed
344 /// in the span. They can be set at any time during the span's lifetime.
345 ///
346 /// # Arguments
347 ///
348 /// * `attribute` - The key-value attribute to set
349 ///
350 /// # Examples
351 ///
352 /// ```rust
353 /// use veecle_telemetry::{KeyValue, Span, Value};
354 ///
355 /// let span = Span::new("user_operation", &[]);
356 /// span.set_attribute(KeyValue::new("user_id", Value::I64(123)));
357 /// span.set_attribute(KeyValue::new("operation_type", Value::String("update".into())));
358 /// ```
359 pub fn set_attribute(&self, attribute: KeyValue<'static>) {
360 #[cfg(not(feature = "enable"))]
361 {
362 let _ = attribute;
363 }
364
365 #[cfg(feature = "enable")]
366 {
367 if let Some(span_id) = self.span_id {
368 get_collector().span_attribute(SpanSetAttributeMessage { span_id, attribute });
369 }
370 }
371 }
372}
373
374impl CurrentSpan {
375 /// Adds an event to the current span.
376 ///
377 /// Events represent point-in-time occurrences within a span's lifetime.
378 ///
379 /// # Arguments
380 ///
381 /// * `name` - The name of the event
382 /// * `attributes` - Key-value attributes providing additional context
383 ///
384 /// # Examples
385 ///
386 /// ```rust
387 /// use veecle_telemetry::{CurrentSpan, KeyValue, Value, span};
388 ///
389 /// let _guard = span!("operation").entered();
390 /// CurrentSpan::add_event("checkpoint", &[]);
391 /// CurrentSpan::add_event("milestone", &[KeyValue::new("progress", 75)]);
392 /// ```
393 ///
394 /// Does nothing if there's no active span.
395 pub fn add_event(name: &'static str, attributes: &'_ [KeyValue<'static>]) {
396 #[cfg(not(feature = "enable"))]
397 {
398 let _ = (name, attributes);
399 }
400
401 #[cfg(feature = "enable")]
402 {
403 if let Some(context) = SpanContext::current() {
404 get_collector().span_event(SpanAddEventMessage {
405 span_id: context.span_id,
406 name: name.into(),
407 time_unix_nano: now().as_nanos(),
408 attributes: attributes.into(),
409 });
410 }
411 }
412 }
413
414 /// Creates a link from the current span to another span.
415 /// Does nothing if there's no active span.
416 ///
417 /// Links connect spans across different traces, allowing you to represent
418 /// relationships between spans that are not parent-child relationships.
419 ///
420 /// # Examples
421 ///
422 /// ```
423 /// use veecle_telemetry::{CurrentSpan, Span, SpanContext, SpanId, ProcessId};
424 ///
425 /// let _guard = Span::new("my_span", &[]).entered();
426 ///
427 /// let external_context = SpanContext::new(ProcessId::from_raw(0x123), SpanId(0x456));
428 /// CurrentSpan::add_link(external_context);
429 /// ```
430 pub fn add_link(link: SpanContext) {
431 #[cfg(not(feature = "enable"))]
432 {
433 let _ = link;
434 }
435
436 #[cfg(feature = "enable")]
437 {
438 if let Some(context) = SpanContext::current() {
439 get_collector().span_link(SpanAddLinkMessage {
440 span_id: context.span_id,
441 link,
442 });
443 }
444 }
445 }
446
447 /// Sets an attribute on the current span.
448 ///
449 /// Attributes provide additional context about the work being performed
450 /// in the span.
451 ///
452 /// # Arguments
453 ///
454 /// * `attribute` - The key-value attribute to set
455 ///
456 /// # Examples
457 ///
458 /// ```rust
459 /// use veecle_telemetry::{CurrentSpan, KeyValue, Value, span};
460 ///
461 /// let _guard = span!("operation").entered();
462 /// CurrentSpan::set_attribute(KeyValue::new("user_id", 123));
463 /// CurrentSpan::set_attribute(KeyValue::new("status", "success"));
464 /// ```
465 ///
466 /// Does nothing if there's no active span.
467 pub fn set_attribute(attribute: KeyValue<'static>) {
468 #[cfg(not(feature = "enable"))]
469 {
470 let _ = attribute;
471 }
472
473 #[cfg(feature = "enable")]
474 {
475 if let Some(context) = SpanContext::current() {
476 get_collector().span_attribute(SpanSetAttributeMessage {
477 span_id: context.span_id,
478 attribute,
479 });
480 }
481 }
482 }
483}
484
485#[cfg(feature = "enable")]
486impl Span {
487 fn new_inner(name: &'static str, attributes: &'_ [KeyValue<'static>]) -> Self {
488 let span_id = SpanId::next_id();
489 let parent_span_id = CURRENT_SPAN.get();
490
491 get_collector().new_span(SpanCreateMessage {
492 span_id,
493 parent_span_id,
494 name: name.into(),
495 start_time_unix_nano: now().as_nanos(),
496 attributes: attributes.into(),
497 });
498
499 Self {
500 span_id: Some(span_id),
501 }
502 }
503
504 fn do_enter(&self) {
505 #[cfg(feature = "enable")]
506 if let Some(span_id) = self.span_id {
507 let timestamp = now();
508 get_collector().enter_span(SpanEnterMessage {
509 span_id,
510 time_unix_nano: timestamp.0,
511 });
512 }
513 }
514
515 fn do_exit(&self) {
516 #[cfg(feature = "enable")]
517 if let Some(span_id) = self.span_id {
518 let timestamp = now();
519 get_collector().exit_span(SpanExitMessage {
520 span_id,
521 time_unix_nano: timestamp.0,
522 });
523 }
524 }
525}
526
527impl Drop for Span {
528 fn drop(&mut self) {
529 #[cfg(feature = "enable")]
530 if let Some(span_id) = self.span_id.take() {
531 let timestamp = now();
532 get_collector().close_span(SpanCloseMessage {
533 span_id,
534 end_time_unix_nano: timestamp.0,
535 });
536 }
537 }
538}
539
540/// Exits and drops the span when this is dropped.
541#[derive(Debug)]
542pub struct SpanGuard {
543 #[cfg(feature = "enable")]
544 pub(crate) inner: Option<SpanGuardInner>,
545
546 /// ```compile_fail
547 /// use veecle_telemetry::span::*;
548 /// trait AssertSend: Send {}
549 ///
550 /// impl AssertSend for SpanGuard {}
551 /// ```
552 _not_send: PhantomNotSend,
553}
554
555#[cfg(feature = "enable")]
556#[derive(Debug)]
557pub(crate) struct SpanGuardInner {
558 span: Span,
559 parent: Option<SpanId>,
560}
561
562impl SpanGuard {
563 pub(crate) fn noop() -> Self {
564 Self {
565 #[cfg(feature = "enable")]
566 inner: None,
567 _not_send: PhantomNotSend,
568 }
569 }
570
571 #[cfg(feature = "enable")]
572 pub(crate) fn new(span: Span, parent: Option<SpanId>) -> Self {
573 Self {
574 #[cfg(feature = "enable")]
575 inner: Some(SpanGuardInner { span, parent }),
576 _not_send: PhantomNotSend,
577 }
578 }
579}
580
581impl Drop for SpanGuard {
582 fn drop(&mut self) {
583 #[cfg(feature = "enable")]
584 if let Some(inner) = self.inner.take() {
585 let _ = CURRENT_SPAN.try_with(|current| current.replace(inner.parent));
586 inner.span.do_exit();
587 }
588 }
589}
590
591/// Exits the span when dropped.
592#[derive(Debug)]
593pub struct SpanGuardRef<'a> {
594 #[cfg(feature = "enable")]
595 pub(crate) inner: Option<SpanGuardRefInner<'a>>,
596
597 _phantom: PhantomData<&'a ()>,
598}
599
600#[cfg(feature = "enable")]
601#[derive(Debug)]
602pub(crate) struct SpanGuardRefInner<'a> {
603 span: &'a Span,
604 parent: Option<SpanId>,
605}
606
607impl<'a> SpanGuardRef<'a> {
608 pub(crate) fn noop() -> Self {
609 Self {
610 #[cfg(feature = "enable")]
611 inner: None,
612 _phantom: PhantomData,
613 }
614 }
615
616 #[cfg(feature = "enable")]
617 pub(crate) fn new(span: &'a Span, parent: Option<SpanId>) -> Self {
618 Self {
619 #[cfg(feature = "enable")]
620 inner: Some(SpanGuardRefInner { span, parent }),
621 _phantom: PhantomData,
622 }
623 }
624}
625
626impl Drop for SpanGuardRef<'_> {
627 fn drop(&mut self) {
628 #[cfg(feature = "enable")]
629 if let Some(inner) = self.inner.take() {
630 let _ = CURRENT_SPAN.try_with(|current| current.replace(inner.parent));
631 inner.span.do_exit();
632 }
633 }
634}
635
636/// Technically, `SpanGuard` _can_ implement both `Send` *and*
637/// `Sync` safely. It doesn't, because it has a `PhantomNotSend` field,
638/// specifically added in order to make it `!Send`.
639///
640/// Sending an `SpanGuard` guard between threads cannot cause memory unsafety.
641/// However, it *would* result in incorrect behavior, so we add a
642/// `PhantomNotSend` to prevent it from being sent between threads. This is
643/// because it must be *dropped* on the same thread that it was created;
644/// otherwise, the span will never be exited on the thread where it was entered,
645/// and it will attempt to exit the span on a thread that may never have entered
646/// it. However, we still want them to be `Sync` so that a struct holding an
647/// `Entered` guard can be `Sync`.
648///
649/// Thus, this is totally safe.
650#[derive(Debug)]
651struct PhantomNotSend {
652 ghost: PhantomData<*mut ()>,
653}
654
655#[allow(non_upper_case_globals)]
656const PhantomNotSend: PhantomNotSend = PhantomNotSend { ghost: PhantomData };
657
658/// # Safety:
659///
660/// Trivially safe, as `PhantomNotSend` doesn't have any API.
661unsafe impl Sync for PhantomNotSend {}
662
663#[cfg(all(test, feature = "std"))]
664mod tests {
665 use super::*;
666 use crate::{ProcessId, SpanContext, SpanId};
667
668 #[test]
669 fn span_noop() {
670 let span = Span::noop();
671 assert!(span.span_id.is_none());
672 }
673
674 #[test]
675 fn span_new_without_parent() {
676 CURRENT_SPAN.set(None);
677
678 let span = Span::new("test_span", &[]);
679 assert!(span.span_id.is_some());
680 }
681
682 #[test]
683 fn span_new_with_parent() {
684 let parent_span_id = SpanId::next_id();
685 CURRENT_SPAN.set(Some(parent_span_id));
686
687 let span = Span::new("child_span", &[]);
688 let span_id = span.span_id.unwrap();
689 assert_ne!(span_id, parent_span_id);
690
691 CURRENT_SPAN.set(None);
692 }
693
694 #[test]
695 fn span_root() {
696 let span = Span::new("root_span", &[]);
697 let span_id = span.span_id.unwrap();
698 assert_ne!(span_id, SpanId(0));
699 }
700
701 #[test]
702 fn span_context_from_span() {
703 let span = Span::new("test_span", &[]);
704
705 let extracted_context = span.context();
706 let context = extracted_context.unwrap();
707 assert_eq!(context.process_id, get_collector().process_id());
708 }
709
710 #[test]
711 fn span_context_from_noop_span() {
712 let span = Span::noop();
713 let extracted_context = span.context();
714 assert!(extracted_context.is_none());
715 }
716
717 #[test]
718 fn span_enter_and_current_context() {
719 CURRENT_SPAN.set(None);
720
721 assert!(SpanContext::current().is_none());
722
723 let span = Span::new("test_span", &[]);
724
725 {
726 let _guard = span.enter();
727 assert_eq!(SpanContext::current().unwrap(), span.context().unwrap());
728 }
729
730 // After guard is dropped, should be back to no current context
731 assert!(SpanContext::current().is_none());
732 }
733
734 #[test]
735 fn span_entered_guard() {
736 CURRENT_SPAN.set(None);
737
738 let span = Span::new("test_span", &[]);
739
740 {
741 let _guard = span.entered();
742 // Should have current context while guard exists
743 let current_context = SpanContext::current();
744 assert!(current_context.is_some());
745 }
746
747 // Should be cleared after guard is dropped
748 assert!(SpanContext::current().is_none());
749 }
750
751 #[test]
752 fn noop_span_operations() {
753 let noop_span = Span::noop();
754
755 {
756 let _guard = noop_span.enter();
757 assert!(SpanContext::current().is_none());
758 }
759
760 let _entered_guard = noop_span.entered();
761 assert!(SpanContext::current().is_none());
762 }
763
764 #[test]
765 fn nested_spans() {
766 CURRENT_SPAN.set(None);
767
768 let root_span = Span::new("test_span", &[]);
769 let root_context = root_span.context().unwrap();
770 let _root_guard = root_span.entered();
771
772 let child_span = Span::new("child", &[]);
773 assert_ne!(child_span.context().unwrap().span_id, root_context.span_id);
774 }
775
776 #[test]
777 fn span_event() {
778 let span = Span::new("test_span", &[]);
779
780 let event_attributes = [KeyValue::new("event_key", "event_value")];
781
782 span.add_event("test_event", &event_attributes);
783
784 let noop_span = Span::noop();
785 noop_span.add_event("noop_event", &event_attributes);
786 }
787
788 #[test]
789 fn span_link() {
790 let span = Span::new("test_span", &[]);
791
792 let link_context = SpanContext::new(ProcessId::from_raw(0), SpanId(0));
793 span.add_link(link_context);
794
795 let noop_span = Span::noop();
796 noop_span.add_link(link_context);
797 }
798
799 #[test]
800 fn span_attribute() {
801 let span = Span::new("test_span", &[]);
802
803 let attribute = KeyValue::new("test_key", "test_value");
804 span.set_attribute(attribute.clone());
805
806 let noop_span = Span::noop();
807 noop_span.set_attribute(attribute);
808 }
809
810 #[test]
811 fn span_methods_with_entered_span() {
812 let span = Span::new("test_span", &[]);
813
814 let _guard = span.enter();
815
816 // All these should work while span is entered
817 span.add_event("entered_event", &[]);
818 span.add_link(SpanContext::new(ProcessId::from_raw(0), SpanId(0)));
819 span.set_attribute(KeyValue::new("entered_key", true));
820 }
821
822 #[test]
823 fn current_span_event_with_active_span() {
824 CURRENT_SPAN.set(None);
825
826 let _root_guard = Span::new("test_span", &[]).entered();
827
828 let event_attributes = [KeyValue::new("current_event_key", "current_event_value")];
829 CurrentSpan::add_event("current_test_event", &event_attributes);
830 }
831
832 #[test]
833 fn current_span_link_with_active_span() {
834 CURRENT_SPAN.set(None);
835
836 let _root_guard = Span::new("test_span", &[]).entered();
837
838 let link_context = SpanContext::new(ProcessId::from_raw(0), SpanId(0));
839 CurrentSpan::add_link(link_context);
840 }
841
842 #[test]
843 fn current_span_attribute_with_active_span() {
844 CURRENT_SPAN.set(None);
845
846 let span = Span::new("test_span", &[]);
847
848 let _guard = span.enter();
849 let attribute = KeyValue::new("current_attr_key", "current_attr_value");
850 CurrentSpan::set_attribute(attribute);
851 }
852}