1use crate::stack::IdValueStack;
2use crate::{OtelData, OtelDataState};
3pub use filtered::FilteredOpenTelemetryLayer;
4use opentelemetry::ContextGuard;
5use opentelemetry::{
6 trace::{self as otel, noop, Span, SpanBuilder, SpanKind, Status, TraceContextExt},
7 Context as OtelContext, Key, KeyValue, StringValue, Value,
8};
9use std::cell::RefCell;
10use std::thread;
11#[cfg(not(all(target_arch = "wasm32", not(target_os = "wasi"))))]
12use std::time::Instant;
13use std::{any::TypeId, borrow::Cow};
14use std::{fmt, vec};
15use std::{marker, mem::take};
16use tracing_core::span::{self, Attributes, Id, Record};
17use tracing_core::{field, Event, Subscriber};
18#[cfg(feature = "tracing-log")]
19use tracing_log::NormalizeEvent;
20use tracing_subscriber::layer::Context;
21use tracing_subscriber::layer::Filter;
22use tracing_subscriber::registry::{ExtensionsMut, LookupSpan};
23use tracing_subscriber::Layer;
24#[cfg(all(target_arch = "wasm32", not(target_os = "wasi")))]
25use web_time::Instant;
26
27mod filtered;
28
29const SPAN_NAME_FIELD: &str = "otel.name";
30const SPAN_KIND_FIELD: &str = "otel.kind";
31const SPAN_STATUS_CODE_FIELD: &str = "otel.status_code";
32const SPAN_STATUS_DESCRIPTION_FIELD: &str = "otel.status_description";
33const SPAN_EVENT_COUNT_FIELD: &str = "otel.tracing_event_count";
34
35const EVENT_EXCEPTION_NAME: &str = "exception";
36const FIELD_EXCEPTION_MESSAGE: &str = "exception.message";
37const FIELD_EXCEPTION_STACKTRACE: &str = "exception.stacktrace";
38
39pub struct OpenTelemetryLayer<S, T> {
45 tracer: T,
46 location: bool,
47 tracked_inactivity: bool,
48 with_threads: bool,
49 with_level: bool,
50 with_target: bool,
51 context_activation: bool,
52 sem_conv_config: SemConvConfig,
53 with_context: WithContext,
54 _registry: marker::PhantomData<S>,
55}
56
57impl<S> Default for OpenTelemetryLayer<S, noop::NoopTracer>
58where
59 S: Subscriber + for<'span> LookupSpan<'span>,
60{
61 fn default() -> Self {
62 OpenTelemetryLayer::new(noop::NoopTracer::new())
63 }
64}
65
66pub fn layer<S>() -> OpenTelemetryLayer<S, noop::NoopTracer>
91where
92 S: Subscriber + for<'span> LookupSpan<'span>,
93{
94 OpenTelemetryLayer::default()
95}
96
97pub(crate) struct WithContext {
108 #[allow(clippy::type_complexity)]
112 pub(crate) with_context: fn(&tracing::Dispatch, &span::Id, f: &mut dyn FnMut(&mut OtelData)),
113
114 #[allow(clippy::type_complexity)]
119 pub(crate) with_activated_context:
120 fn(&tracing::Dispatch, &span::Id, f: &mut dyn FnMut(&mut OtelData)),
121
122 #[allow(clippy::type_complexity)]
127 pub(crate) with_activated_otel_context:
128 fn(&tracing::Dispatch, &mut ExtensionsMut<'_>, f: &mut dyn FnMut(&OtelContext)),
129}
130
131impl WithContext {
132 pub(crate) fn with_context(
136 &self,
137 dispatch: &tracing::Dispatch,
138 id: &span::Id,
139 mut f: impl FnMut(&mut OtelData),
140 ) {
141 (self.with_context)(dispatch, id, &mut f)
142 }
143
144 pub(crate) fn with_activated_context(
151 &self,
152 dispatch: &tracing::Dispatch,
153 id: &span::Id,
154 mut f: impl FnMut(&mut OtelData),
155 ) {
156 (self.with_activated_context)(dispatch, id, &mut f)
157 }
158
159 #[allow(clippy::type_complexity)]
164 pub(crate) fn with_activated_otel_context(
165 &self,
166 dispatch: &tracing::Dispatch,
167 extensions: &mut ExtensionsMut<'_>,
168 mut f: impl FnMut(&OtelContext),
169 ) {
170 (self.with_activated_otel_context)(dispatch, extensions, &mut f)
171 }
172}
173
174fn str_to_span_kind(s: &str) -> Option<otel::SpanKind> {
175 match s {
176 s if s.eq_ignore_ascii_case("server") => Some(otel::SpanKind::Server),
177 s if s.eq_ignore_ascii_case("client") => Some(otel::SpanKind::Client),
178 s if s.eq_ignore_ascii_case("producer") => Some(otel::SpanKind::Producer),
179 s if s.eq_ignore_ascii_case("consumer") => Some(otel::SpanKind::Consumer),
180 s if s.eq_ignore_ascii_case("internal") => Some(otel::SpanKind::Internal),
181 _ => None,
182 }
183}
184
185fn str_to_status(s: &str) -> otel::Status {
186 match s {
187 s if s.eq_ignore_ascii_case("ok") => otel::Status::Ok,
188 s if s.eq_ignore_ascii_case("error") => otel::Status::error(""),
189 _ => otel::Status::Unset,
190 }
191}
192
193#[derive(Default)]
194struct SpanBuilderUpdates {
195 name: Option<Cow<'static, str>>,
196 span_kind: Option<SpanKind>,
197 status: Option<Status>,
198 attributes: Option<Vec<KeyValue>>,
199}
200
201impl SpanBuilderUpdates {
202 fn update(self, span_builder: &mut SpanBuilder, s: &mut Status) {
203 let Self {
204 name,
205 span_kind,
206 status,
207 attributes,
208 } = self;
209
210 if let Some(name) = name {
211 span_builder.name = name;
212 }
213 if let Some(span_kind) = span_kind {
214 span_builder.span_kind = Some(span_kind);
215 }
216 if let Some(status) = status {
217 *s = status;
218 }
219 if let Some(attributes) = attributes {
220 if let Some(builder_attributes) = &mut span_builder.attributes {
221 builder_attributes.extend(attributes);
222 } else {
223 span_builder.attributes = Some(attributes);
224 }
225 }
226 }
227
228 fn update_span(self, span: &opentelemetry::trace::SpanRef<'_>) {
229 let Self {
230 status, attributes, ..
231 } = self;
232
233 if let Some(status) = status {
234 span.set_status(status);
235 }
236 if let Some(attributes) = attributes {
237 span.set_attributes(attributes);
238 }
239 }
240}
241
242struct SpanEventVisitor<'a, 'b> {
243 event_builder: &'a mut otel::Event,
244 span_builder_updates: &'b mut Option<SpanBuilderUpdates>,
245 sem_conv_config: SemConvConfig,
246}
247
248impl field::Visit for SpanEventVisitor<'_, '_> {
249 fn record_bool(&mut self, field: &field::Field, value: bool) {
253 match field.name() {
254 "message" => self.event_builder.name = value.to_string().into(),
255 #[cfg(feature = "tracing-log")]
257 name if name.starts_with("log.") => (),
258 name => {
259 self.event_builder
260 .attributes
261 .push(KeyValue::new(name, value));
262 }
263 }
264 }
265
266 fn record_f64(&mut self, field: &field::Field, value: f64) {
270 match field.name() {
271 "message" => self.event_builder.name = value.to_string().into(),
272 #[cfg(feature = "tracing-log")]
274 name if name.starts_with("log.") => (),
275 name => {
276 self.event_builder
277 .attributes
278 .push(KeyValue::new(name, value));
279 }
280 }
281 }
282
283 fn record_i64(&mut self, field: &field::Field, value: i64) {
287 match field.name() {
288 "message" => self.event_builder.name = value.to_string().into(),
289 #[cfg(feature = "tracing-log")]
291 name if name.starts_with("log.") => (),
292 name => {
293 self.event_builder
294 .attributes
295 .push(KeyValue::new(name, value));
296 }
297 }
298 }
299
300 fn record_str(&mut self, field: &field::Field, value: &str) {
304 match field.name() {
305 "message" => self.event_builder.name = value.to_string().into(),
306 "error" if self.event_builder.name.is_empty() => {
310 if self.sem_conv_config.error_events_to_status {
311 self.span_builder_updates
312 .get_or_insert_with(SpanBuilderUpdates::default)
313 .status
314 .replace(otel::Status::error(format!("{value:?}")));
315 }
316 if self.sem_conv_config.error_events_to_exceptions {
317 self.event_builder.name = EVENT_EXCEPTION_NAME.into();
318 self.event_builder
319 .attributes
320 .push(KeyValue::new(FIELD_EXCEPTION_MESSAGE, format!("{value:?}")));
321 } else {
322 self.event_builder
323 .attributes
324 .push(KeyValue::new("error", format!("{value:?}")));
325 }
326 }
327 #[cfg(feature = "tracing-log")]
329 name if name.starts_with("log.") => (),
330 name => {
331 self.event_builder
332 .attributes
333 .push(KeyValue::new(name, value.to_string()));
334 }
335 }
336 }
337
338 fn record_debug(&mut self, field: &field::Field, value: &dyn fmt::Debug) {
343 match field.name() {
344 "message" => self.event_builder.name = format!("{value:?}").into(),
345 "error" if self.event_builder.name.is_empty() => {
349 if self.sem_conv_config.error_events_to_status {
350 self.span_builder_updates
351 .get_or_insert_with(SpanBuilderUpdates::default)
352 .status
353 .replace(otel::Status::error(format!("{value:?}")));
354 }
355 if self.sem_conv_config.error_events_to_exceptions {
356 self.event_builder.name = EVENT_EXCEPTION_NAME.into();
357 self.event_builder
358 .attributes
359 .push(KeyValue::new(FIELD_EXCEPTION_MESSAGE, format!("{value:?}")));
360 } else {
361 self.event_builder
362 .attributes
363 .push(KeyValue::new("error", format!("{value:?}")));
364 }
365 }
366 #[cfg(feature = "tracing-log")]
368 name if name.starts_with("log.") => (),
369 name => {
370 self.event_builder
371 .attributes
372 .push(KeyValue::new(name, format!("{value:?}")));
373 }
374 }
375 }
376
377 fn record_error(
382 &mut self,
383 field: &tracing_core::Field,
384 value: &(dyn std::error::Error + 'static),
385 ) {
386 let mut chain: Vec<StringValue> = Vec::new();
387 let mut next_err = value.source();
388
389 while let Some(err) = next_err {
390 chain.push(err.to_string().into());
391 next_err = err.source();
392 }
393
394 let error_msg = value.to_string();
395
396 if self.sem_conv_config.error_fields_to_exceptions {
397 self.event_builder.attributes.push(KeyValue::new(
398 Key::new(FIELD_EXCEPTION_MESSAGE),
399 Value::String(StringValue::from(error_msg.clone())),
400 ));
401
402 self.event_builder.attributes.push(KeyValue::new(
409 Key::new(FIELD_EXCEPTION_STACKTRACE),
410 Value::Array(chain.clone().into()),
411 ));
412 }
413
414 if self.sem_conv_config.error_records_to_exceptions {
415 let attributes = self
416 .span_builder_updates
417 .get_or_insert_with(SpanBuilderUpdates::default)
418 .attributes
419 .get_or_insert_with(Vec::new);
420
421 attributes.push(KeyValue::new(
422 FIELD_EXCEPTION_MESSAGE,
423 Value::String(error_msg.clone().into()),
424 ));
425
426 attributes.push(KeyValue::new(
433 FIELD_EXCEPTION_STACKTRACE,
434 Value::Array(chain.clone().into()),
435 ));
436 }
437
438 self.event_builder.attributes.push(KeyValue::new(
439 Key::new(field.name()),
440 Value::String(StringValue::from(error_msg)),
441 ));
442 self.event_builder.attributes.push(KeyValue::new(
443 Key::new(format!("{}.chain", field.name())),
444 Value::Array(chain.into()),
445 ));
446 }
447}
448
449#[derive(Clone, Copy)]
451struct SemConvConfig {
452 error_fields_to_exceptions: bool,
457
458 error_records_to_exceptions: bool,
463
464 error_events_to_status: bool,
473
474 error_events_to_exceptions: bool,
482}
483
484struct SpanAttributeVisitor<'a> {
485 span_builder_updates: &'a mut SpanBuilderUpdates,
486 sem_conv_config: SemConvConfig,
487}
488
489impl SpanAttributeVisitor<'_> {
490 fn record(&mut self, attribute: KeyValue) {
491 self.span_builder_updates
492 .attributes
493 .get_or_insert_with(Vec::new)
494 .push(KeyValue::new(attribute.key, attribute.value));
495 }
496}
497
498impl field::Visit for SpanAttributeVisitor<'_> {
499 fn record_bool(&mut self, field: &field::Field, value: bool) {
503 self.record(KeyValue::new(field.name(), value));
504 }
505
506 fn record_f64(&mut self, field: &field::Field, value: f64) {
510 self.record(KeyValue::new(field.name(), value));
511 }
512
513 fn record_i64(&mut self, field: &field::Field, value: i64) {
517 self.record(KeyValue::new(field.name(), value));
518 }
519
520 fn record_str(&mut self, field: &field::Field, value: &str) {
524 match field.name() {
525 SPAN_NAME_FIELD => self.span_builder_updates.name = Some(value.to_string().into()),
526 SPAN_KIND_FIELD => self.span_builder_updates.span_kind = str_to_span_kind(value),
527 SPAN_STATUS_CODE_FIELD => self.span_builder_updates.status = Some(str_to_status(value)),
528 SPAN_STATUS_DESCRIPTION_FIELD => {
529 self.span_builder_updates.status = Some(otel::Status::error(value.to_string()))
530 }
531 _ => self.record(KeyValue::new(field.name(), value.to_string())),
532 }
533 }
534
535 fn record_debug(&mut self, field: &field::Field, value: &dyn fmt::Debug) {
540 match field.name() {
541 SPAN_NAME_FIELD => self.span_builder_updates.name = Some(format!("{value:?}").into()),
542 SPAN_KIND_FIELD => {
543 self.span_builder_updates.span_kind = str_to_span_kind(&format!("{value:?}"))
544 }
545 SPAN_STATUS_CODE_FIELD => {
546 self.span_builder_updates.status = Some(str_to_status(&format!("{value:?}")))
547 }
548 SPAN_STATUS_DESCRIPTION_FIELD => {
549 self.span_builder_updates.status = Some(otel::Status::error(format!("{value:?}")))
550 }
551 _ => self.record(KeyValue::new(
552 Key::new(field.name()),
553 Value::String(format!("{value:?}").into()),
554 )),
555 }
556 }
557
558 fn record_error(
563 &mut self,
564 field: &tracing_core::Field,
565 value: &(dyn std::error::Error + 'static),
566 ) {
567 let mut chain: Vec<StringValue> = Vec::new();
568 let mut next_err = value.source();
569
570 while let Some(err) = next_err {
571 chain.push(err.to_string().into());
572 next_err = err.source();
573 }
574
575 let error_msg = value.to_string();
576
577 if self.sem_conv_config.error_fields_to_exceptions {
578 self.record(KeyValue::new(
579 Key::new(FIELD_EXCEPTION_MESSAGE),
580 Value::from(error_msg.clone()),
581 ));
582
583 self.record(KeyValue::new(
590 Key::new(FIELD_EXCEPTION_STACKTRACE),
591 Value::Array(chain.clone().into()),
592 ));
593 }
594
595 self.record(KeyValue::new(
596 Key::new(field.name()),
597 Value::String(error_msg.into()),
598 ));
599 self.record(KeyValue::new(
600 Key::new(format!("{}.chain", field.name())),
601 Value::Array(chain.into()),
602 ));
603 }
604}
605
606impl<S, T> OpenTelemetryLayer<S, T>
607where
608 S: Subscriber + for<'span> LookupSpan<'span>,
609 T: otel::Tracer + 'static,
610 T::Span: Send + Sync,
611{
612 pub fn new(tracer: T) -> Self {
647 OpenTelemetryLayer {
648 tracer,
649 location: true,
650 tracked_inactivity: true,
651 with_threads: true,
652 with_level: false,
653 with_target: true,
654 context_activation: true,
655 sem_conv_config: SemConvConfig {
656 error_fields_to_exceptions: true,
657 error_records_to_exceptions: true,
658 error_events_to_exceptions: true,
659 error_events_to_status: true,
660 },
661 with_context: WithContext {
662 with_context: Self::get_context,
663 with_activated_context: Self::get_activated_context,
664 with_activated_otel_context: Self::get_activated_otel_context,
665 },
666 _registry: marker::PhantomData,
667 }
668 }
669
670 pub fn with_tracer<Tracer>(self, tracer: Tracer) -> OpenTelemetryLayer<S, Tracer>
704 where
705 Tracer: otel::Tracer + 'static,
706 Tracer::Span: Send + Sync,
707 {
708 OpenTelemetryLayer {
709 tracer,
710 location: self.location,
711 tracked_inactivity: self.tracked_inactivity,
712 with_threads: self.with_threads,
713 with_level: self.with_level,
714 with_target: self.with_target,
715 context_activation: self.context_activation,
716 sem_conv_config: self.sem_conv_config,
717 with_context: WithContext {
718 with_context: OpenTelemetryLayer::<S, Tracer>::get_context,
719 with_activated_context: OpenTelemetryLayer::<S, Tracer>::get_activated_context,
720 with_activated_otel_context:
721 OpenTelemetryLayer::<S, Tracer>::get_activated_otel_context,
722 },
723 _registry: self._registry,
724 }
726 }
727
728 pub fn with_error_fields_to_exceptions(self, error_fields_to_exceptions: bool) -> Self {
744 Self {
745 sem_conv_config: SemConvConfig {
746 error_fields_to_exceptions,
747 ..self.sem_conv_config
748 },
749 ..self
750 }
751 }
752
753 pub fn with_error_events_to_status(self, error_events_to_status: bool) -> Self {
758 Self {
759 sem_conv_config: SemConvConfig {
760 error_events_to_status,
761 ..self.sem_conv_config
762 },
763 ..self
764 }
765 }
766
767 pub fn with_error_events_to_exceptions(self, error_events_to_exceptions: bool) -> Self {
778 Self {
779 sem_conv_config: SemConvConfig {
780 error_events_to_exceptions,
781 ..self.sem_conv_config
782 },
783 ..self
784 }
785 }
786
787 pub fn with_error_records_to_exceptions(self, error_records_to_exceptions: bool) -> Self {
803 Self {
804 sem_conv_config: SemConvConfig {
805 error_records_to_exceptions,
806 ..self.sem_conv_config
807 },
808 ..self
809 }
810 }
811
812 pub fn with_location(self, location: bool) -> Self {
822 Self { location, ..self }
823 }
824
825 pub fn with_tracked_inactivity(self, tracked_inactivity: bool) -> Self {
831 Self {
832 tracked_inactivity,
833 ..self
834 }
835 }
836
837 pub fn with_threads(self, threads: bool) -> Self {
845 Self {
846 with_threads: threads,
847 ..self
848 }
849 }
850
851 pub fn with_level(self, level: bool) -> Self {
858 Self {
859 with_level: level,
860 ..self
861 }
862 }
863
864 pub fn with_target(self, target: bool) -> Self {
868 Self {
869 with_target: target,
870 ..self
871 }
872 }
873
874 pub fn with_context_activation(self, context_activation: bool) -> Self {
882 Self {
883 context_activation,
884 ..self
885 }
886 }
887
888 pub fn with_counting_event_filter<F: Filter<S>>(
894 self,
895 filter: F,
896 ) -> FilteredOpenTelemetryLayer<S, T, F> {
897 FilteredOpenTelemetryLayer::new(self, filter)
898 }
899
900 fn parent_context(&self, attrs: &Attributes<'_>, ctx: &Context<'_, S>) -> OtelContext {
908 if let Some(parent) = attrs.parent() {
909 if let Some(span) = ctx.span(parent) {
922 let mut extensions = span.extensions_mut();
923 if let Some(otel_data) = extensions.get_mut::<OtelData>() {
924 return self.with_started_cx(otel_data, &|cx| cx.clone());
927 }
928 }
929 }
930
931 if attrs.is_contextual() {
932 if self.context_activation {
933 OtelContext::current()
936 } else {
937 ctx.lookup_current()
940 .and_then(|span| {
941 let mut extensions = span.extensions_mut();
942 extensions
943 .get_mut::<OtelData>()
944 .map(|data| self.with_started_cx(data, &|cx| cx.clone()))
945 })
946 .unwrap_or_else(OtelContext::current)
947 }
948 } else {
949 OtelContext::default()
950 }
951 }
952
953 fn get_context(dispatch: &tracing::Dispatch, id: &span::Id, f: &mut dyn FnMut(&mut OtelData)) {
966 let subscriber = dispatch
967 .downcast_ref::<S>()
968 .expect("subscriber should downcast to expected type; this is a bug!");
969 let span = subscriber
970 .span(id)
971 .expect("registry should have a span for the current ID");
972
973 let mut extensions = span.extensions_mut();
974 if let Some(otel_data) = extensions.get_mut::<OtelData>() {
975 f(otel_data);
976 }
977 }
978
979 fn get_activated_context(
992 dispatch: &tracing::Dispatch,
993 id: &span::Id,
994 f: &mut dyn FnMut(&mut OtelData),
995 ) {
996 let subscriber = dispatch
997 .downcast_ref::<S>()
998 .expect("subscriber should downcast to expected type; this is a bug!");
999 let span = subscriber
1000 .span(id)
1001 .expect("registry should have a span for the current ID");
1002
1003 let mut extensions = span.extensions_mut();
1004
1005 Self::get_activated_context_extensions(dispatch, &mut extensions, f)
1006 }
1007
1008 fn get_activated_otel_context(
1021 dispatch: &tracing::Dispatch,
1022 extensions: &mut ExtensionsMut<'_>,
1023 f: &mut dyn FnMut(&OtelContext),
1024 ) {
1025 Self::get_activated_context_extensions(
1026 dispatch,
1027 extensions,
1028 &mut |otel_data: &mut OtelData| {
1029 if let OtelDataState::Context { current_cx } = &otel_data.state {
1030 f(current_cx)
1031 }
1032 },
1033 );
1034 }
1035
1036 fn get_activated_context_extensions(
1037 dispatch: &tracing::Dispatch,
1038 extensions: &mut ExtensionsMut<'_>,
1039 f: &mut dyn FnMut(&mut OtelData),
1040 ) {
1041 let layer = dispatch
1042 .downcast_ref::<OpenTelemetryLayer<S, T>>()
1043 .expect("layer should downcast to expected type; this is a bug!");
1044
1045 if let Some(otel_data) = extensions.get_mut::<OtelData>() {
1046 layer.start_cx(otel_data);
1048 f(otel_data);
1049 }
1050 }
1051
1052 fn extra_span_attrs(&self) -> usize {
1053 let mut extra_attrs = 0;
1054 if self.location {
1055 extra_attrs += 3;
1056 }
1057 if self.with_threads {
1058 extra_attrs += 2;
1059 }
1060 if self.with_level {
1061 extra_attrs += 1;
1062 }
1063 if self.with_target {
1064 extra_attrs += 1;
1065 }
1066 extra_attrs
1067 }
1068
1069 fn start_cx(&self, otel_data: &mut OtelData) {
1074 if let OtelDataState::Context { .. } = &otel_data.state {
1075 } else if let OtelDataState::Builder {
1077 builder,
1078 parent_cx,
1079 status,
1080 } = take(&mut otel_data.state)
1081 {
1082 let mut span = builder.start_with_context(&self.tracer, &parent_cx);
1083 span.set_status(status);
1084 let current_cx = parent_cx.with_span(span);
1085 otel_data.state = OtelDataState::Context { current_cx };
1086 }
1087 }
1088
1089 fn with_started_cx<U>(&self, otel_data: &mut OtelData, f: &dyn Fn(&OtelContext) -> U) -> U {
1090 self.start_cx(otel_data);
1091 match &otel_data.state {
1092 OtelDataState::Context { current_cx, .. } => f(current_cx),
1093 _ => panic!("OtelDataState should be a Context after starting it; this is a bug!"),
1094 }
1095 }
1096}
1097
1098thread_local! {
1099 static THREAD_ID: u64 = {
1100 thread_id_integer(thread::current().id())
1108 };
1109}
1110
1111thread_local! {
1112 static GUARD_STACK: RefCell<IdContextGuardStack> = RefCell::new(IdContextGuardStack::new());
1113}
1114
1115type IdContextGuardStack = IdValueStack<ContextGuard>;
1116
1117impl<S, T> Layer<S> for OpenTelemetryLayer<S, T>
1118where
1119 S: Subscriber + for<'span> LookupSpan<'span>,
1120 T: otel::Tracer + 'static,
1121 T::Span: Send + Sync,
1122{
1123 fn on_new_span(&self, attrs: &Attributes<'_>, id: &span::Id, ctx: Context<'_, S>) {
1128 let span = ctx.span(id).expect("Span not found, this is a bug");
1129 let mut extensions = span.extensions_mut();
1130
1131 if self.tracked_inactivity && extensions.get_mut::<Timings>().is_none() {
1132 extensions.insert(Timings::new());
1133 }
1134
1135 let parent_cx = self.parent_context(attrs, &ctx);
1136 let mut builder = self
1137 .tracer
1138 .span_builder(attrs.metadata().name())
1139 .with_start_time(crate::time::now());
1140
1141 let builder_attrs = builder.attributes.get_or_insert(Vec::with_capacity(
1142 attrs.fields().len() + self.extra_span_attrs(),
1143 ));
1144
1145 if self.location {
1146 let meta = attrs.metadata();
1147
1148 if let Some(filename) = meta.file() {
1149 builder_attrs.push(KeyValue::new("code.file.path", filename));
1150 }
1151
1152 if let Some(module) = meta.module_path() {
1153 builder_attrs.push(KeyValue::new("code.module.name", module));
1154 }
1155
1156 if let Some(line) = meta.line() {
1157 builder_attrs.push(KeyValue::new("code.line.number", line as i64));
1158 }
1159 }
1160
1161 if self.with_threads {
1162 THREAD_ID.with(|id| builder_attrs.push(KeyValue::new("thread.id", *id as i64)));
1163 if let Some(name) = std::thread::current().name() {
1164 builder_attrs.push(KeyValue::new("thread.name", name.to_string()));
1169 }
1170 }
1171
1172 if self.with_level {
1173 builder_attrs.push(KeyValue::new("level", attrs.metadata().level().as_str()));
1174 }
1175 if self.with_target {
1176 builder_attrs.push(KeyValue::new("target", attrs.metadata().target()));
1177 }
1178
1179 let mut updates = SpanBuilderUpdates::default();
1180 attrs.record(&mut SpanAttributeVisitor {
1181 span_builder_updates: &mut updates,
1182 sem_conv_config: self.sem_conv_config,
1183 });
1184
1185 let mut status = Status::Unset;
1186 updates.update(&mut builder, &mut status);
1187 extensions.insert(OtelData {
1188 state: OtelDataState::Builder {
1189 builder,
1190 parent_cx,
1191 status,
1192 },
1193 end_time: None,
1194 });
1195 }
1196
1197 fn on_enter(&self, id: &span::Id, ctx: Context<'_, S>) {
1198 if !self.context_activation && !self.tracked_inactivity {
1199 return;
1200 }
1201
1202 let span = ctx.span(id).expect("Span not found, this is a bug");
1203 let mut extensions = span.extensions_mut();
1204
1205 if self.context_activation {
1206 if let Some(otel_data) = extensions.get_mut::<OtelData>() {
1207 self.with_started_cx(otel_data, &|cx| {
1208 let guard = cx.clone().attach();
1209 GUARD_STACK.with(|stack| stack.borrow_mut().push(id.clone(), guard));
1210 });
1211 }
1212
1213 if !self.tracked_inactivity {
1214 return;
1215 }
1216 }
1217
1218 if let Some(timings) = extensions.get_mut::<Timings>() {
1219 if timings.entered_count == 0 {
1220 let now = Instant::now();
1221 timings.idle += (now - timings.last).as_nanos() as i64;
1222 timings.last = now;
1223 }
1224 timings.entered_count += 1;
1225 }
1226 }
1227
1228 fn on_exit(&self, id: &span::Id, ctx: Context<'_, S>) {
1229 let span = ctx.span(id).expect("Span not found, this is a bug");
1230 let mut extensions = span.extensions_mut();
1231
1232 if let Some(otel_data) = extensions.get_mut::<OtelData>() {
1233 otel_data.end_time = Some(crate::time::now());
1234 if self.context_activation {
1235 GUARD_STACK.with(|stack| stack.borrow_mut().pop(id));
1236 }
1237 }
1238
1239 if !self.tracked_inactivity {
1240 return;
1241 }
1242
1243 if let Some(timings) = extensions.get_mut::<Timings>() {
1244 timings.entered_count -= 1;
1245 if timings.entered_count == 0 {
1246 let now = Instant::now();
1247 timings.busy += (now - timings.last).as_nanos() as i64;
1248 timings.last = now;
1249 }
1250 }
1251 }
1252
1253 fn on_record(&self, id: &Id, values: &Record<'_>, ctx: Context<'_, S>) {
1257 let span = ctx.span(id).expect("Span not found, this is a bug");
1258 let mut updates = SpanBuilderUpdates::default();
1259 values.record(&mut SpanAttributeVisitor {
1260 span_builder_updates: &mut updates,
1261 sem_conv_config: self.sem_conv_config,
1262 });
1263 let mut extensions = span.extensions_mut();
1264 if let Some(otel_data) = extensions.get_mut::<OtelData>() {
1265 match &mut otel_data.state {
1266 OtelDataState::Builder {
1267 builder, status, ..
1268 } => {
1269 updates.update(builder, status);
1271 }
1272 OtelDataState::Context { current_cx, .. } => {
1273 updates.update_span(¤t_cx.span());
1275 }
1276 }
1277 }
1278 }
1279
1280 fn on_follows_from(&self, id: &Id, follows: &Id, ctx: Context<S>) {
1281 let span = ctx.span(id).expect("Span not found, this is a bug");
1282 let mut extensions = span.extensions_mut();
1283 let Some(data) = extensions.get_mut::<OtelData>() else {
1284 return; };
1286
1287 if let Some(follows_span) = ctx.span(follows) {
1291 let mut follows_extensions = follows_span.extensions_mut();
1292 let Some(follows_data) = follows_extensions.get_mut::<OtelData>() else {
1293 return; };
1295 let follows_context =
1296 self.with_started_cx(follows_data, &|cx| cx.span().span_context().clone());
1297 match &mut data.state {
1298 OtelDataState::Builder { builder, .. } => {
1299 if let Some(ref mut links) = builder.links {
1300 links.push(otel::Link::with_context(follows_context));
1301 } else {
1302 builder.links = Some(vec![otel::Link::with_context(follows_context)]);
1303 }
1304 }
1305 OtelDataState::Context { current_cx, .. } => {
1306 current_cx.span().add_link(follows_context, vec![]);
1307 }
1308 }
1309 }
1310 }
1311
1312 fn on_event(&self, event: &Event<'_>, ctx: Context<'_, S>) {
1321 if let Some(span) = event.parent().and_then(|id| ctx.span(id)).or_else(|| {
1323 event
1324 .is_contextual()
1325 .then(|| ctx.lookup_current())
1326 .flatten()
1327 }) {
1328 #[cfg(feature = "tracing-log")]
1331 let normalized_meta = event.normalized_metadata();
1332 #[cfg(feature = "tracing-log")]
1333 let meta = normalized_meta.as_ref().unwrap_or_else(|| event.metadata());
1334 #[cfg(not(feature = "tracing-log"))]
1335 let meta = event.metadata();
1336
1337 let target = Key::new("target");
1338
1339 #[cfg(feature = "tracing-log")]
1340 let target = if normalized_meta.is_some() {
1341 KeyValue::new(target, Value::String(meta.target().to_owned().into()))
1342 } else {
1343 KeyValue::new(target, Value::String(event.metadata().target().into()))
1344 };
1345
1346 #[cfg(not(feature = "tracing-log"))]
1347 let target = KeyValue::new(target, Value::String(meta.target().into()));
1348
1349 let mut otel_event = otel::Event::new(
1350 String::new(),
1351 crate::time::now(),
1352 vec![
1353 KeyValue::new(
1354 Key::new("level"),
1355 Value::String(meta.level().as_str().into()),
1356 ),
1357 target,
1358 ],
1359 0,
1360 );
1361
1362 let mut builder_updates = None;
1363 event.record(&mut SpanEventVisitor {
1364 event_builder: &mut otel_event,
1365 span_builder_updates: &mut builder_updates,
1366 sem_conv_config: self.sem_conv_config,
1367 });
1368
1369 if otel_event.name.is_empty() {
1375 otel_event.name = std::borrow::Cow::Borrowed(event.metadata().name());
1376 }
1377
1378 let mut extensions = span.extensions_mut();
1379
1380 if let Some(otel_data) = extensions.get_mut::<OtelData>() {
1381 if self.location {
1382 #[cfg(not(feature = "tracing-log"))]
1383 let normalized_meta: Option<tracing_core::Metadata<'_>> = None;
1384 let (file, module) = match &normalized_meta {
1385 Some(meta) => (
1386 meta.file().map(|s| Value::from(s.to_owned())),
1387 meta.module_path().map(|s| Value::from(s.to_owned())),
1388 ),
1389 None => (
1390 event.metadata().file().map(Value::from),
1391 event.metadata().module_path().map(Value::from),
1392 ),
1393 };
1394
1395 if let Some(file) = file {
1396 otel_event
1397 .attributes
1398 .push(KeyValue::new("code.file.path", file));
1399 }
1400 if let Some(module) = module {
1401 otel_event
1402 .attributes
1403 .push(KeyValue::new("code.module.name", module));
1404 }
1405 if let Some(line) = meta.line() {
1406 otel_event
1407 .attributes
1408 .push(KeyValue::new("code.line.number", line as i64));
1409 }
1410 }
1411
1412 match &mut otel_data.state {
1413 OtelDataState::Builder {
1414 builder, status, ..
1415 } => {
1416 if *status == otel::Status::Unset
1417 && *meta.level() == tracing_core::Level::ERROR
1418 {
1419 *status = otel::Status::error("");
1420 }
1421 if let Some(builder_updates) = builder_updates {
1422 builder_updates.update(builder, status);
1423 }
1424 if let Some(ref mut events) = builder.events {
1425 events.push(otel_event);
1426 } else {
1427 builder.events = Some(vec![otel_event]);
1428 }
1429 }
1430 OtelDataState::Context { current_cx, .. } => {
1431 let span = current_cx.span();
1432 if *meta.level() == tracing_core::Level::ERROR {
1434 span.set_status(otel::Status::error(""));
1435 }
1436 if let Some(builder_updates) = builder_updates {
1437 builder_updates.update_span(&span);
1438 }
1439 span.add_event(otel_event.name, otel_event.attributes);
1440 }
1441 }
1442 };
1443 }
1444 }
1445
1446 fn on_close(&self, id: span::Id, ctx: Context<'_, S>) {
1450 let span = ctx.span(&id).expect("Span not found, this is a bug");
1451 let (otel_data, timings) = {
1453 let mut extensions = span.extensions_mut();
1454 let timings = if self.tracked_inactivity {
1455 extensions.remove::<Timings>()
1456 } else {
1457 None
1458 };
1459 (extensions.remove::<OtelData>(), timings)
1460 };
1461
1462 if let Some(OtelData { state, end_time }) = otel_data {
1463 let timings = timings.map(|timings| {
1465 let busy_ns = Key::new("busy_ns");
1466 let idle_ns = Key::new("idle_ns");
1467
1468 vec![
1469 KeyValue::new(busy_ns, timings.busy),
1470 KeyValue::new(idle_ns, timings.idle),
1471 ]
1472 });
1473
1474 match state {
1475 OtelDataState::Builder {
1476 builder,
1477 parent_cx,
1478 status,
1479 } => {
1480 let mut span = builder.start_with_context(&self.tracer, &parent_cx);
1482 if let Some(timings) = timings {
1483 span.set_attributes(timings)
1484 };
1485 span.set_status(status);
1486 if let Some(end_time) = end_time {
1487 span.end_with_timestamp(end_time);
1488 } else {
1489 span.end();
1490 }
1491 }
1492 OtelDataState::Context { current_cx } => {
1493 let span = current_cx.span();
1494 if let Some(timings) = timings {
1495 span.set_attributes(timings)
1496 };
1497 end_time
1498 .map_or_else(|| span.end(), |end_time| span.end_with_timestamp(end_time));
1499 }
1500 }
1501 }
1502 }
1503
1504 unsafe fn downcast_raw(&self, id: TypeId) -> Option<*const ()> {
1507 match id {
1508 id if id == TypeId::of::<Self>() => Some(self as *const _ as *const ()),
1509 id if id == TypeId::of::<WithContext>() => {
1510 Some(&self.with_context as *const _ as *const ())
1511 }
1512 _ => None,
1513 }
1514 }
1515}
1516
1517struct Timings {
1518 idle: i64,
1519 busy: i64,
1520 last: Instant,
1521 entered_count: u64,
1522}
1523
1524impl Timings {
1525 fn new() -> Self {
1526 Self {
1527 idle: 0,
1528 busy: 0,
1529 last: Instant::now(),
1530 entered_count: 0,
1531 }
1532 }
1533}
1534
1535fn thread_id_integer(id: thread::ThreadId) -> u64 {
1536 let thread_id = format!("{id:?}");
1537 thread_id
1538 .trim_start_matches("ThreadId(")
1539 .trim_end_matches(')')
1540 .parse::<u64>()
1541 .expect("thread ID should parse as an integer")
1542}
1543
1544#[cfg(test)]
1545mod tests {
1546 use crate::OpenTelemetrySpanExt;
1547
1548 use super::*;
1549 use opentelemetry::trace::{SpanContext, TraceFlags, TracerProvider};
1550 use opentelemetry_sdk::trace::SpanExporter;
1551 use std::{collections::HashMap, error::Error, fmt::Display, time::SystemTime};
1552 use tracing::trace_span;
1553 use tracing_core::LevelFilter;
1554 use tracing_subscriber::prelude::*;
1555
1556 #[derive(Debug, Clone)]
1557 struct TestTracer {
1558 tracer: opentelemetry_sdk::trace::Tracer,
1559 exporter: opentelemetry_sdk::trace::InMemorySpanExporter,
1560 }
1561
1562 impl TestTracer {
1563 fn spans(&mut self) -> Vec<opentelemetry_sdk::trace::SpanData> {
1564 self.exporter
1565 .force_flush()
1566 .expect("problems flushing spans");
1567 self.exporter
1568 .get_finished_spans()
1569 .expect("problems recording spans")
1570 }
1571
1572 fn with_data<T>(&mut self, f: impl FnOnce(&opentelemetry_sdk::trace::SpanData) -> T) -> T {
1573 let spans = self.spans();
1574 f(spans.first().expect("no spans recorded"))
1575 }
1576
1577 fn attributes(&mut self) -> HashMap<String, Value> {
1578 self.with_data(|data| {
1579 data.attributes
1580 .iter()
1581 .map(|kv| (kv.key.to_string(), kv.value.clone()))
1582 .collect()
1583 })
1584 }
1585 }
1586
1587 impl Default for TestTracer {
1588 fn default() -> Self {
1589 let exporter = opentelemetry_sdk::trace::InMemorySpanExporter::default();
1590 let provider = opentelemetry_sdk::trace::SdkTracerProvider::builder()
1591 .with_simple_exporter(exporter.clone())
1592 .build();
1593 let tracer = provider.tracer("test-tracer");
1594 Self { tracer, exporter }
1595 }
1596 }
1597
1598 impl opentelemetry::trace::Tracer for TestTracer {
1599 type Span = opentelemetry_sdk::trace::Span;
1600
1601 fn build_with_context(&self, builder: SpanBuilder, parent_cx: &OtelContext) -> Self::Span {
1602 self.tracer.build_with_context(builder, parent_cx)
1603 }
1604 }
1605
1606 #[derive(Debug, Clone)]
1607 struct TestSpan(otel::SpanContext);
1608 impl otel::Span for TestSpan {
1609 fn add_event_with_timestamp<T: Into<Cow<'static, str>>>(
1610 &mut self,
1611 _: T,
1612 _: SystemTime,
1613 _: Vec<KeyValue>,
1614 ) {
1615 }
1616 fn span_context(&self) -> &otel::SpanContext {
1617 &self.0
1618 }
1619 fn is_recording(&self) -> bool {
1620 false
1621 }
1622 fn set_attribute(&mut self, _attribute: KeyValue) {}
1623 fn set_status(&mut self, _status: otel::Status) {}
1624 fn update_name<T: Into<Cow<'static, str>>>(&mut self, _new_name: T) {}
1625 fn add_link(&mut self, _span_context: SpanContext, _attributes: Vec<KeyValue>) {}
1626 fn end_with_timestamp(&mut self, _timestamp: SystemTime) {}
1627 }
1628
1629 #[derive(Debug)]
1630 struct TestDynError {
1631 msg: &'static str,
1632 source: Option<Box<TestDynError>>,
1633 }
1634 impl Display for TestDynError {
1635 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1636 write!(f, "{}", self.msg)
1637 }
1638 }
1639 impl Error for TestDynError {
1640 fn source(&self) -> Option<&(dyn Error + 'static)> {
1641 match &self.source {
1642 Some(source) => Some(source),
1643 None => None,
1644 }
1645 }
1646 }
1647 impl TestDynError {
1648 fn new(msg: &'static str) -> Self {
1649 Self { msg, source: None }
1650 }
1651 fn with_parent(self, parent_msg: &'static str) -> Self {
1652 Self {
1653 msg: parent_msg,
1654 source: Some(Box::new(self)),
1655 }
1656 }
1657 }
1658
1659 #[test]
1660 fn dynamic_span_names() {
1661 let dynamic_name = "GET http://example.com".to_string();
1662 let mut tracer = TestTracer::default();
1663 let subscriber = tracing_subscriber::registry().with(layer().with_tracer(tracer.clone()));
1664
1665 tracing::subscriber::with_default(subscriber, || {
1666 tracing::debug_span!("static_name", otel.name = dynamic_name.as_str());
1667 });
1668
1669 let recorded_name = tracer.spans().first().unwrap().name.clone();
1670 assert_eq!(recorded_name, dynamic_name.as_str())
1671 }
1672
1673 #[test]
1674 fn span_kind() {
1675 let mut tracer = TestTracer::default();
1676 let subscriber = tracing_subscriber::registry().with(layer().with_tracer(tracer.clone()));
1677
1678 tracing::subscriber::with_default(subscriber, || {
1679 tracing::debug_span!("request", otel.kind = "server");
1680 });
1681
1682 let recorded_kind = tracer.with_data(|data| data.span_kind.clone());
1683 assert_eq!(recorded_kind, otel::SpanKind::Server)
1684 }
1685
1686 #[test]
1687 fn span_status_code() {
1688 let mut tracer = TestTracer::default();
1689 let subscriber = tracing_subscriber::registry().with(layer().with_tracer(tracer.clone()));
1690
1691 tracing::subscriber::with_default(subscriber, || {
1692 tracing::debug_span!("request", otel.status_code = ?otel::Status::Ok);
1693 });
1694
1695 let recorded_status = tracer.with_data(|data| data.status.clone());
1696 assert_eq!(recorded_status, otel::Status::Ok)
1697 }
1698
1699 #[test]
1700 fn span_status_description() {
1701 let mut tracer = TestTracer::default();
1702 let subscriber = tracing_subscriber::registry().with(layer().with_tracer(tracer.clone()));
1703
1704 let message = "message";
1705
1706 tracing::subscriber::with_default(subscriber, || {
1707 tracing::debug_span!("request", otel.status_description = message);
1708 });
1709
1710 let recorded_status_message = tracer.with_data(|data| data.status.clone());
1711
1712 assert_eq!(recorded_status_message, otel::Status::error(message))
1713 }
1714
1715 #[test]
1716 fn trace_id_from_existing_context_with_context_activation() {
1717 trace_id_from_existing_context_impl(true);
1718 }
1719
1720 #[test]
1721 fn trace_id_from_existing_context_no_context_activation() {
1722 trace_id_from_existing_context_impl(false);
1723 }
1724
1725 fn trace_id_from_existing_context_impl(context_activation: bool) {
1726 let mut tracer = TestTracer::default();
1727 let subscriber = tracing_subscriber::registry().with(
1728 layer()
1729 .with_tracer(tracer.clone())
1730 .with_context_activation(context_activation),
1731 );
1732 let trace_id = otel::TraceId::from(42u128);
1733 let existing_cx = OtelContext::current_with_span(TestSpan(otel::SpanContext::new(
1734 trace_id,
1735 otel::SpanId::from(1u64),
1736 TraceFlags::SAMPLED,
1737 false,
1738 Default::default(),
1739 )));
1740 let _g = existing_cx.attach();
1741
1742 tracing::subscriber::with_default(subscriber, || {
1743 tracing::debug_span!("request", otel.kind = "server");
1744 });
1745
1746 let recorded_trace_id = tracer.with_data(|data| data.span_context.trace_id());
1747 assert_eq!(recorded_trace_id, trace_id)
1748 }
1749
1750 #[test]
1751 fn includes_timings_with_context_activation() {
1752 includes_timings_impl(true);
1753 }
1754
1755 #[test]
1756 fn includes_timings_no_context_activation() {
1757 includes_timings_impl(false);
1758 }
1759
1760 fn includes_timings_impl(context_activation: bool) {
1761 let mut tracer = TestTracer::default();
1762 let subscriber = tracing_subscriber::registry().with(
1763 layer()
1764 .with_tracer(tracer.clone())
1765 .with_tracked_inactivity(true)
1766 .with_context_activation(context_activation),
1767 );
1768
1769 tracing::subscriber::with_default(subscriber, || {
1770 tracing::debug_span!("request");
1771 });
1772
1773 let attributes = tracer.attributes();
1774
1775 assert!(attributes.contains_key("idle_ns"));
1776 assert!(attributes.contains_key("busy_ns"));
1777 }
1778
1779 #[test]
1780 fn records_error_fields_with_context_activation() {
1781 records_error_fields_impl(true);
1782 }
1783
1784 #[test]
1785 fn records_error_fields_no_context_activation() {
1786 records_error_fields_impl(false);
1787 }
1788
1789 fn records_error_fields_impl(context_activation: bool) {
1790 let mut tracer = TestTracer::default();
1791 let subscriber = tracing_subscriber::registry().with(
1792 layer()
1793 .with_tracer(tracer.clone())
1794 .with_context_activation(context_activation),
1795 );
1796
1797 let err = TestDynError::new("base error")
1798 .with_parent("intermediate error")
1799 .with_parent("user error");
1800
1801 tracing::subscriber::with_default(subscriber, || {
1802 tracing::debug_span!(
1803 "request",
1804 error = &err as &(dyn std::error::Error + 'static)
1805 );
1806 });
1807
1808 let attributes = tracer.attributes();
1809
1810 assert_eq!(attributes["error"].as_str(), "user error");
1811 assert_eq!(
1812 attributes["error.chain"],
1813 Value::Array(
1814 vec![
1815 StringValue::from("intermediate error"),
1816 StringValue::from("base error")
1817 ]
1818 .into()
1819 )
1820 );
1821
1822 assert_eq!(attributes[FIELD_EXCEPTION_MESSAGE].as_str(), "user error");
1823 assert_eq!(
1824 attributes[FIELD_EXCEPTION_STACKTRACE],
1825 Value::Array(
1826 vec![
1827 StringValue::from("intermediate error"),
1828 StringValue::from("base error")
1829 ]
1830 .into()
1831 )
1832 );
1833 }
1834
1835 #[test]
1836 fn records_event_name_with_context_activation() {
1837 records_event_name_impl(true);
1838 }
1839
1840 #[test]
1841 fn records_event_name_no_context_activation() {
1842 records_event_name_impl(false);
1843 }
1844
1845 fn records_event_name_impl(context_activation: bool) {
1846 let mut tracer = TestTracer::default();
1847 let subscriber = tracing_subscriber::registry().with(
1848 layer()
1849 .with_tracer(tracer.clone())
1850 .with_context_activation(context_activation),
1851 );
1852
1853 tracing::subscriber::with_default(subscriber, || {
1854 tracing::debug_span!("test span").in_scope(|| {
1855 tracing::event!(tracing::Level::INFO, "event name 1"); tracing::event!(name: "event name 2", tracing::Level::INFO, field1 = "field1");
1857 tracing::event!(name: "event name 3", tracing::Level::INFO, error = "field2");
1858 tracing::event!(name: "event name 4", tracing::Level::INFO, message = "field3");
1859 tracing::event!(name: "event name 5", tracing::Level::INFO, name = "field4");
1860 });
1861 });
1862
1863 let events = tracer.with_data(|data| data.events.clone());
1864
1865 let mut iter = events.iter();
1866
1867 assert_eq!(iter.next().unwrap().name, "event name 1");
1868 assert_eq!(iter.next().unwrap().name, "event name 2");
1869 assert_eq!(iter.next().unwrap().name, "exception"); assert_eq!(iter.next().unwrap().name, "field3"); assert_eq!(iter.next().unwrap().name, "event name 5"); }
1873
1874 #[test]
1875 fn event_filter_count() {
1876 let mut tracer = TestTracer::default();
1877 let subscriber = tracing_subscriber::registry().with(
1878 layer()
1879 .with_tracer(tracer.clone())
1880 .with_counting_event_filter(LevelFilter::INFO)
1881 .with_filter(LevelFilter::DEBUG),
1882 );
1883
1884 tracing::subscriber::with_default(subscriber, || {
1885 tracing::debug_span!("test span").in_scope(|| {
1886 tracing::event!(tracing::Level::TRACE, "1");
1887 tracing::event!(tracing::Level::DEBUG, "2");
1888 tracing::event!(tracing::Level::INFO, "3");
1889 tracing::event!(tracing::Level::WARN, "4");
1890 tracing::event!(tracing::Level::ERROR, "5");
1891 });
1892 });
1893
1894 let events = tracer.with_data(|data| data.events.clone());
1895
1896 let mut iter = events.iter();
1897
1898 assert_eq!(iter.next().unwrap().name, "3");
1899 assert_eq!(iter.next().unwrap().name, "4");
1900 assert_eq!(iter.next().unwrap().name, "5");
1901 assert!(iter.next().is_none());
1902
1903 let spans = tracer.spans();
1904 assert_eq!(spans.len(), 1);
1905
1906 let Value::I64(event_count) = spans
1907 .first()
1908 .unwrap()
1909 .attributes
1910 .iter()
1911 .find(|key_value| key_value.key.as_str() == SPAN_EVENT_COUNT_FIELD)
1912 .unwrap()
1913 .value
1914 else {
1915 panic!("Unexpected type of `{SPAN_EVENT_COUNT_FIELD}`.");
1916 };
1917 assert_eq!(event_count, 4);
1920 }
1921
1922 #[test]
1923 fn records_no_error_fields_with_context_activation() {
1924 records_no_error_fields_impl(true);
1925 }
1926
1927 #[test]
1928 fn records_no_error_fields_no_context_activation() {
1929 records_no_error_fields_impl(false);
1930 }
1931
1932 fn records_no_error_fields_impl(context_activation: bool) {
1933 let mut tracer = TestTracer::default();
1934 let subscriber = tracing_subscriber::registry().with(
1935 layer()
1936 .with_error_records_to_exceptions(false)
1937 .with_tracer(tracer.clone())
1938 .with_context_activation(context_activation),
1939 );
1940
1941 let err = TestDynError::new("base error")
1942 .with_parent("intermediate error")
1943 .with_parent("user error");
1944
1945 tracing::subscriber::with_default(subscriber, || {
1946 tracing::debug_span!(
1947 "request",
1948 error = &err as &(dyn std::error::Error + 'static)
1949 );
1950 });
1951
1952 let attributes = tracer.attributes();
1953
1954 assert_eq!(attributes["error"].as_str(), "user error");
1955 assert_eq!(
1956 attributes["error.chain"],
1957 Value::Array(
1958 vec![
1959 StringValue::from("intermediate error"),
1960 StringValue::from("base error")
1961 ]
1962 .into()
1963 )
1964 );
1965
1966 assert_eq!(attributes[FIELD_EXCEPTION_MESSAGE].as_str(), "user error");
1967 assert_eq!(
1968 attributes[FIELD_EXCEPTION_STACKTRACE],
1969 Value::Array(
1970 vec![
1971 StringValue::from("intermediate error"),
1972 StringValue::from("base error")
1973 ]
1974 .into()
1975 )
1976 );
1977 }
1978
1979 #[test]
1980 fn includes_span_location_with_context_activation() {
1981 includes_span_location_impl(true);
1982 }
1983
1984 #[test]
1985 fn includes_span_location_no_context_activation() {
1986 includes_span_location_impl(false);
1987 }
1988
1989 fn includes_span_location_impl(context_activation: bool) {
1990 let mut tracer = TestTracer::default();
1991 let subscriber = tracing_subscriber::registry().with(
1992 layer()
1993 .with_tracer(tracer.clone())
1994 .with_location(true)
1995 .with_context_activation(context_activation),
1996 );
1997
1998 tracing::subscriber::with_default(subscriber, || {
1999 tracing::debug_span!("request");
2000 });
2001
2002 let attributes = tracer.attributes();
2003
2004 assert!(attributes.contains_key("code.file.path"));
2005 assert!(attributes.contains_key("code.module.name"));
2006 assert!(attributes.contains_key("code.line.number"));
2007 }
2008
2009 #[test]
2010 fn excludes_span_location_with_context_activation() {
2011 excludes_span_location_impl(true);
2012 }
2013
2014 #[test]
2015 fn excludes_span_location_no_context_activation() {
2016 excludes_span_location_impl(false);
2017 }
2018
2019 fn excludes_span_location_impl(context_activation: bool) {
2020 let mut tracer = TestTracer::default();
2021 let subscriber = tracing_subscriber::registry().with(
2022 layer()
2023 .with_tracer(tracer.clone())
2024 .with_location(false)
2025 .with_context_activation(context_activation),
2026 );
2027
2028 tracing::subscriber::with_default(subscriber, || {
2029 tracing::debug_span!("request");
2030 });
2031
2032 let attributes = tracer.attributes();
2033
2034 assert!(!attributes.contains_key("code.file.path"));
2035 assert!(!attributes.contains_key("code.module.name"));
2036 assert!(!attributes.contains_key("code.line.number"));
2037 }
2038
2039 #[test]
2040 fn includes_thread_with_context_activation() {
2041 includes_thread_impl(true);
2042 }
2043
2044 #[test]
2045 fn includes_thread_no_context_activation() {
2046 includes_thread_impl(false);
2047 }
2048
2049 fn includes_thread_impl(context_activation: bool) {
2050 let thread = thread::current();
2051 let expected_name = thread
2052 .name()
2053 .map(|name| Value::String(name.to_owned().into()));
2054 let expected_id = Value::I64(thread_id_integer(thread.id()) as i64);
2055
2056 let mut tracer = TestTracer::default();
2057 let subscriber = tracing_subscriber::registry().with(
2058 layer()
2059 .with_tracer(tracer.clone())
2060 .with_threads(true)
2061 .with_context_activation(context_activation),
2062 );
2063
2064 tracing::subscriber::with_default(subscriber, || {
2065 tracing::debug_span!("request");
2066 });
2067
2068 let attributes = tracer.attributes();
2069
2070 assert_eq!(attributes.get("thread.name"), expected_name.as_ref());
2071 assert_eq!(attributes.get("thread.id"), Some(&expected_id));
2072 }
2073
2074 #[test]
2075 fn excludes_thread_with_context_activation() {
2076 excludes_thread_impl(true);
2077 }
2078
2079 #[test]
2080 fn excludes_thread_no_context_activation() {
2081 excludes_thread_impl(false);
2082 }
2083
2084 fn excludes_thread_impl(context_activation: bool) {
2085 let mut tracer = TestTracer::default();
2086 let subscriber = tracing_subscriber::registry().with(
2087 layer()
2088 .with_tracer(tracer.clone())
2089 .with_threads(false)
2090 .with_context_activation(context_activation),
2091 );
2092
2093 tracing::subscriber::with_default(subscriber, || {
2094 tracing::debug_span!("request");
2095 });
2096
2097 let attributes = tracer.attributes();
2098
2099 assert!(!attributes.contains_key("thread.name"));
2100 assert!(!attributes.contains_key("thread.id"));
2101 }
2102
2103 #[test]
2104 fn includes_level_with_context_activation() {
2105 includes_level_impl(true);
2106 }
2107
2108 #[test]
2109 fn includes_level_no_context_activation() {
2110 includes_level_impl(false);
2111 }
2112
2113 fn includes_level_impl(context_activation: bool) {
2114 let mut tracer = TestTracer::default();
2115 let subscriber = tracing_subscriber::registry().with(
2116 layer()
2117 .with_tracer(tracer.clone())
2118 .with_level(true)
2119 .with_context_activation(context_activation),
2120 );
2121
2122 tracing::subscriber::with_default(subscriber, || {
2123 tracing::debug_span!("request");
2124 });
2125
2126 let attributes = tracer.attributes();
2127
2128 assert!(attributes.contains_key("level"));
2129 }
2130
2131 #[test]
2132 fn excludes_level_with_context_activation() {
2133 excludes_level_impl(true);
2134 }
2135
2136 #[test]
2137 fn excludes_level_no_context_activation() {
2138 excludes_level_impl(false);
2139 }
2140
2141 fn excludes_level_impl(context_activation: bool) {
2142 let mut tracer = TestTracer::default();
2143 let subscriber = tracing_subscriber::registry().with(
2144 layer()
2145 .with_tracer(tracer.clone())
2146 .with_level(false)
2147 .with_context_activation(context_activation),
2148 );
2149
2150 tracing::subscriber::with_default(subscriber, || {
2151 tracing::debug_span!("request");
2152 });
2153
2154 let attributes = tracer.attributes();
2155
2156 assert!(!attributes.contains_key("level"));
2157 }
2158
2159 #[test]
2160 fn includes_target() {
2161 let mut tracer = TestTracer::default();
2162 let subscriber = tracing_subscriber::registry()
2163 .with(layer().with_tracer(tracer.clone()).with_target(true));
2164
2165 tracing::subscriber::with_default(subscriber, || {
2166 tracing::debug_span!("request");
2167 });
2168
2169 let attributes = tracer.attributes();
2170 let keys = attributes.keys().map(|k| k.as_str()).collect::<Vec<&str>>();
2171 assert!(keys.contains(&"target"));
2172 }
2173
2174 #[test]
2175 fn excludes_target() {
2176 let mut tracer = TestTracer::default();
2177 let subscriber = tracing_subscriber::registry()
2178 .with(layer().with_tracer(tracer.clone()).with_target(false));
2179
2180 tracing::subscriber::with_default(subscriber, || {
2181 tracing::debug_span!("request");
2182 });
2183
2184 let attributes = tracer.attributes();
2185 let keys = attributes.keys().map(|k| k.as_str()).collect::<Vec<&str>>();
2186 assert!(!keys.contains(&"target"));
2187 }
2188
2189 #[test]
2190 fn propagates_error_fields_from_event_to_span_with_context_activation() {
2191 propagates_error_fields_from_event_to_span_impl(true);
2192 }
2193
2194 #[test]
2195 fn propagates_error_fields_from_event_to_span_no_context_activation() {
2196 propagates_error_fields_from_event_to_span_impl(false);
2197 }
2198
2199 fn propagates_error_fields_from_event_to_span_impl(context_activation: bool) {
2200 let mut tracer = TestTracer::default();
2201 let subscriber = tracing_subscriber::registry().with(
2202 layer()
2203 .with_tracer(tracer.clone())
2204 .with_context_activation(context_activation),
2205 );
2206
2207 let err = TestDynError::new("base error")
2208 .with_parent("intermediate error")
2209 .with_parent("user error");
2210
2211 tracing::subscriber::with_default(subscriber, || {
2212 let _guard = tracing::debug_span!("request",).entered();
2213
2214 tracing::error!(
2215 error = &err as &(dyn std::error::Error + 'static),
2216 "request error!"
2217 )
2218 });
2219
2220 let attributes = tracer.attributes();
2221
2222 assert_eq!(attributes[FIELD_EXCEPTION_MESSAGE].as_str(), "user error");
2223 assert_eq!(
2224 attributes[FIELD_EXCEPTION_STACKTRACE],
2225 Value::Array(
2226 vec![
2227 StringValue::from("intermediate error"),
2228 StringValue::from("base error")
2229 ]
2230 .into()
2231 )
2232 );
2233 }
2234
2235 #[test]
2236 fn propagates_no_error_fields_from_event_to_span_with_context_activation() {
2237 propagates_no_error_fields_from_event_to_span_impl(true);
2238 }
2239
2240 #[test]
2241 fn propagates_no_error_fields_from_event_to_span_no_context_activation() {
2242 propagates_no_error_fields_from_event_to_span_impl(false);
2243 }
2244
2245 fn propagates_no_error_fields_from_event_to_span_impl(context_activation: bool) {
2246 let mut tracer = TestTracer::default();
2247 let subscriber = tracing_subscriber::registry().with(
2248 layer()
2249 .with_error_fields_to_exceptions(false)
2250 .with_tracer(tracer.clone())
2251 .with_context_activation(context_activation),
2252 );
2253
2254 let err = TestDynError::new("base error")
2255 .with_parent("intermediate error")
2256 .with_parent("user error");
2257
2258 tracing::subscriber::with_default(subscriber, || {
2259 let _guard = tracing::debug_span!("request",).entered();
2260
2261 tracing::error!(
2262 error = &err as &(dyn std::error::Error + 'static),
2263 "request error!"
2264 )
2265 });
2266
2267 let attributes = tracer.attributes();
2268
2269 assert_eq!(attributes[FIELD_EXCEPTION_MESSAGE].as_str(), "user error");
2270 assert_eq!(
2271 attributes[FIELD_EXCEPTION_STACKTRACE],
2272 Value::Array(
2273 vec![
2274 StringValue::from("intermediate error"),
2275 StringValue::from("base error")
2276 ]
2277 .into()
2278 )
2279 );
2280 }
2281
2282 #[test]
2283 fn tracing_error_compatibility_with_context_activation() {
2284 tracing_error_compatibility_impl(true);
2285 }
2286
2287 #[test]
2288 fn tracing_error_compatibility_no_context_activation() {
2289 tracing_error_compatibility_impl(false);
2290 }
2291
2292 fn tracing_error_compatibility_impl(context_activation: bool) {
2293 let tracer = TestTracer::default();
2294 let subscriber = tracing_subscriber::registry()
2295 .with(
2296 layer()
2297 .with_error_fields_to_exceptions(false)
2298 .with_tracer(tracer.clone())
2299 .with_context_activation(context_activation),
2300 )
2301 .with(tracing_error::ErrorLayer::default());
2302
2303 tracing::subscriber::with_default(subscriber, || {
2304 let span = tracing::info_span!("Blows up!", exception = tracing::field::Empty);
2305 let _entered = span.enter();
2306 let context = tracing_error::SpanTrace::capture();
2307
2308 span.record("exception", tracing::field::debug(&context));
2310 tracing::info!(exception = &tracing::field::debug(&context), "hello");
2312 });
2313
2314 }
2316
2317 #[derive(Debug, PartialEq)]
2318 struct ValueA(&'static str);
2319 #[derive(Debug, PartialEq)]
2320 struct ValueB(&'static str);
2321
2322 #[test]
2323 fn otel_context_propagation() {
2324 use opentelemetry::trace::Tracer;
2325 use tracing::span;
2326
2327 let mut tracer = TestTracer::default();
2328 let subscriber = tracing_subscriber::registry().with(layer().with_tracer(tracer.clone()));
2329
2330 tracing::subscriber::with_default(subscriber, || {
2331 let _outer_guard =
2333 OtelContext::attach(OtelContext::default().with_value(ValueA("outer")));
2334 assert_eq!(OtelContext::current().get(), Some(&ValueA("outer")));
2335 let root = span!(tracing::Level::TRACE, "tokio-tracing-span-parent");
2336 drop(_outer_guard);
2338 assert!(OtelContext::current().get::<ValueA>().is_none());
2339 let _enter_root = root.enter();
2341 assert_eq!(OtelContext::current().get(), Some(&ValueA("outer")));
2342 let _inner_guard =
2344 OtelContext::attach(OtelContext::current_with_value(ValueB("inner")));
2345 assert_eq!(OtelContext::current().get(), Some(&ValueA("outer")));
2346 assert_eq!(OtelContext::current().get(), Some(&ValueB("inner")));
2347 let child = span!(tracing::Level::TRACE, "tokio-tracing-span-child");
2348 drop(_inner_guard);
2350 assert_eq!(OtelContext::current().get(), Some(&ValueA("outer")));
2351 assert!(OtelContext::current().get::<ValueB>().is_none());
2352 let _enter_child = child.enter();
2354 assert_eq!(OtelContext::current().get(), Some(&ValueA("outer")));
2355 assert_eq!(OtelContext::current().get(), Some(&ValueB("inner")));
2356 let span = tracer
2359 .tracer
2360 .span_builder("otel-tracing-span")
2361 .start(&tracer);
2362 let _otel_guard = OtelContext::attach(OtelContext::current_with_span(span));
2363 let child2 = span!(tracing::Level::TRACE, "tokio-tracing-span-child2");
2364 drop(_otel_guard);
2365 drop(_enter_child);
2367 assert_eq!(OtelContext::current().get(), Some(&ValueA("outer")));
2368 assert!(OtelContext::current().get::<ValueB>().is_none());
2369 drop(_enter_root);
2371 assert!(OtelContext::current().get::<ValueA>().is_none());
2372 assert!(OtelContext::current().get::<ValueB>().is_none());
2373 let _ = child2.enter();
2374 });
2375
2376 let spans = tracer.spans();
2378 let parent = spans
2379 .iter()
2380 .find(|span| span.name == "tokio-tracing-span-parent")
2381 .unwrap();
2382 let child = spans
2383 .iter()
2384 .find(|span| span.name == "tokio-tracing-span-child")
2385 .unwrap();
2386 let child2 = spans
2387 .iter()
2388 .find(|span| span.name == "tokio-tracing-span-child2")
2389 .unwrap();
2390 let otel = spans
2391 .iter()
2392 .find(|span| span.name == "otel-tracing-span")
2393 .unwrap();
2394 assert_eq!(parent.parent_span_id, otel::SpanId::INVALID);
2396 assert_eq!(child.parent_span_id, parent.span_context.span_id());
2398 assert_eq!(otel.parent_span_id, child.span_context.span_id());
2400 assert_eq!(child2.parent_span_id, otel.span_context.span_id());
2402 }
2403
2404 #[test]
2405 fn parent_context() {
2406 let mut tracer = TestTracer::default();
2407 let subscriber = tracing_subscriber::registry().with(layer().with_tracer(tracer.clone()));
2408
2409 tracing::subscriber::with_default(subscriber, || {
2410 let root = trace_span!("root");
2411
2412 let child1 = trace_span!("child-1");
2413 let root_context = root.context(); let _ = child1.set_parent(root_context); let _enter_root = root.enter();
2417 drop(_enter_root);
2418
2419 let child2 = trace_span!("child-2");
2420 let _ = child2.set_parent(root.context());
2421 });
2422
2423 let spans = tracer.spans();
2425 let parent = spans.iter().find(|span| span.name == "root").unwrap();
2426 let child1 = spans.iter().find(|span| span.name == "child-1").unwrap();
2427 let child2 = spans.iter().find(|span| span.name == "child-2").unwrap();
2428 assert_eq!(parent.parent_span_id, otel::SpanId::INVALID);
2429 assert_eq!(child1.parent_span_id, parent.span_context.span_id());
2430 assert_eq!(child2.parent_span_id, parent.span_context.span_id());
2431 }
2432
2433 #[test]
2434 fn record_after_with_context_activation() {
2435 record_after_impl(true);
2436 }
2437
2438 #[test]
2439 fn record_after_no_context_activation() {
2440 record_after_impl(false);
2441 }
2442
2443 fn record_after_impl(context_activation: bool) {
2444 let mut tracer = TestTracer::default();
2445 let subscriber = tracing_subscriber::registry().with(
2446 layer()
2447 .with_tracer(tracer.clone())
2448 .with_context_activation(context_activation),
2449 );
2450
2451 tracing::subscriber::with_default(subscriber, || {
2452 let root = trace_span!("root", before = "before", after = "before");
2453
2454 root.record("before", "after");
2456
2457 let _enter_root = root.enter();
2459 drop(_enter_root);
2460
2461 root.record("after", "after");
2463 });
2464
2465 let spans = tracer.spans();
2468 let parent = spans.iter().find(|span| span.name == "root").unwrap();
2469 assert_eq!(parent.parent_span_id, otel::SpanId::INVALID);
2470 assert!(parent
2471 .attributes
2472 .iter()
2473 .filter(|kv| kv.key.as_str() == "before")
2474 .any(|kv| kv.value.as_str() == "after"));
2475
2476 assert!(parent
2477 .attributes
2478 .iter()
2479 .filter(|kv| kv.key.as_str() == "after")
2480 .any(|kv| kv.value.as_str() == "after"));
2481 }
2482
2483 #[test]
2484 fn parent_context_2() {
2485 let mut tracer = TestTracer::default();
2486 let subscriber = tracing_subscriber::registry().with(layer().with_tracer(tracer.clone()));
2487
2488 tracing::subscriber::with_default(subscriber, || {
2489 let root = trace_span!("root");
2490 _ = root.enter();
2491
2492 let child1 = trace_span!("child-1");
2493 let _ = child1.set_parent(root.context());
2494
2495 trace_span!(parent: &child1, "child-2");
2496 let _ = child1.set_parent(root.context()); trace_span!(parent: &child1, "child-3");
2499 });
2500
2501 let spans = tracer.spans();
2503 let root = spans.iter().find(|span| span.name == "root").unwrap();
2504 let child1 = spans.iter().find(|span| span.name == "child-1").unwrap();
2505 let child2 = spans.iter().find(|span| span.name == "child-2").unwrap();
2506 let child3 = spans.iter().find(|span| span.name == "child-3").unwrap();
2507 assert_eq!(root.parent_span_id, otel::SpanId::INVALID);
2508 assert_eq!(child1.parent_span_id, root.span_context.span_id());
2509 assert_eq!(child2.parent_span_id, child1.span_context.span_id());
2510
2511 assert_eq!(child3.parent_span_id, child1.span_context.span_id());
2513 }
2514
2515 #[test]
2516 fn follows_from_adds_link_with_context_activation() {
2517 follows_from_adds_link_impl(true);
2518 }
2519
2520 #[test]
2521 fn follows_from_adds_link_no_context_activation() {
2522 follows_from_adds_link_impl(false);
2523 }
2524
2525 fn follows_from_adds_link_impl(context_activation: bool) {
2526 use crate::OpenTelemetrySpanExt;
2527 let mut tracer = TestTracer::default();
2528 let subscriber = tracing_subscriber::registry().with(
2529 layer()
2530 .with_tracer(tracer.clone())
2531 .with_context_activation(context_activation),
2532 );
2533
2534 let span1_id = tracing::subscriber::with_default(subscriber, || {
2535 let span2 = tracing::debug_span!("span2");
2536 let span1 = tracing::debug_span!("span1");
2537
2538 let _ = span2.context();
2540
2541 span2.follows_from(&span1);
2543
2544 let _guard = span2.enter();
2546
2547 span1.context().span().span_context().span_id()
2549 });
2550
2551 let spans = tracer.spans();
2552 assert_eq!(spans.len(), 2, "Expected two spans to be exported");
2554 assert!(spans.iter().any(|span| span.name == "span1"));
2555 let span2 = spans
2556 .iter()
2557 .find(|span| span.name == "span2")
2558 .expect("Expected span2 to be exported");
2559
2560 let links = span2
2562 .links
2563 .iter()
2564 .map(|link| link.span_context.span_id())
2565 .collect::<Vec<_>>();
2566
2567 assert_eq!(
2569 links.len(),
2570 1,
2571 "Expected span to have one link from follows_from relationship"
2572 );
2573
2574 assert!(
2575 links.contains(&span1_id),
2576 "Link should point to the correct source span"
2577 );
2578 }
2579
2580 #[test]
2581 fn follows_from_multiple_links_with_context_activation() {
2582 follows_from_multiple_links_impl(true);
2583 }
2584
2585 #[test]
2586 fn follows_from_multiple_links_no_context_activation() {
2587 follows_from_multiple_links_impl(false);
2588 }
2589
2590 fn follows_from_multiple_links_impl(context_activation: bool) {
2591 use crate::OpenTelemetrySpanExt;
2592 let mut tracer = TestTracer::default();
2593 let subscriber = tracing_subscriber::registry().with(
2594 layer()
2595 .with_tracer(tracer.clone())
2596 .with_context_activation(context_activation),
2597 );
2598
2599 let (span1_id, span2_id) = tracing::subscriber::with_default(subscriber, || {
2600 let span3 = tracing::debug_span!("span3");
2601 let span2 = tracing::debug_span!("span2");
2602 let span1 = tracing::debug_span!("span1");
2603
2604 span3.follows_from(&span1);
2606 span3.follows_from(&span2);
2607
2608 let _guard = span3.enter();
2610
2611 (
2613 span1.context().span().span_context().span_id(),
2614 span2.context().span().span_context().span_id(),
2615 )
2616 });
2617
2618 let spans = tracer.spans();
2619 assert_eq!(spans.len(), 3, "Expected three spans to be exported");
2621 assert!(spans.iter().any(|span| span.name == "span1"));
2622 assert!(spans.iter().any(|span| span.name == "span2"));
2623 let span3 = spans
2624 .iter()
2625 .find(|span| span.name == "span3")
2626 .expect("Expected span3 to be exported");
2627
2628 let links = span3
2630 .links
2631 .iter()
2632 .map(|link| link.span_context.span_id())
2633 .collect::<Vec<_>>();
2634
2635 assert_eq!(
2637 links.len(),
2638 2,
2639 "Expected span to have two links from follows_from relationships"
2640 );
2641
2642 assert!(
2644 links[0] == span1_id && links[1] == span2_id,
2645 "Links should point to the correct source spans"
2646 );
2647 }
2648
2649 #[test]
2650 fn context_activation_disabled() {
2651 use tracing::span;
2652
2653 let mut tracer = TestTracer::default();
2654 let subscriber = tracing_subscriber::registry().with(
2655 layer()
2656 .with_tracer(tracer.clone())
2657 .with_context_activation(false),
2658 );
2659
2660 tracing::subscriber::with_default(subscriber, || {
2661 let _outer_guard =
2663 OtelContext::attach(OtelContext::default().with_value(ValueA("outer")));
2664 assert_eq!(OtelContext::current().get(), Some(&ValueA("outer")));
2665
2666 let root = span!(tracing::Level::TRACE, "tokio-tracing-span-parent");
2667
2668 drop(_outer_guard);
2670 assert!(OtelContext::current().get::<ValueA>().is_none());
2671
2672 let _enter_root = root.enter();
2675 assert!(OtelContext::current().get::<ValueA>().is_none());
2676
2677 let _inner_guard =
2679 OtelContext::attach(OtelContext::current_with_value(ValueB("inner")));
2680 assert!(OtelContext::current().get::<ValueA>().is_none());
2681 assert_eq!(OtelContext::current().get(), Some(&ValueB("inner")));
2682
2683 let child = span!(tracing::Level::TRACE, "tokio-tracing-span-child");
2684
2685 drop(_inner_guard);
2687 assert!(OtelContext::current().get::<ValueA>().is_none());
2688 assert!(OtelContext::current().get::<ValueB>().is_none());
2689
2690 let _enter_child = child.enter();
2693 assert!(OtelContext::current().get::<ValueA>().is_none());
2694 assert!(OtelContext::current().get::<ValueB>().is_none());
2695 });
2696
2697 let spans = tracer.spans();
2699 assert_eq!(spans.len(), 2);
2700 assert!(spans
2701 .iter()
2702 .any(|span| span.name == "tokio-tracing-span-parent"));
2703 assert!(spans
2704 .iter()
2705 .any(|span| span.name == "tokio-tracing-span-child"));
2706 }
2707}