1#[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;
61use crate::id::SpanId;
62#[cfg(feature = "enable")]
63use crate::protocol::{
64 SpanAddEventMessage, SpanAddLinkMessage, SpanCloseMessage, SpanCreateMessage, SpanEnterMessage,
65 SpanExitMessage, SpanSetAttributeMessage,
66};
67#[cfg(feature = "enable")]
68use crate::time::now;
69use crate::value::KeyValue;
70
71#[cfg(feature = "enable")]
72thread_local! {
73 pub(crate) static CURRENT_SPAN: Cell<Option<SpanContext>> = const { Cell::new(None) };
74}
75
76#[must_use]
103#[derive(Default, Debug)]
104pub struct Span {
105 #[cfg(feature = "enable")]
106 pub(crate) inner: Option<SpanInner>,
107}
108
109#[cfg(feature = "enable")]
110#[derive(Debug)]
111pub(crate) struct SpanInner {
112 pub(crate) context: SpanContext,
113}
114
115#[derive(Default, Debug)]
134pub struct CurrentSpan;
135
136impl Span {
137 #[inline]
142 pub fn noop() -> Self {
143 Self {
144 #[cfg(feature = "enable")]
145 inner: None,
146 }
147 }
148
149 pub fn new(name: &'static str, attributes: &'_ [KeyValue<'static>]) -> Self {
167 #[cfg(not(feature = "enable"))]
168 {
169 let _ = (name, attributes);
170 Self::noop()
171 }
172
173 #[cfg(feature = "enable")]
174 {
175 if let Some(parent) = CURRENT_SPAN.get() {
176 Self::new_inner(name, parent, attributes)
177 } else {
178 Self::noop()
179 }
180 }
181 }
182
183 pub fn root(
197 name: &'static str,
198 mut span_context: SpanContext,
199 attributes: &'_ [KeyValue<'static>],
200 ) -> Self {
201 span_context.span_id = SpanId(0);
203
204 #[cfg(not(feature = "enable"))]
205 {
206 let _ = (name, attributes);
207 Self::noop()
208 }
209
210 #[cfg(feature = "enable")]
211 {
212 Self::new_inner(name, span_context, attributes)
213 }
214 }
215
216 pub fn enter(&'_ self) -> SpanGuardRef<'_> {
232 #[cfg(not(feature = "enable"))]
233 {
234 SpanGuardRef::noop()
235 }
236
237 #[cfg(feature = "enable")]
238 {
239 let Some(context) = self.inner.as_ref().map(|inner| inner.context) else {
240 return SpanGuardRef::noop();
241 };
242
243 self.do_enter();
244 CURRENT_SPAN
245 .try_with(|current| {
246 let parent = current.get();
247 current.set(Some(context));
248
249 SpanGuardRef::new(self, parent)
250 })
251 .unwrap_or(SpanGuardRef::noop())
252 }
253 }
254
255 pub fn entered(self) -> SpanGuard {
271 #[cfg(not(feature = "enable"))]
272 {
273 SpanGuard::noop()
274 }
275
276 #[cfg(feature = "enable")]
277 {
278 let Some(context) = self.inner.as_ref().map(|inner| inner.context) else {
279 return SpanGuard::noop();
280 };
281
282 self.do_enter();
283 CURRENT_SPAN
284 .try_with(|current| {
285 let parent = current.get();
286 current.set(Some(context));
287
288 SpanGuard::new(self, parent)
289 })
290 .unwrap_or(SpanGuard::noop())
291 }
292 }
293
294 pub fn add_event(&self, name: &'static str, attributes: &'_ [KeyValue<'static>]) {
314 #[cfg(not(feature = "enable"))]
315 {
316 let _ = (name, attributes);
317 }
318
319 #[cfg(feature = "enable")]
320 {
321 if let Some(inner) = &self.inner {
322 get_collector().span_event(SpanAddEventMessage {
323 trace_id: inner.context.trace_id,
324 span_id: inner.context.span_id,
325 name: name.into(),
326 time_unix_nano: now().as_nanos(),
327 attributes: attributes.into(),
328 });
329 }
330 }
331 }
332
333 pub fn add_link(&self, link: SpanContext) {
348 #[cfg(not(feature = "enable"))]
349 {
350 let _ = link;
351 }
352
353 #[cfg(feature = "enable")]
354 {
355 if let Some(inner) = self.inner.as_ref() {
356 get_collector().span_link(SpanAddLinkMessage {
357 trace_id: inner.context.trace_id,
358 span_id: inner.context.span_id,
359 link,
360 });
361 }
362 }
363 }
364
365 pub fn set_attribute(&self, attribute: KeyValue<'static>) {
384 #[cfg(not(feature = "enable"))]
385 {
386 let _ = attribute;
387 }
388
389 #[cfg(feature = "enable")]
390 {
391 if let Some(inner) = self.inner.as_ref() {
392 get_collector().span_attribute(SpanSetAttributeMessage {
393 trace_id: inner.context.trace_id,
394 span_id: inner.context.span_id,
395 attribute,
396 });
397 }
398 }
399 }
400}
401
402impl CurrentSpan {
403 pub fn add_event(name: &'static str, attributes: &'_ [KeyValue<'static>]) {
424 #[cfg(not(feature = "enable"))]
425 {
426 let _ = (name, attributes);
427 }
428
429 #[cfg(feature = "enable")]
430 {
431 if let Some(context) = SpanContext::current() {
432 get_collector().span_event(SpanAddEventMessage {
433 trace_id: context.trace_id,
434 span_id: context.span_id,
435 name: name.into(),
436 time_unix_nano: now().as_nanos(),
437 attributes: attributes.into(),
438 });
439 }
440 }
441 }
442
443 pub fn add_link(link: SpanContext) {
460 #[cfg(not(feature = "enable"))]
461 {
462 let _ = link;
463 }
464
465 #[cfg(feature = "enable")]
466 {
467 if let Some(context) = SpanContext::current() {
468 get_collector().span_link(SpanAddLinkMessage {
469 trace_id: context.trace_id,
470 span_id: context.span_id,
471 link,
472 });
473 }
474 }
475 }
476
477 pub fn set_attribute(attribute: KeyValue<'static>) {
498 #[cfg(not(feature = "enable"))]
499 {
500 let _ = attribute;
501 }
502
503 #[cfg(feature = "enable")]
504 {
505 if let Some(context) = SpanContext::current() {
506 get_collector().span_attribute(SpanSetAttributeMessage {
507 trace_id: context.trace_id,
508 span_id: context.span_id,
509 attribute,
510 });
511 }
512 }
513 }
514}
515
516#[cfg(feature = "enable")]
517impl Span {
518 fn new_inner(
519 name: &'static str,
520 parent: SpanContext,
521 attributes: &'_ [KeyValue<'static>],
522 ) -> Self {
523 let span_id = SpanId::next_id();
524 let context = SpanContext::new(parent.trace_id, span_id);
525
526 let parent_id = if parent.span_id == SpanId(0) {
527 None
528 } else {
529 Some(parent.span_id)
530 };
531
532 get_collector().new_span(SpanCreateMessage {
533 trace_id: context.trace_id,
534 span_id: context.span_id,
535 parent_span_id: parent_id,
536 name: name.into(),
537 start_time_unix_nano: now().as_nanos(),
538 attributes: attributes.into(),
539 });
540
541 Self {
542 inner: Some(SpanInner { context }),
543 }
544 }
545
546 fn do_enter(&self) {
547 #[cfg(feature = "enable")]
548 if let Some(inner) = self.inner.as_ref() {
549 let timestamp = now();
550 get_collector().enter_span(SpanEnterMessage {
551 trace_id: inner.context.trace_id,
552 span_id: inner.context.span_id,
553 time_unix_nano: timestamp.0,
554 });
555 }
556 }
557
558 fn do_exit(&self) {
559 #[cfg(feature = "enable")]
560 if let Some(inner) = self.inner.as_ref() {
561 let timestamp = now();
562 get_collector().exit_span(SpanExitMessage {
563 trace_id: inner.context.trace_id,
564 span_id: inner.context.span_id,
565 time_unix_nano: timestamp.0,
566 });
567 }
568 }
569}
570
571impl Drop for Span {
572 fn drop(&mut self) {
573 #[cfg(feature = "enable")]
574 if let Some(inner) = self.inner.take() {
575 let timestamp = now();
576 get_collector().close_span(SpanCloseMessage {
577 trace_id: inner.context.trace_id,
578 span_id: inner.context.span_id,
579 end_time_unix_nano: timestamp.0,
580 });
581 }
582 }
583}
584
585#[derive(Debug)]
587pub struct SpanGuard {
588 #[cfg(feature = "enable")]
589 pub(crate) inner: Option<SpanGuardInner>,
590
591 _not_send: PhantomNotSend,
598}
599
600#[cfg(feature = "enable")]
601#[derive(Debug)]
602pub(crate) struct SpanGuardInner {
603 span: Span,
604 parent: Option<SpanContext>,
605}
606
607impl SpanGuard {
608 pub(crate) fn noop() -> Self {
609 Self {
610 #[cfg(feature = "enable")]
611 inner: None,
612 _not_send: PhantomNotSend,
613 }
614 }
615
616 #[cfg(feature = "enable")]
617 pub(crate) fn new(span: Span, parent: Option<SpanContext>) -> Self {
618 Self {
619 #[cfg(feature = "enable")]
620 inner: Some(SpanGuardInner { span, parent }),
621 _not_send: PhantomNotSend,
622 }
623 }
624}
625
626impl Drop for SpanGuard {
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#[derive(Debug)]
638pub struct SpanGuardRef<'a> {
639 #[cfg(feature = "enable")]
640 pub(crate) inner: Option<SpanGuardRefInner<'a>>,
641
642 _phantom: PhantomData<&'a ()>,
643}
644
645#[cfg(feature = "enable")]
646#[derive(Debug)]
647pub(crate) struct SpanGuardRefInner<'a> {
648 span: &'a Span,
649 parent: Option<SpanContext>,
650}
651
652impl<'a> SpanGuardRef<'a> {
653 pub(crate) fn noop() -> Self {
654 Self {
655 #[cfg(feature = "enable")]
656 inner: None,
657 _phantom: PhantomData,
658 }
659 }
660
661 #[cfg(feature = "enable")]
662 pub(crate) fn new(span: &'a Span, parent: Option<SpanContext>) -> Self {
663 Self {
664 #[cfg(feature = "enable")]
665 inner: Some(SpanGuardRefInner { span, parent }),
666 _phantom: PhantomData,
667 }
668 }
669}
670
671impl Drop for SpanGuardRef<'_> {
672 fn drop(&mut self) {
673 #[cfg(feature = "enable")]
674 if let Some(inner) = self.inner.take() {
675 let _ = CURRENT_SPAN.try_with(|current| current.replace(inner.parent));
676 inner.span.do_exit();
677 }
678 }
679}
680
681#[derive(Debug)]
696struct PhantomNotSend {
697 ghost: PhantomData<*mut ()>,
698}
699
700#[allow(non_upper_case_globals)]
701const PhantomNotSend: PhantomNotSend = PhantomNotSend { ghost: PhantomData };
702
703unsafe impl Sync for PhantomNotSend {}
707
708#[cfg(all(test, feature = "std"))]
709mod tests {
710 use super::*;
711 use crate::{SpanContext, SpanId, TraceId};
712
713 #[test]
714 fn span_noop() {
715 let span = Span::noop();
716 assert!(span.inner.is_none());
717 }
718
719 #[test]
720 fn span_new_without_parent() {
721 CURRENT_SPAN.set(None);
722
723 let span = Span::new("test_span", &[]);
724 assert!(span.inner.is_none());
725 }
726
727 #[test]
728 fn span_new_with_parent() {
729 let parent_context = SpanContext::generate();
730 CURRENT_SPAN.set(Some(parent_context));
731
732 let span = Span::new("child_span", &[]);
733 let inner = span.inner.as_ref().unwrap();
734 assert_eq!(inner.context.trace_id, parent_context.trace_id);
735 assert_ne!(inner.context.span_id, parent_context.span_id);
736
737 CURRENT_SPAN.set(None);
738 }
739
740 #[test]
741 fn span_root() {
742 let root_context = SpanContext::generate();
743 assert_eq!(root_context.span_id, SpanId(0));
744
745 let span = Span::root("root_span", root_context, &[]);
746 let inner = span.inner.as_ref().unwrap();
747 assert_eq!(inner.context.trace_id, root_context.trace_id);
748 assert_ne!(inner.context.span_id, SpanId(0));
749 }
750
751 #[test]
752 fn span_context_from_span() {
753 let root_context = SpanContext::generate();
754 let span = Span::root("test_span", root_context, &[]);
755
756 let extracted_context = SpanContext::from_span(&span);
757 let context = extracted_context.unwrap();
758 assert_eq!(context.trace_id, root_context.trace_id);
759 }
760
761 #[test]
762 fn span_context_from_noop_span() {
763 let span = Span::noop();
764 let extracted_context = SpanContext::from_span(&span);
765 assert!(extracted_context.is_none());
766 }
767
768 #[test]
769 fn span_enter_and_current_context() {
770 CURRENT_SPAN.set(None);
771
772 assert!(SpanContext::current().is_none());
773
774 let root_context = SpanContext::generate();
775 let span = Span::root("test_span", root_context, &[]);
776
777 {
778 let _guard = span.enter();
779 let current_context = SpanContext::current();
780 let context = current_context.unwrap();
781 assert_eq!(context.trace_id, root_context.trace_id);
782 }
783
784 assert!(SpanContext::current().is_none());
786 }
787
788 #[test]
789 fn span_entered_guard() {
790 CURRENT_SPAN.set(None);
791
792 let root_context = SpanContext::generate();
793 let span = Span::root("test_span", root_context, &[]);
794
795 {
796 let _guard = span.entered();
797 let current_context = SpanContext::current();
799 assert!(current_context.is_some());
800 }
801
802 assert!(SpanContext::current().is_none());
804 }
805
806 #[test]
807 fn noop_span_operations() {
808 let noop_span = Span::noop();
809
810 {
811 let _guard = noop_span.enter();
812 assert!(SpanContext::current().is_none());
813 }
814
815 let _entered_guard = noop_span.entered();
816 assert!(SpanContext::current().is_none());
817 }
818
819 #[test]
820 fn nested_spans() {
821 CURRENT_SPAN.set(None);
822
823 let root_context = SpanContext::generate();
824 let _root_guard = Span::root("test_span", root_context, &[]).entered();
825
826 let child_span = Span::new("child", &[]);
827 let child_inner = child_span.inner.as_ref().unwrap();
828 assert_eq!(child_inner.context.trace_id, root_context.trace_id);
829 assert_ne!(child_inner.context.span_id, root_context.span_id);
830 }
831
832 #[test]
833 fn span_event() {
834 let context = SpanContext::generate();
835 let span = Span::root("test_span", context, &[]);
836
837 let event_attributes = [KeyValue::new("event_key", "event_value")];
838
839 span.add_event("test_event", &event_attributes);
840
841 let noop_span = Span::noop();
842 noop_span.add_event("noop_event", &event_attributes);
843 }
844
845 #[test]
846 fn span_link() {
847 let context = SpanContext::generate();
848 let span = Span::root("test_span", context, &[]);
849
850 let link_context = SpanContext::new(TraceId(0), SpanId(0));
851 span.add_link(link_context);
852
853 let noop_span = Span::noop();
854 noop_span.add_link(link_context);
855 }
856
857 #[test]
858 fn span_attribute() {
859 let context = SpanContext::generate();
860 let span = Span::root("test_span", context, &[]);
861
862 let attribute = KeyValue::new("test_key", "test_value");
863 span.set_attribute(attribute.clone());
864
865 let noop_span = Span::noop();
866 noop_span.set_attribute(attribute);
867 }
868
869 #[test]
870 fn span_methods_with_entered_span() {
871 let context = SpanContext::generate();
872 let span = Span::root("test_span", context, &[]);
873
874 let _guard = span.enter();
875
876 span.add_event("entered_event", &[]);
878 span.add_link(SpanContext::new(TraceId(0), SpanId(0)));
879 span.set_attribute(KeyValue::new("entered_key", true));
880 }
881
882 #[test]
883 fn current_span_event_with_active_span() {
884 CURRENT_SPAN.set(None);
885
886 let context = SpanContext::generate();
887 let _root_guard = Span::root("test_span", context, &[]).entered();
888
889 let event_attributes = [KeyValue::new("current_event_key", "current_event_value")];
890 CurrentSpan::add_event("current_test_event", &event_attributes);
891 }
892
893 #[test]
894 fn current_span_link_with_active_span() {
895 CURRENT_SPAN.set(None);
896
897 let context = SpanContext::generate();
898 let _root_guard = Span::root("test_span", context, &[]).entered();
899
900 let link_context = SpanContext::new(TraceId(0), SpanId(0));
901 CurrentSpan::add_link(link_context);
902 }
903
904 #[test]
905 fn current_span_attribute_with_active_span() {
906 CURRENT_SPAN.set(None);
907
908 let context = SpanContext::generate();
909 let span = Span::root("test_span", context, &[]);
910
911 let _guard = span.enter();
912 let attribute = KeyValue::new("current_attr_key", "current_attr_value");
913 CurrentSpan::set_attribute(attribute);
914 }
915}