1use crate::stack::IdValueStack;
2use crate::{OtelData, OtelDataLock, 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::{Cell, RefCell};
10use std::sync::Arc;
11use std::thread;
12#[cfg(not(all(target_arch = "wasm32", not(target_os = "wasi"))))]
13use std::time::Instant;
14use std::{any::TypeId, borrow::Cow};
15use std::{fmt, vec};
16use std::{marker, mem::take};
17use tracing_core::span::{self, Attributes, Id, Record};
18use tracing_core::{field, Event, Subscriber};
19#[cfg(feature = "tracing-log")]
20use tracing_log::NormalizeEvent;
21use tracing_subscriber::layer::Context;
22use tracing_subscriber::layer::Filter;
23use tracing_subscriber::registry::LookupSpan;
24use tracing_subscriber::Layer;
25#[cfg(all(target_arch = "wasm32", not(target_os = "wasi")))]
26use web_time::Instant;
27
28mod filtered;
29
30const SPAN_NAME_FIELD: &str = "otel.name";
31const SPAN_KIND_FIELD: &str = "otel.kind";
32const SPAN_STATUS_CODE_FIELD: &str = "otel.status_code";
33const SPAN_STATUS_DESCRIPTION_FIELD: &str = "otel.status_description";
34const SPAN_EVENT_COUNT_FIELD: &str = "otel.tracing_event_count";
35
36const EVENT_EXCEPTION_NAME: &str = "exception";
37const FIELD_EXCEPTION_MESSAGE: &str = "exception.message";
38const FIELD_EXCEPTION_STACKTRACE: &str = "exception.stacktrace";
39
40std::thread_local! {
41 static INSIDE_TRACING: Cell<bool> = const { Cell::new(false) };
42}
43
44fn prevent_reentrant_call<T>(f: impl FnOnce() -> T) -> T {
45 let old_value = INSIDE_TRACING.with(|inside| inside.replace(true));
46 #[cfg(all(test, __reentrant_tracing_test))]
47 tracing::info!("This should not deadlock...");
48 let result = f();
49 INSIDE_TRACING.with(|inside| inside.set(old_value));
50 result
51}
52
53pub struct OpenTelemetryLayer<S, T> {
59 tracer: T,
60 location: bool,
61 tracked_inactivity: bool,
62 with_threads: bool,
63 with_level: bool,
64 with_target: bool,
65 context_activation: bool,
66 sem_conv_config: SemConvConfig,
67 with_context: WithContext,
68 _registry: marker::PhantomData<S>,
69}
70
71impl<S> Default for OpenTelemetryLayer<S, noop::NoopTracer>
72where
73 S: Subscriber + for<'span> LookupSpan<'span>,
74{
75 fn default() -> Self {
76 OpenTelemetryLayer::new(noop::NoopTracer::new())
77 }
78}
79
80pub fn layer<S>() -> OpenTelemetryLayer<S, noop::NoopTracer>
105where
106 S: Subscriber + for<'span> LookupSpan<'span>,
107{
108 OpenTelemetryLayer::default()
109}
110
111pub(crate) struct WithContext {
122 #[allow(clippy::type_complexity)]
126 pub(crate) with_context: fn(&tracing::Dispatch, &span::Id, f: &mut dyn FnMut(&mut OtelData)),
127
128 #[allow(clippy::type_complexity)]
133 pub(crate) with_activated_context:
134 fn(&tracing::Dispatch, &span::Id, f: &mut dyn FnMut(&mut OtelData)),
135
136 #[allow(clippy::type_complexity)]
141 pub(crate) with_activated_otel_context:
142 fn(&tracing::Dispatch, &span::Id, f: &mut dyn FnMut(&OtelContext)),
143}
144
145impl WithContext {
146 pub(crate) fn with_context(
150 &self,
151 dispatch: &tracing::Dispatch,
152 id: &span::Id,
153 mut f: impl FnMut(&mut OtelData),
154 ) {
155 (self.with_context)(dispatch, id, &mut f)
156 }
157
158 pub(crate) fn with_activated_context(
165 &self,
166 dispatch: &tracing::Dispatch,
167 id: &span::Id,
168 mut f: impl FnMut(&mut OtelData),
169 ) {
170 (self.with_activated_context)(dispatch, id, &mut f)
171 }
172
173 #[allow(clippy::type_complexity)]
178 pub(crate) fn with_activated_otel_context(
179 &self,
180 dispatch: &tracing::Dispatch,
181 id: &span::Id,
182 mut f: impl FnMut(&OtelContext),
183 ) {
184 (self.with_activated_otel_context)(dispatch, id, &mut f)
185 }
186}
187
188fn str_to_span_kind(s: &str) -> Option<otel::SpanKind> {
189 match s {
190 s if s.eq_ignore_ascii_case("server") => Some(otel::SpanKind::Server),
191 s if s.eq_ignore_ascii_case("client") => Some(otel::SpanKind::Client),
192 s if s.eq_ignore_ascii_case("producer") => Some(otel::SpanKind::Producer),
193 s if s.eq_ignore_ascii_case("consumer") => Some(otel::SpanKind::Consumer),
194 s if s.eq_ignore_ascii_case("internal") => Some(otel::SpanKind::Internal),
195 _ => None,
196 }
197}
198
199fn str_to_status(s: &str) -> otel::Status {
200 match s {
201 s if s.eq_ignore_ascii_case("ok") => otel::Status::Ok,
202 s if s.eq_ignore_ascii_case("error") => otel::Status::error(""),
203 _ => otel::Status::Unset,
204 }
205}
206
207#[derive(Default)]
208struct SpanBuilderUpdates {
209 name: Option<Cow<'static, str>>,
210 span_kind: Option<SpanKind>,
211 status: Option<Status>,
212 attributes: Option<Vec<KeyValue>>,
213}
214
215impl SpanBuilderUpdates {
216 fn update(self, span_builder: &mut SpanBuilder, s: &mut Status) {
217 let Self {
218 name,
219 span_kind,
220 status,
221 attributes,
222 } = self;
223
224 if let Some(name) = name {
225 span_builder.name = name;
226 }
227 if let Some(span_kind) = span_kind {
228 span_builder.span_kind = Some(span_kind);
229 }
230 if let Some(status) = status {
231 *s = status;
232 }
233 if let Some(attributes) = attributes {
234 if let Some(builder_attributes) = &mut span_builder.attributes {
235 builder_attributes.extend(attributes);
236 } else {
237 span_builder.attributes = Some(attributes);
238 }
239 }
240 }
241
242 fn update_span(self, span: &opentelemetry::trace::SpanRef<'_>) {
243 let Self {
244 status, attributes, ..
245 } = self;
246
247 if let Some(status) = status {
248 span.set_status(status);
249 }
250 if let Some(attributes) = attributes {
251 span.set_attributes(attributes);
252 }
253 }
254}
255
256struct SpanEventVisitor<'a, 'b> {
257 event_builder: &'a mut otel::Event,
258 span_builder_updates: &'b mut Option<SpanBuilderUpdates>,
259 sem_conv_config: SemConvConfig,
260}
261
262impl field::Visit for SpanEventVisitor<'_, '_> {
263 fn record_bool(&mut self, field: &field::Field, value: bool) {
267 match field.name() {
268 "message" => self.event_builder.name = value.to_string().into(),
269 #[cfg(feature = "tracing-log")]
271 name if name.starts_with("log.") => (),
272 name => {
273 self.event_builder
274 .attributes
275 .push(KeyValue::new(name, value));
276 }
277 }
278 }
279
280 fn record_f64(&mut self, field: &field::Field, value: f64) {
284 match field.name() {
285 "message" => self.event_builder.name = value.to_string().into(),
286 #[cfg(feature = "tracing-log")]
288 name if name.starts_with("log.") => (),
289 name => {
290 self.event_builder
291 .attributes
292 .push(KeyValue::new(name, value));
293 }
294 }
295 }
296
297 fn record_i64(&mut self, field: &field::Field, value: i64) {
301 match field.name() {
302 "message" => self.event_builder.name = value.to_string().into(),
303 #[cfg(feature = "tracing-log")]
305 name if name.starts_with("log.") => (),
306 name => {
307 self.event_builder
308 .attributes
309 .push(KeyValue::new(name, value));
310 }
311 }
312 }
313
314 fn record_str(&mut self, field: &field::Field, value: &str) {
318 match field.name() {
319 "message" => self.event_builder.name = value.to_string().into(),
320 "error" if self.event_builder.name.is_empty() => {
324 if self.sem_conv_config.error_events_to_status {
325 self.span_builder_updates
326 .get_or_insert_with(SpanBuilderUpdates::default)
327 .status
328 .replace(otel::Status::error(format!("{value:?}")));
329 }
330 if self.sem_conv_config.error_events_to_exceptions {
331 self.event_builder.name = EVENT_EXCEPTION_NAME.into();
332 self.event_builder
333 .attributes
334 .push(KeyValue::new(FIELD_EXCEPTION_MESSAGE, format!("{value:?}")));
335 } else {
336 self.event_builder
337 .attributes
338 .push(KeyValue::new("error", format!("{value:?}")));
339 }
340 }
341 #[cfg(feature = "tracing-log")]
343 name if name.starts_with("log.") => (),
344 name => {
345 self.event_builder
346 .attributes
347 .push(KeyValue::new(name, value.to_string()));
348 }
349 }
350 }
351
352 fn record_debug(&mut self, field: &field::Field, value: &dyn fmt::Debug) {
357 match field.name() {
358 "message" => self.event_builder.name = format!("{value:?}").into(),
359 "error" if self.event_builder.name.is_empty() => {
363 if self.sem_conv_config.error_events_to_status {
364 self.span_builder_updates
365 .get_or_insert_with(SpanBuilderUpdates::default)
366 .status
367 .replace(otel::Status::error(format!("{value:?}")));
368 }
369 if self.sem_conv_config.error_events_to_exceptions {
370 self.event_builder.name = EVENT_EXCEPTION_NAME.into();
371 self.event_builder
372 .attributes
373 .push(KeyValue::new(FIELD_EXCEPTION_MESSAGE, format!("{value:?}")));
374 } else {
375 self.event_builder
376 .attributes
377 .push(KeyValue::new("error", format!("{value:?}")));
378 }
379 }
380 #[cfg(feature = "tracing-log")]
382 name if name.starts_with("log.") => (),
383 name => {
384 self.event_builder
385 .attributes
386 .push(KeyValue::new(name, format!("{value:?}")));
387 }
388 }
389 }
390
391 fn record_error(
396 &mut self,
397 field: &tracing_core::Field,
398 value: &(dyn std::error::Error + 'static),
399 ) {
400 let mut chain: Vec<StringValue> = Vec::new();
401 let mut next_err = value.source();
402
403 while let Some(err) = next_err {
404 chain.push(err.to_string().into());
405 next_err = err.source();
406 }
407
408 let error_msg = value.to_string();
409
410 if self.sem_conv_config.error_fields_to_exceptions {
411 self.event_builder.attributes.push(KeyValue::new(
412 Key::new(FIELD_EXCEPTION_MESSAGE),
413 Value::String(StringValue::from(error_msg.clone())),
414 ));
415
416 self.event_builder.attributes.push(KeyValue::new(
423 Key::new(FIELD_EXCEPTION_STACKTRACE),
424 Value::Array(chain.clone().into()),
425 ));
426 }
427
428 if self.sem_conv_config.error_records_to_exceptions {
429 let attributes = self
430 .span_builder_updates
431 .get_or_insert_with(SpanBuilderUpdates::default)
432 .attributes
433 .get_or_insert_with(Vec::new);
434
435 attributes.push(KeyValue::new(
436 FIELD_EXCEPTION_MESSAGE,
437 Value::String(error_msg.clone().into()),
438 ));
439
440 attributes.push(KeyValue::new(
447 FIELD_EXCEPTION_STACKTRACE,
448 Value::Array(chain.clone().into()),
449 ));
450 }
451
452 self.event_builder.attributes.push(KeyValue::new(
453 Key::new(field.name()),
454 Value::String(StringValue::from(error_msg)),
455 ));
456 self.event_builder.attributes.push(KeyValue::new(
457 Key::new(format!("{}.chain", field.name())),
458 Value::Array(chain.into()),
459 ));
460 }
461}
462
463#[derive(Clone, Copy)]
465struct SemConvConfig {
466 error_fields_to_exceptions: bool,
471
472 error_records_to_exceptions: bool,
477
478 error_events_to_status: bool,
487
488 error_events_to_exceptions: bool,
496}
497
498struct SpanAttributeVisitor<'a> {
499 span_builder_updates: &'a mut SpanBuilderUpdates,
500 sem_conv_config: SemConvConfig,
501}
502
503impl SpanAttributeVisitor<'_> {
504 fn record(&mut self, attribute: KeyValue) {
505 self.span_builder_updates
506 .attributes
507 .get_or_insert_with(Vec::new)
508 .push(KeyValue::new(attribute.key, attribute.value));
509 }
510}
511
512impl field::Visit for SpanAttributeVisitor<'_> {
513 fn record_bool(&mut self, field: &field::Field, value: bool) {
517 self.record(KeyValue::new(field.name(), value));
518 }
519
520 fn record_f64(&mut self, field: &field::Field, value: f64) {
524 self.record(KeyValue::new(field.name(), value));
525 }
526
527 fn record_i64(&mut self, field: &field::Field, value: i64) {
531 self.record(KeyValue::new(field.name(), value));
532 }
533
534 fn record_str(&mut self, field: &field::Field, value: &str) {
538 match field.name() {
539 SPAN_NAME_FIELD => self.span_builder_updates.name = Some(value.to_string().into()),
540 SPAN_KIND_FIELD => self.span_builder_updates.span_kind = str_to_span_kind(value),
541 SPAN_STATUS_CODE_FIELD => self.span_builder_updates.status = Some(str_to_status(value)),
542 SPAN_STATUS_DESCRIPTION_FIELD => {
543 self.span_builder_updates.status = Some(otel::Status::error(value.to_string()))
544 }
545 _ => self.record(KeyValue::new(field.name(), value.to_string())),
546 }
547 }
548
549 fn record_debug(&mut self, field: &field::Field, value: &dyn fmt::Debug) {
554 match field.name() {
555 SPAN_NAME_FIELD => self.span_builder_updates.name = Some(format!("{value:?}").into()),
556 SPAN_KIND_FIELD => {
557 self.span_builder_updates.span_kind = str_to_span_kind(&format!("{value:?}"))
558 }
559 SPAN_STATUS_CODE_FIELD => {
560 self.span_builder_updates.status = Some(str_to_status(&format!("{value:?}")))
561 }
562 SPAN_STATUS_DESCRIPTION_FIELD => {
563 self.span_builder_updates.status = Some(otel::Status::error(format!("{value:?}")))
564 }
565 _ => self.record(KeyValue::new(
566 Key::new(field.name()),
567 Value::String(format!("{value:?}").into()),
568 )),
569 }
570 }
571
572 fn record_error(
577 &mut self,
578 field: &tracing_core::Field,
579 value: &(dyn std::error::Error + 'static),
580 ) {
581 let mut chain: Vec<StringValue> = Vec::new();
582 let mut next_err = value.source();
583
584 while let Some(err) = next_err {
585 chain.push(err.to_string().into());
586 next_err = err.source();
587 }
588
589 let error_msg = value.to_string();
590
591 if self.sem_conv_config.error_fields_to_exceptions {
592 self.record(KeyValue::new(
593 Key::new(FIELD_EXCEPTION_MESSAGE),
594 Value::from(error_msg.clone()),
595 ));
596
597 self.record(KeyValue::new(
604 Key::new(FIELD_EXCEPTION_STACKTRACE),
605 Value::Array(chain.clone().into()),
606 ));
607 }
608
609 self.record(KeyValue::new(
610 Key::new(field.name()),
611 Value::String(error_msg.into()),
612 ));
613 self.record(KeyValue::new(
614 Key::new(format!("{}.chain", field.name())),
615 Value::Array(chain.into()),
616 ));
617 }
618}
619
620impl<S, T> OpenTelemetryLayer<S, T>
621where
622 S: Subscriber + for<'span> LookupSpan<'span>,
623 T: otel::Tracer + 'static,
624 T::Span: Send + Sync,
625{
626 pub fn new(tracer: T) -> Self {
661 OpenTelemetryLayer {
662 tracer,
663 location: true,
664 tracked_inactivity: true,
665 with_threads: true,
666 with_level: false,
667 with_target: true,
668 context_activation: true,
669 sem_conv_config: SemConvConfig {
670 error_fields_to_exceptions: true,
671 error_records_to_exceptions: true,
672 error_events_to_exceptions: true,
673 error_events_to_status: true,
674 },
675 with_context: WithContext {
676 with_context: Self::get_context,
677 with_activated_context: Self::get_activated_context,
678 with_activated_otel_context: Self::get_activated_otel_context,
679 },
680 _registry: marker::PhantomData,
681 }
682 }
683
684 pub fn with_tracer<Tracer>(self, tracer: Tracer) -> OpenTelemetryLayer<S, Tracer>
718 where
719 Tracer: otel::Tracer + 'static,
720 Tracer::Span: Send + Sync,
721 {
722 OpenTelemetryLayer {
723 tracer,
724 location: self.location,
725 tracked_inactivity: self.tracked_inactivity,
726 with_threads: self.with_threads,
727 with_level: self.with_level,
728 with_target: self.with_target,
729 context_activation: self.context_activation,
730 sem_conv_config: self.sem_conv_config,
731 with_context: WithContext {
732 with_context: OpenTelemetryLayer::<S, Tracer>::get_context,
733 with_activated_context: OpenTelemetryLayer::<S, Tracer>::get_activated_context,
734 with_activated_otel_context:
735 OpenTelemetryLayer::<S, Tracer>::get_activated_otel_context,
736 },
737 _registry: self._registry,
738 }
740 }
741
742 pub fn with_error_fields_to_exceptions(self, error_fields_to_exceptions: bool) -> Self {
758 Self {
759 sem_conv_config: SemConvConfig {
760 error_fields_to_exceptions,
761 ..self.sem_conv_config
762 },
763 ..self
764 }
765 }
766
767 pub fn with_error_events_to_status(self, error_events_to_status: bool) -> Self {
772 Self {
773 sem_conv_config: SemConvConfig {
774 error_events_to_status,
775 ..self.sem_conv_config
776 },
777 ..self
778 }
779 }
780
781 pub fn with_error_events_to_exceptions(self, error_events_to_exceptions: bool) -> Self {
792 Self {
793 sem_conv_config: SemConvConfig {
794 error_events_to_exceptions,
795 ..self.sem_conv_config
796 },
797 ..self
798 }
799 }
800
801 pub fn with_error_records_to_exceptions(self, error_records_to_exceptions: bool) -> Self {
817 Self {
818 sem_conv_config: SemConvConfig {
819 error_records_to_exceptions,
820 ..self.sem_conv_config
821 },
822 ..self
823 }
824 }
825
826 pub fn with_location(self, location: bool) -> Self {
836 Self { location, ..self }
837 }
838
839 pub fn with_tracked_inactivity(self, tracked_inactivity: bool) -> Self {
845 Self {
846 tracked_inactivity,
847 ..self
848 }
849 }
850
851 pub fn with_threads(self, threads: bool) -> Self {
859 Self {
860 with_threads: threads,
861 ..self
862 }
863 }
864
865 pub fn with_level(self, level: bool) -> Self {
872 Self {
873 with_level: level,
874 ..self
875 }
876 }
877
878 pub fn with_target(self, target: bool) -> Self {
882 Self {
883 with_target: target,
884 ..self
885 }
886 }
887
888 pub fn with_context_activation(self, context_activation: bool) -> Self {
896 Self {
897 context_activation,
898 ..self
899 }
900 }
901
902 pub fn with_counting_event_filter<F: Filter<S>>(
908 self,
909 filter: F,
910 ) -> FilteredOpenTelemetryLayer<S, T, F> {
911 FilteredOpenTelemetryLayer::new(self, filter)
912 }
913
914 fn parent_context(&self, attrs: &Attributes<'_>, ctx: &Context<'_, S>) -> OtelContext {
922 if let Some(parent) = attrs.parent() {
923 if let Some(span) = ctx.span(parent) {
936 let extensions = span.extensions();
937 if let Some(otel_data) = extensions.get::<OtelDataLock>() {
938 return self.with_started_cx(&mut otel_data.lock(), &|cx| cx.clone());
941 }
942 }
943 }
944
945 if attrs.is_contextual() {
946 if self.context_activation {
947 OtelContext::current()
950 } else {
951 ctx.lookup_current()
954 .and_then(|span| {
955 let extensions = span.extensions();
956 extensions
957 .get::<OtelDataLock>()
958 .map(|data| self.with_started_cx(&mut data.lock(), &|cx| cx.clone()))
959 })
960 .unwrap_or_else(OtelContext::current)
961 }
962 } else {
963 OtelContext::default()
964 }
965 }
966
967 fn get_context(dispatch: &tracing::Dispatch, id: &span::Id, f: &mut dyn FnMut(&mut OtelData)) {
980 let subscriber = dispatch
981 .downcast_ref::<S>()
982 .expect("subscriber should downcast to expected type; this is a bug!");
983 let span = subscriber
984 .span(id)
985 .expect("registry should have a span for the current ID");
986
987 let otel_data = span.extensions().get::<OtelDataLock>().cloned();
988 if let Some(otel_data) = otel_data {
989 f(&mut otel_data.lock());
990 }
991 }
992
993 fn get_activated_context(
1006 dispatch: &tracing::Dispatch,
1007 id: &span::Id,
1008 f: &mut dyn FnMut(&mut OtelData),
1009 ) {
1010 let layer = dispatch
1011 .downcast_ref::<OpenTelemetryLayer<S, T>>()
1012 .expect("layer should downcast to expected type; this is a bug!");
1013
1014 let subscriber = dispatch
1015 .downcast_ref::<S>()
1016 .expect("subscriber should downcast to expected type; this is a bug!");
1017
1018 let span = subscriber
1019 .span(id)
1020 .expect("registry should have a span for the current ID");
1021
1022 let otel_data = span.extensions().get::<OtelDataLock>().cloned();
1023
1024 if let Some(otel_data) = otel_data {
1025 let mut otel_data = otel_data.lock();
1026 layer.start_cx(&mut otel_data);
1028 f(&mut otel_data);
1029 }
1030 }
1031
1032 fn get_activated_otel_context(
1045 dispatch: &tracing::Dispatch,
1046 span: &span::Id,
1047 f: &mut dyn FnMut(&OtelContext),
1048 ) {
1049 Self::get_activated_context(dispatch, span, &mut |otel_data: &mut OtelData| {
1050 if let OtelDataState::Context { current_cx } = &otel_data.state {
1051 f(current_cx)
1052 }
1053 });
1054 }
1055
1056 fn extra_span_attrs(&self) -> usize {
1057 let mut extra_attrs = 0;
1058 if self.location {
1059 extra_attrs += 3;
1060 }
1061 if self.with_threads {
1062 extra_attrs += 2;
1063 }
1064 if self.with_level {
1065 extra_attrs += 1;
1066 }
1067 if self.with_target {
1068 extra_attrs += 1;
1069 }
1070 extra_attrs
1071 }
1072
1073 fn start_cx(&self, otel_data: &mut OtelData) {
1078 if let OtelDataState::Context { .. } = &otel_data.state {
1079 } else if let OtelDataState::Builder {
1081 builder,
1082 parent_cx,
1083 status,
1084 } = take(&mut otel_data.state)
1085 {
1086 let current_cx = prevent_reentrant_call(|| {
1089 let mut span = builder.start_with_context(&self.tracer, &parent_cx);
1090 span.set_status(status);
1091 parent_cx.with_span(span)
1092 });
1093
1094 otel_data.state = OtelDataState::Context { current_cx };
1095 }
1096 }
1097
1098 fn with_started_cx<U>(&self, otel_data: &mut OtelData, f: &dyn Fn(&OtelContext) -> U) -> U {
1099 self.start_cx(otel_data);
1100 match &otel_data.state {
1101 OtelDataState::Context { current_cx, .. } => prevent_reentrant_call(|| f(current_cx)),
1102 _ => panic!("OtelDataState should be a Context after starting it; this is a bug!"),
1103 }
1104 }
1105}
1106
1107thread_local! {
1108 static THREAD_ID: u64 = {
1109 thread_id_integer(thread::current().id())
1117 };
1118}
1119
1120thread_local! {
1121 static GUARD_STACK: RefCell<IdContextGuardStack> = RefCell::new(IdContextGuardStack::new());
1122}
1123
1124type IdContextGuardStack = IdValueStack<ContextGuard>;
1125
1126impl<S, T> Layer<S> for OpenTelemetryLayer<S, T>
1127where
1128 S: Subscriber + for<'span> LookupSpan<'span>,
1129 T: otel::Tracer + 'static,
1130 T::Span: Send + Sync,
1131{
1132 fn on_new_span(&self, attrs: &Attributes<'_>, id: &span::Id, ctx: Context<'_, S>) {
1137 let span = ctx.span(id).expect("Span not found, this is a bug");
1138 let mut extensions = span.extensions_mut();
1139
1140 if self.tracked_inactivity && extensions.get_mut::<Timings>().is_none() {
1141 extensions.insert(Timings::new());
1142 }
1143
1144 let parent_cx = self.parent_context(attrs, &ctx);
1145 let mut builder = self
1146 .tracer
1147 .span_builder(attrs.metadata().name())
1148 .with_start_time(crate::time::now());
1149
1150 let builder_attrs = builder.attributes.get_or_insert(Vec::with_capacity(
1151 attrs.fields().len() + self.extra_span_attrs(),
1152 ));
1153
1154 if self.location {
1155 let meta = attrs.metadata();
1156
1157 if let Some(filename) = meta.file() {
1158 builder_attrs.push(KeyValue::new("code.file.path", filename));
1159 }
1160
1161 if let Some(module) = meta.module_path() {
1162 builder_attrs.push(KeyValue::new("code.module.name", module));
1163 }
1164
1165 if let Some(line) = meta.line() {
1166 builder_attrs.push(KeyValue::new("code.line.number", line as i64));
1167 }
1168 }
1169
1170 if self.with_threads {
1171 THREAD_ID.with(|id| builder_attrs.push(KeyValue::new("thread.id", *id as i64)));
1172 if let Some(name) = std::thread::current().name() {
1173 builder_attrs.push(KeyValue::new("thread.name", name.to_string()));
1178 }
1179 }
1180
1181 if self.with_level {
1182 builder_attrs.push(KeyValue::new("level", attrs.metadata().level().as_str()));
1183 }
1184 if self.with_target {
1185 builder_attrs.push(KeyValue::new("target", attrs.metadata().target()));
1186 }
1187
1188 let mut updates = SpanBuilderUpdates::default();
1189 attrs.record(&mut SpanAttributeVisitor {
1190 span_builder_updates: &mut updates,
1191 sem_conv_config: self.sem_conv_config,
1192 });
1193
1194 let mut status = Status::Unset;
1195 updates.update(&mut builder, &mut status);
1196 extensions.insert(OtelDataLock::new(OtelData {
1197 state: OtelDataState::Builder {
1198 builder,
1199 parent_cx,
1200 status,
1201 },
1202 end_time: None,
1203 }));
1204 }
1205
1206 fn on_enter(&self, id: &span::Id, ctx: Context<'_, S>) {
1207 if !self.context_activation && !self.tracked_inactivity {
1208 return;
1209 }
1210
1211 let span = ctx.span(id).expect("Span not found, this is a bug");
1212
1213 if self.context_activation {
1214 let otel_data = span.extensions().get::<OtelDataLock>().cloned();
1217 if let Some(otel_data) = otel_data {
1218 self.with_started_cx(&mut otel_data.lock(), &|cx| {
1219 let guard = prevent_reentrant_call(|| cx.clone().attach());
1220 GUARD_STACK.with(|stack| stack.borrow_mut().push(id.clone(), guard));
1221 });
1222 }
1223
1224 if !self.tracked_inactivity {
1225 return;
1226 }
1227 }
1228
1229 let mut extensions = span.extensions_mut();
1230
1231 if let Some(timings) = extensions.get_mut::<Timings>() {
1232 if timings.entered_count == 0 {
1233 let now = Instant::now();
1234 timings.idle += (now - timings.last).as_nanos() as i64;
1235 timings.last = now;
1236 }
1237 timings.entered_count += 1;
1238 }
1239 }
1240
1241 fn on_exit(&self, id: &span::Id, ctx: Context<'_, S>) {
1242 let span = ctx.span(id).expect("Span not found, this is a bug");
1243 let mut extensions = span.extensions_mut();
1244
1245 if let Some(otel_data) = extensions.get_mut::<OtelDataLock>() {
1249 otel_data.lock().end_time = Some(crate::time::now());
1250 if self.context_activation {
1251 GUARD_STACK.with(|stack| stack.borrow_mut().pop(id));
1252 }
1253 }
1254
1255 if !self.tracked_inactivity {
1256 return;
1257 }
1258
1259 if let Some(timings) = extensions.get_mut::<Timings>() {
1260 timings.entered_count -= 1;
1261 if timings.entered_count == 0 {
1262 let now = Instant::now();
1263 timings.busy += (now - timings.last).as_nanos() as i64;
1264 timings.last = now;
1265 }
1266 }
1267 }
1268
1269 fn on_record(&self, id: &Id, values: &Record<'_>, ctx: Context<'_, S>) {
1273 let span = ctx.span(id).expect("Span not found, this is a bug");
1274 let mut updates = SpanBuilderUpdates::default();
1275 values.record(&mut SpanAttributeVisitor {
1276 span_builder_updates: &mut updates,
1277 sem_conv_config: self.sem_conv_config,
1278 });
1279 let extensions = span.extensions();
1280 if let Some(otel_data) = extensions.get::<OtelDataLock>() {
1281 match &mut otel_data.lock().state {
1282 OtelDataState::Builder {
1283 builder, status, ..
1284 } => {
1285 updates.update(builder, status);
1287 }
1288 OtelDataState::Context { current_cx, .. } => {
1289 updates.update_span(¤t_cx.span());
1291 }
1292 }
1293 }
1294 }
1295
1296 fn on_follows_from(&self, id: &Id, follows: &Id, ctx: Context<S>) {
1297 let span = ctx.span(id).expect("Span not found, this is a bug");
1298 let otel_data = span.extensions().get::<OtelDataLock>().cloned();
1299 let Some(data) = otel_data else {
1300 return; };
1302
1303 if let Some(follows_span) = ctx.span(follows) {
1307 let follows_extensions = follows_span.extensions();
1308 let follows_otel_data = follows_extensions.get::<OtelDataLock>();
1309 let Some(follows_data) = follows_otel_data else {
1310 return; };
1312
1313 let follows_data = follows_data.clone();
1314 let mut follows_locked = follows_data.lock();
1315 drop(follows_extensions);
1319
1320 let follows_context =
1321 self.with_started_cx(&mut follows_locked, &|cx| cx.span().span_context().clone());
1322 match &mut data.lock().state {
1323 OtelDataState::Builder { builder, .. } => {
1324 if let Some(ref mut links) = builder.links {
1325 links.push(otel::Link::with_context(follows_context));
1326 } else {
1327 builder.links = Some(vec![otel::Link::with_context(follows_context)]);
1328 }
1329 }
1330 OtelDataState::Context { current_cx, .. } => {
1331 prevent_reentrant_call(|| current_cx.span().add_link(follows_context, vec![]));
1332 }
1333 }
1334 }
1335 }
1336
1337 fn on_event(&self, event: &Event<'_>, ctx: Context<'_, S>) {
1346 if INSIDE_TRACING.with(|inside| inside.get()) {
1347 return;
1349 }
1350 if let Some(span) = event.parent().and_then(|id| ctx.span(id)).or_else(|| {
1352 event
1353 .is_contextual()
1354 .then(|| ctx.lookup_current())
1355 .flatten()
1356 }) {
1357 #[cfg(feature = "tracing-log")]
1360 let normalized_meta = event.normalized_metadata();
1361 #[cfg(feature = "tracing-log")]
1362 let meta = normalized_meta.as_ref().unwrap_or_else(|| event.metadata());
1363 #[cfg(not(feature = "tracing-log"))]
1364 let meta = event.metadata();
1365
1366 let target = Key::new("target");
1367
1368 #[cfg(feature = "tracing-log")]
1369 let target = if normalized_meta.is_some() {
1370 KeyValue::new(target, Value::String(meta.target().to_owned().into()))
1371 } else {
1372 KeyValue::new(target, Value::String(event.metadata().target().into()))
1373 };
1374
1375 #[cfg(not(feature = "tracing-log"))]
1376 let target = KeyValue::new(target, Value::String(meta.target().into()));
1377
1378 let mut otel_event = otel::Event::new(
1379 String::new(),
1380 crate::time::now(),
1381 vec![
1382 KeyValue::new(
1383 Key::new("level"),
1384 Value::String(meta.level().as_str().into()),
1385 ),
1386 target,
1387 ],
1388 0,
1389 );
1390
1391 let mut builder_updates = None;
1392 event.record(&mut SpanEventVisitor {
1393 event_builder: &mut otel_event,
1394 span_builder_updates: &mut builder_updates,
1395 sem_conv_config: self.sem_conv_config,
1396 });
1397
1398 if otel_event.name.is_empty() {
1404 otel_event.name = std::borrow::Cow::Borrowed(event.metadata().name());
1405 }
1406
1407 let otel_data = span.extensions().get::<OtelDataLock>().cloned();
1408
1409 if let Some(otel_data) = otel_data {
1410 if self.location {
1411 #[cfg(not(feature = "tracing-log"))]
1412 let normalized_meta: Option<tracing_core::Metadata<'_>> = None;
1413 let (file, module) = match &normalized_meta {
1414 Some(meta) => (
1415 meta.file().map(|s| Value::from(s.to_owned())),
1416 meta.module_path().map(|s| Value::from(s.to_owned())),
1417 ),
1418 None => (
1419 event.metadata().file().map(Value::from),
1420 event.metadata().module_path().map(Value::from),
1421 ),
1422 };
1423
1424 if let Some(file) = file {
1425 otel_event
1426 .attributes
1427 .push(KeyValue::new("code.file.path", file));
1428 }
1429 if let Some(module) = module {
1430 otel_event
1431 .attributes
1432 .push(KeyValue::new("code.module.name", module));
1433 }
1434 if let Some(line) = meta.line() {
1435 otel_event
1436 .attributes
1437 .push(KeyValue::new("code.line.number", line as i64));
1438 }
1439 }
1440
1441 match &mut otel_data.lock().state {
1442 OtelDataState::Builder {
1443 builder, status, ..
1444 } => {
1445 if *status == otel::Status::Unset
1446 && *meta.level() == tracing_core::Level::ERROR
1447 {
1448 *status = otel::Status::error("");
1449 }
1450 if let Some(builder_updates) = builder_updates {
1451 builder_updates.update(builder, status);
1452 }
1453 if let Some(ref mut events) = builder.events {
1454 events.push(otel_event);
1455 } else {
1456 builder.events = Some(vec![otel_event]);
1457 }
1458 }
1459 OtelDataState::Context { current_cx, .. } => {
1460 let span = current_cx.span();
1461 if *meta.level() == tracing_core::Level::ERROR {
1463 span.set_status(otel::Status::error(""));
1464 }
1465 if let Some(builder_updates) = builder_updates {
1466 builder_updates.update_span(&span);
1467 }
1468 span.add_event(otel_event.name, otel_event.attributes);
1469 }
1470 }
1471 };
1472 }
1473 }
1474
1475 fn on_close(&self, id: span::Id, ctx: Context<'_, S>) {
1479 let span = ctx.span(&id).expect("Span not found, this is a bug");
1480 let (otel_data_lock, timings) = {
1482 let mut extensions = span.extensions_mut();
1483 let timings = if self.tracked_inactivity {
1484 extensions.remove::<Timings>()
1485 } else {
1486 None
1487 };
1488 (extensions.remove::<OtelDataLock>(), timings)
1489 };
1490
1491 if let Some(otel_data_lock) = otel_data_lock {
1492 drop(otel_data_lock.lock());
1495 debug_assert!(
1496 Arc::strong_count(&otel_data_lock.inner) == 1,
1497 "OtelDataLock should not be held by anything else when closing spans. This is a bug in `tracing-opentelemetry`, please file a bug report. This will be ignored when compiled with --release."
1498 );
1499 debug_assert!(
1503 Arc::weak_count(&otel_data_lock.inner) == 0,
1504 "OtelDataLock should never be made into `Weak`. This is a bug in `tracing-opentelemetry`, please file a bug report. This will be ignored when compiled with --release."
1505 );
1506
1507 let otel_data = Arc::try_unwrap(otel_data_lock.inner)
1508 .map(|lock| lock.into_inner().unwrap())
1509 .unwrap_or_else(|otel_data| otel_data.lock().unwrap().clone());
1510
1511 let timings = timings.map(|timings| {
1513 let busy_ns = Key::new("busy_ns");
1514 let idle_ns = Key::new("idle_ns");
1515
1516 vec![
1517 KeyValue::new(busy_ns, timings.busy),
1518 KeyValue::new(idle_ns, timings.idle),
1519 ]
1520 });
1521
1522 match otel_data.state {
1523 OtelDataState::Builder {
1524 builder,
1525 parent_cx,
1526 status,
1527 } => {
1528 let mut span = builder.start_with_context(&self.tracer, &parent_cx);
1532 if let Some(timings) = timings {
1533 span.set_attributes(timings)
1534 };
1535 span.set_status(status.clone());
1536 if let Some(end_time) = otel_data.end_time {
1537 span.end_with_timestamp(end_time);
1538 } else {
1539 span.end();
1540 }
1541 }
1542 OtelDataState::Context { current_cx } => {
1543 let span = current_cx.span();
1544 if let Some(timings) = timings {
1545 span.set_attributes(timings)
1546 };
1547 otel_data
1548 .end_time
1549 .map_or_else(|| span.end(), |end_time| span.end_with_timestamp(end_time));
1550 }
1551 }
1552 }
1553 }
1554
1555 unsafe fn downcast_raw(&self, id: TypeId) -> Option<*const ()> {
1558 match id {
1559 id if id == TypeId::of::<Self>() => Some(self as *const _ as *const ()),
1560 id if id == TypeId::of::<WithContext>() => {
1561 Some(&self.with_context as *const _ as *const ())
1562 }
1563 _ => None,
1564 }
1565 }
1566}
1567
1568struct Timings {
1569 idle: i64,
1570 busy: i64,
1571 last: Instant,
1572 entered_count: u64,
1573}
1574
1575impl Timings {
1576 fn new() -> Self {
1577 Self {
1578 idle: 0,
1579 busy: 0,
1580 last: Instant::now(),
1581 entered_count: 0,
1582 }
1583 }
1584}
1585
1586fn thread_id_integer(id: thread::ThreadId) -> u64 {
1587 let thread_id = format!("{id:?}");
1588 thread_id
1589 .trim_start_matches("ThreadId(")
1590 .trim_end_matches(')')
1591 .parse::<u64>()
1592 .expect("thread ID should parse as an integer")
1593}
1594
1595#[cfg(test)]
1596mod tests {
1597 use crate::OpenTelemetrySpanExt;
1598
1599 use super::*;
1600 use opentelemetry::trace::{SpanContext, TraceFlags, TracerProvider};
1601 use opentelemetry_sdk::trace::SpanExporter;
1602 use std::{collections::HashMap, error::Error, fmt::Display, time::SystemTime};
1603 use tracing::trace_span;
1604 use tracing_core::LevelFilter;
1605 use tracing_subscriber::prelude::*;
1606
1607 #[derive(Debug, Clone)]
1608 struct TestTracer {
1609 tracer: opentelemetry_sdk::trace::Tracer,
1610 exporter: opentelemetry_sdk::trace::InMemorySpanExporter,
1611 }
1612
1613 impl TestTracer {
1614 fn spans(&mut self) -> Vec<opentelemetry_sdk::trace::SpanData> {
1615 self.exporter
1616 .force_flush()
1617 .expect("problems flushing spans");
1618 self.exporter
1619 .get_finished_spans()
1620 .expect("problems recording spans")
1621 }
1622
1623 fn with_data<T>(&mut self, f: impl FnOnce(&opentelemetry_sdk::trace::SpanData) -> T) -> T {
1624 let spans = self.spans();
1625 f(spans.first().expect("no spans recorded"))
1626 }
1627
1628 fn attributes(&mut self) -> HashMap<String, Value> {
1629 self.with_data(|data| {
1630 data.attributes
1631 .iter()
1632 .map(|kv| (kv.key.to_string(), kv.value.clone()))
1633 .collect()
1634 })
1635 }
1636 }
1637
1638 impl Default for TestTracer {
1639 fn default() -> Self {
1640 let exporter = opentelemetry_sdk::trace::InMemorySpanExporter::default();
1641 let provider = opentelemetry_sdk::trace::SdkTracerProvider::builder()
1642 .with_simple_exporter(exporter.clone())
1643 .build();
1644 let tracer = provider.tracer("test-tracer");
1645 Self { tracer, exporter }
1646 }
1647 }
1648
1649 impl opentelemetry::trace::Tracer for TestTracer {
1650 type Span = opentelemetry_sdk::trace::Span;
1651
1652 fn build_with_context(&self, builder: SpanBuilder, parent_cx: &OtelContext) -> Self::Span {
1653 self.tracer.build_with_context(builder, parent_cx)
1654 }
1655 }
1656
1657 #[derive(Debug, Clone)]
1658 struct TestSpan(otel::SpanContext);
1659 impl otel::Span for TestSpan {
1660 fn add_event_with_timestamp<T: Into<Cow<'static, str>>>(
1661 &mut self,
1662 _: T,
1663 _: SystemTime,
1664 _: Vec<KeyValue>,
1665 ) {
1666 }
1667 fn span_context(&self) -> &otel::SpanContext {
1668 &self.0
1669 }
1670 fn is_recording(&self) -> bool {
1671 false
1672 }
1673 fn set_attribute(&mut self, _attribute: KeyValue) {}
1674 fn set_status(&mut self, _status: otel::Status) {}
1675 fn update_name<T: Into<Cow<'static, str>>>(&mut self, _new_name: T) {}
1676 fn add_link(&mut self, _span_context: SpanContext, _attributes: Vec<KeyValue>) {}
1677 fn end_with_timestamp(&mut self, _timestamp: SystemTime) {}
1678 }
1679
1680 #[derive(Debug)]
1681 struct TestDynError {
1682 msg: &'static str,
1683 source: Option<Box<TestDynError>>,
1684 }
1685 impl Display for TestDynError {
1686 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1687 write!(f, "{}", self.msg)
1688 }
1689 }
1690 impl Error for TestDynError {
1691 fn source(&self) -> Option<&(dyn Error + 'static)> {
1692 match &self.source {
1693 Some(source) => Some(source),
1694 None => None,
1695 }
1696 }
1697 }
1698 impl TestDynError {
1699 fn new(msg: &'static str) -> Self {
1700 Self { msg, source: None }
1701 }
1702 fn with_parent(self, parent_msg: &'static str) -> Self {
1703 Self {
1704 msg: parent_msg,
1705 source: Some(Box::new(self)),
1706 }
1707 }
1708 }
1709
1710 #[test]
1711 fn dynamic_span_names() {
1712 let dynamic_name = "GET http://example.com".to_string();
1713 let mut tracer = TestTracer::default();
1714 let subscriber = tracing_subscriber::registry().with(layer().with_tracer(tracer.clone()));
1715
1716 tracing::subscriber::with_default(subscriber, || {
1717 tracing::debug_span!("static_name", otel.name = dynamic_name.as_str());
1718 });
1719
1720 let recorded_name = tracer.spans().first().unwrap().name.clone();
1721 assert_eq!(recorded_name, dynamic_name.as_str())
1722 }
1723
1724 #[test]
1725 fn span_kind() {
1726 let mut tracer = TestTracer::default();
1727 let subscriber = tracing_subscriber::registry().with(layer().with_tracer(tracer.clone()));
1728
1729 tracing::subscriber::with_default(subscriber, || {
1730 tracing::debug_span!("request", otel.kind = "server");
1731 });
1732
1733 let recorded_kind = tracer.with_data(|data| data.span_kind.clone());
1734 assert_eq!(recorded_kind, otel::SpanKind::Server)
1735 }
1736
1737 #[test]
1738 fn span_status_code() {
1739 let mut tracer = TestTracer::default();
1740 let subscriber = tracing_subscriber::registry().with(layer().with_tracer(tracer.clone()));
1741
1742 tracing::subscriber::with_default(subscriber, || {
1743 tracing::debug_span!("request", otel.status_code = ?otel::Status::Ok);
1744 });
1745
1746 let recorded_status = tracer.with_data(|data| data.status.clone());
1747 assert_eq!(recorded_status, otel::Status::Ok)
1748 }
1749
1750 #[test]
1751 fn span_status_description() {
1752 let mut tracer = TestTracer::default();
1753 let subscriber = tracing_subscriber::registry().with(layer().with_tracer(tracer.clone()));
1754
1755 let message = "message";
1756
1757 tracing::subscriber::with_default(subscriber, || {
1758 tracing::debug_span!("request", otel.status_description = message);
1759 });
1760
1761 let recorded_status_message = tracer.with_data(|data| data.status.clone());
1762
1763 assert_eq!(recorded_status_message, otel::Status::error(message))
1764 }
1765
1766 #[test]
1767 fn trace_id_from_existing_context_with_context_activation() {
1768 trace_id_from_existing_context_impl(true);
1769 }
1770
1771 #[test]
1772 fn trace_id_from_existing_context_no_context_activation() {
1773 trace_id_from_existing_context_impl(false);
1774 }
1775
1776 fn trace_id_from_existing_context_impl(context_activation: bool) {
1777 let mut tracer = TestTracer::default();
1778 let subscriber = tracing_subscriber::registry().with(
1779 layer()
1780 .with_tracer(tracer.clone())
1781 .with_context_activation(context_activation),
1782 );
1783 let trace_id = otel::TraceId::from(42u128);
1784 let existing_cx = OtelContext::current_with_span(TestSpan(otel::SpanContext::new(
1785 trace_id,
1786 otel::SpanId::from(1u64),
1787 TraceFlags::SAMPLED,
1788 false,
1789 Default::default(),
1790 )));
1791 let _g = existing_cx.attach();
1792
1793 tracing::subscriber::with_default(subscriber, || {
1794 tracing::debug_span!("request", otel.kind = "server");
1795 });
1796
1797 let recorded_trace_id = tracer.with_data(|data| data.span_context.trace_id());
1798 assert_eq!(recorded_trace_id, trace_id)
1799 }
1800
1801 #[test]
1802 fn includes_timings_with_context_activation() {
1803 includes_timings_impl(true);
1804 }
1805
1806 #[test]
1807 fn includes_timings_no_context_activation() {
1808 includes_timings_impl(false);
1809 }
1810
1811 fn includes_timings_impl(context_activation: bool) {
1812 let mut tracer = TestTracer::default();
1813 let subscriber = tracing_subscriber::registry().with(
1814 layer()
1815 .with_tracer(tracer.clone())
1816 .with_tracked_inactivity(true)
1817 .with_context_activation(context_activation),
1818 );
1819
1820 tracing::subscriber::with_default(subscriber, || {
1821 tracing::debug_span!("request");
1822 });
1823
1824 let attributes = tracer.attributes();
1825
1826 assert!(attributes.contains_key("idle_ns"));
1827 assert!(attributes.contains_key("busy_ns"));
1828 }
1829
1830 #[test]
1831 fn records_error_fields_with_context_activation() {
1832 records_error_fields_impl(true);
1833 }
1834
1835 #[test]
1836 fn records_error_fields_no_context_activation() {
1837 records_error_fields_impl(false);
1838 }
1839
1840 fn records_error_fields_impl(context_activation: bool) {
1841 let mut tracer = TestTracer::default();
1842 let subscriber = tracing_subscriber::registry().with(
1843 layer()
1844 .with_tracer(tracer.clone())
1845 .with_context_activation(context_activation),
1846 );
1847
1848 let err = TestDynError::new("base error")
1849 .with_parent("intermediate error")
1850 .with_parent("user error");
1851
1852 tracing::subscriber::with_default(subscriber, || {
1853 tracing::debug_span!(
1854 "request",
1855 error = &err as &(dyn std::error::Error + 'static)
1856 );
1857 });
1858
1859 let attributes = tracer.attributes();
1860
1861 assert_eq!(attributes["error"].as_str(), "user error");
1862 assert_eq!(
1863 attributes["error.chain"],
1864 Value::Array(
1865 vec![
1866 StringValue::from("intermediate error"),
1867 StringValue::from("base error")
1868 ]
1869 .into()
1870 )
1871 );
1872
1873 assert_eq!(attributes[FIELD_EXCEPTION_MESSAGE].as_str(), "user error");
1874 assert_eq!(
1875 attributes[FIELD_EXCEPTION_STACKTRACE],
1876 Value::Array(
1877 vec![
1878 StringValue::from("intermediate error"),
1879 StringValue::from("base error")
1880 ]
1881 .into()
1882 )
1883 );
1884 }
1885
1886 #[test]
1887 fn records_event_name_with_context_activation() {
1888 records_event_name_impl(true);
1889 }
1890
1891 #[test]
1892 fn records_event_name_no_context_activation() {
1893 records_event_name_impl(false);
1894 }
1895
1896 fn records_event_name_impl(context_activation: bool) {
1897 let mut tracer = TestTracer::default();
1898 let subscriber = tracing_subscriber::registry().with(
1899 layer()
1900 .with_tracer(tracer.clone())
1901 .with_context_activation(context_activation),
1902 );
1903
1904 tracing::subscriber::with_default(subscriber, || {
1905 tracing::debug_span!("test span").in_scope(|| {
1906 tracing::event!(tracing::Level::INFO, "event name 1"); tracing::event!(name: "event name 2", tracing::Level::INFO, field1 = "field1");
1908 tracing::event!(name: "event name 3", tracing::Level::INFO, error = "field2");
1909 tracing::event!(name: "event name 4", tracing::Level::INFO, message = "field3");
1910 tracing::event!(name: "event name 5", tracing::Level::INFO, name = "field4");
1911 });
1912 });
1913
1914 let events = tracer.with_data(|data| data.events.clone());
1915
1916 let mut iter = events.iter();
1917
1918 assert_eq!(iter.next().unwrap().name, "event name 1");
1919 assert_eq!(iter.next().unwrap().name, "event name 2");
1920 assert_eq!(iter.next().unwrap().name, "exception"); assert_eq!(iter.next().unwrap().name, "field3"); assert_eq!(iter.next().unwrap().name, "event name 5"); }
1924
1925 #[test]
1926 #[cfg(not(__reentrant_tracing_test))] fn event_filter_count() {
1928 let mut tracer = TestTracer::default();
1929 let subscriber = tracing_subscriber::registry().with(
1930 layer()
1931 .with_tracer(tracer.clone())
1932 .with_counting_event_filter(LevelFilter::INFO)
1933 .with_filter(LevelFilter::DEBUG),
1934 );
1935
1936 tracing::subscriber::with_default(subscriber, || {
1937 tracing::debug_span!("test span").in_scope(|| {
1938 tracing::event!(tracing::Level::TRACE, "1");
1939 tracing::event!(tracing::Level::DEBUG, "2");
1940 tracing::event!(tracing::Level::INFO, "3");
1941 tracing::event!(tracing::Level::WARN, "4");
1942 tracing::event!(tracing::Level::ERROR, "5");
1943 });
1944 });
1945
1946 let events = tracer.with_data(|data| data.events.clone());
1947
1948 let mut iter = events.iter();
1949
1950 assert_eq!(iter.next().unwrap().name, "3");
1951 assert_eq!(iter.next().unwrap().name, "4");
1952 assert_eq!(iter.next().unwrap().name, "5");
1953 assert!(iter.next().is_none());
1954
1955 let spans = tracer.spans();
1956 assert_eq!(spans.len(), 1);
1957
1958 let Value::I64(event_count) = spans
1959 .first()
1960 .unwrap()
1961 .attributes
1962 .iter()
1963 .find(|key_value| key_value.key.as_str() == SPAN_EVENT_COUNT_FIELD)
1964 .unwrap()
1965 .value
1966 else {
1967 panic!("Unexpected type of `{SPAN_EVENT_COUNT_FIELD}`.");
1968 };
1969 assert_eq!(event_count, 4);
1972 }
1973
1974 #[test]
1975 fn records_no_error_fields_with_context_activation() {
1976 records_no_error_fields_impl(true);
1977 }
1978
1979 #[test]
1980 fn records_no_error_fields_no_context_activation() {
1981 records_no_error_fields_impl(false);
1982 }
1983
1984 fn records_no_error_fields_impl(context_activation: bool) {
1985 let mut tracer = TestTracer::default();
1986 let subscriber = tracing_subscriber::registry().with(
1987 layer()
1988 .with_error_records_to_exceptions(false)
1989 .with_tracer(tracer.clone())
1990 .with_context_activation(context_activation),
1991 );
1992
1993 let err = TestDynError::new("base error")
1994 .with_parent("intermediate error")
1995 .with_parent("user error");
1996
1997 tracing::subscriber::with_default(subscriber, || {
1998 tracing::debug_span!(
1999 "request",
2000 error = &err as &(dyn std::error::Error + 'static)
2001 );
2002 });
2003
2004 let attributes = tracer.attributes();
2005
2006 assert_eq!(attributes["error"].as_str(), "user error");
2007 assert_eq!(
2008 attributes["error.chain"],
2009 Value::Array(
2010 vec![
2011 StringValue::from("intermediate error"),
2012 StringValue::from("base error")
2013 ]
2014 .into()
2015 )
2016 );
2017
2018 assert_eq!(attributes[FIELD_EXCEPTION_MESSAGE].as_str(), "user error");
2019 assert_eq!(
2020 attributes[FIELD_EXCEPTION_STACKTRACE],
2021 Value::Array(
2022 vec![
2023 StringValue::from("intermediate error"),
2024 StringValue::from("base error")
2025 ]
2026 .into()
2027 )
2028 );
2029 }
2030
2031 #[test]
2032 fn includes_span_location_with_context_activation() {
2033 includes_span_location_impl(true);
2034 }
2035
2036 #[test]
2037 fn includes_span_location_no_context_activation() {
2038 includes_span_location_impl(false);
2039 }
2040
2041 fn includes_span_location_impl(context_activation: bool) {
2042 let mut tracer = TestTracer::default();
2043 let subscriber = tracing_subscriber::registry().with(
2044 layer()
2045 .with_tracer(tracer.clone())
2046 .with_location(true)
2047 .with_context_activation(context_activation),
2048 );
2049
2050 tracing::subscriber::with_default(subscriber, || {
2051 tracing::debug_span!("request");
2052 });
2053
2054 let attributes = tracer.attributes();
2055
2056 assert!(attributes.contains_key("code.file.path"));
2057 assert!(attributes.contains_key("code.module.name"));
2058 assert!(attributes.contains_key("code.line.number"));
2059 }
2060
2061 #[test]
2062 fn excludes_span_location_with_context_activation() {
2063 excludes_span_location_impl(true);
2064 }
2065
2066 #[test]
2067 fn excludes_span_location_no_context_activation() {
2068 excludes_span_location_impl(false);
2069 }
2070
2071 fn excludes_span_location_impl(context_activation: bool) {
2072 let mut tracer = TestTracer::default();
2073 let subscriber = tracing_subscriber::registry().with(
2074 layer()
2075 .with_tracer(tracer.clone())
2076 .with_location(false)
2077 .with_context_activation(context_activation),
2078 );
2079
2080 tracing::subscriber::with_default(subscriber, || {
2081 tracing::debug_span!("request");
2082 });
2083
2084 let attributes = tracer.attributes();
2085
2086 assert!(!attributes.contains_key("code.file.path"));
2087 assert!(!attributes.contains_key("code.module.name"));
2088 assert!(!attributes.contains_key("code.line.number"));
2089 }
2090
2091 #[test]
2092 fn includes_thread_with_context_activation() {
2093 includes_thread_impl(true);
2094 }
2095
2096 #[test]
2097 fn includes_thread_no_context_activation() {
2098 includes_thread_impl(false);
2099 }
2100
2101 fn includes_thread_impl(context_activation: bool) {
2102 let thread = thread::current();
2103 let expected_name = thread
2104 .name()
2105 .map(|name| Value::String(name.to_owned().into()));
2106 let expected_id = Value::I64(thread_id_integer(thread.id()) as i64);
2107
2108 let mut tracer = TestTracer::default();
2109 let subscriber = tracing_subscriber::registry().with(
2110 layer()
2111 .with_tracer(tracer.clone())
2112 .with_threads(true)
2113 .with_context_activation(context_activation),
2114 );
2115
2116 tracing::subscriber::with_default(subscriber, || {
2117 tracing::debug_span!("request");
2118 });
2119
2120 let attributes = tracer.attributes();
2121
2122 assert_eq!(attributes.get("thread.name"), expected_name.as_ref());
2123 assert_eq!(attributes.get("thread.id"), Some(&expected_id));
2124 }
2125
2126 #[test]
2127 fn excludes_thread_with_context_activation() {
2128 excludes_thread_impl(true);
2129 }
2130
2131 #[test]
2132 fn excludes_thread_no_context_activation() {
2133 excludes_thread_impl(false);
2134 }
2135
2136 fn excludes_thread_impl(context_activation: bool) {
2137 let mut tracer = TestTracer::default();
2138 let subscriber = tracing_subscriber::registry().with(
2139 layer()
2140 .with_tracer(tracer.clone())
2141 .with_threads(false)
2142 .with_context_activation(context_activation),
2143 );
2144
2145 tracing::subscriber::with_default(subscriber, || {
2146 tracing::debug_span!("request");
2147 });
2148
2149 let attributes = tracer.attributes();
2150
2151 assert!(!attributes.contains_key("thread.name"));
2152 assert!(!attributes.contains_key("thread.id"));
2153 }
2154
2155 #[test]
2156 fn includes_level_with_context_activation() {
2157 includes_level_impl(true);
2158 }
2159
2160 #[test]
2161 fn includes_level_no_context_activation() {
2162 includes_level_impl(false);
2163 }
2164
2165 fn includes_level_impl(context_activation: bool) {
2166 let mut tracer = TestTracer::default();
2167 let subscriber = tracing_subscriber::registry().with(
2168 layer()
2169 .with_tracer(tracer.clone())
2170 .with_level(true)
2171 .with_context_activation(context_activation),
2172 );
2173
2174 tracing::subscriber::with_default(subscriber, || {
2175 tracing::debug_span!("request");
2176 });
2177
2178 let attributes = tracer.attributes();
2179
2180 assert!(attributes.contains_key("level"));
2181 }
2182
2183 #[test]
2184 fn excludes_level_with_context_activation() {
2185 excludes_level_impl(true);
2186 }
2187
2188 #[test]
2189 fn excludes_level_no_context_activation() {
2190 excludes_level_impl(false);
2191 }
2192
2193 fn excludes_level_impl(context_activation: bool) {
2194 let mut tracer = TestTracer::default();
2195 let subscriber = tracing_subscriber::registry().with(
2196 layer()
2197 .with_tracer(tracer.clone())
2198 .with_level(false)
2199 .with_context_activation(context_activation),
2200 );
2201
2202 tracing::subscriber::with_default(subscriber, || {
2203 tracing::debug_span!("request");
2204 });
2205
2206 let attributes = tracer.attributes();
2207
2208 assert!(!attributes.contains_key("level"));
2209 }
2210
2211 #[test]
2212 fn includes_target() {
2213 let mut tracer = TestTracer::default();
2214 let subscriber = tracing_subscriber::registry()
2215 .with(layer().with_tracer(tracer.clone()).with_target(true));
2216
2217 tracing::subscriber::with_default(subscriber, || {
2218 tracing::debug_span!("request");
2219 });
2220
2221 let attributes = tracer.attributes();
2222 let keys = attributes.keys().map(|k| k.as_str()).collect::<Vec<&str>>();
2223 assert!(keys.contains(&"target"));
2224 }
2225
2226 #[test]
2227 fn excludes_target() {
2228 let mut tracer = TestTracer::default();
2229 let subscriber = tracing_subscriber::registry()
2230 .with(layer().with_tracer(tracer.clone()).with_target(false));
2231
2232 tracing::subscriber::with_default(subscriber, || {
2233 tracing::debug_span!("request");
2234 });
2235
2236 let attributes = tracer.attributes();
2237 let keys = attributes.keys().map(|k| k.as_str()).collect::<Vec<&str>>();
2238 assert!(!keys.contains(&"target"));
2239 }
2240
2241 #[test]
2242 fn propagates_error_fields_from_event_to_span_with_context_activation() {
2243 propagates_error_fields_from_event_to_span_impl(true);
2244 }
2245
2246 #[test]
2247 fn propagates_error_fields_from_event_to_span_no_context_activation() {
2248 propagates_error_fields_from_event_to_span_impl(false);
2249 }
2250
2251 fn propagates_error_fields_from_event_to_span_impl(context_activation: bool) {
2252 let mut tracer = TestTracer::default();
2253 let subscriber = tracing_subscriber::registry().with(
2254 layer()
2255 .with_tracer(tracer.clone())
2256 .with_context_activation(context_activation),
2257 );
2258
2259 let err = TestDynError::new("base error")
2260 .with_parent("intermediate error")
2261 .with_parent("user error");
2262
2263 tracing::subscriber::with_default(subscriber, || {
2264 let _guard = tracing::debug_span!("request",).entered();
2265
2266 tracing::error!(
2267 error = &err as &(dyn std::error::Error + 'static),
2268 "request error!"
2269 )
2270 });
2271
2272 let attributes = tracer.attributes();
2273
2274 assert_eq!(attributes[FIELD_EXCEPTION_MESSAGE].as_str(), "user error");
2275 assert_eq!(
2276 attributes[FIELD_EXCEPTION_STACKTRACE],
2277 Value::Array(
2278 vec![
2279 StringValue::from("intermediate error"),
2280 StringValue::from("base error")
2281 ]
2282 .into()
2283 )
2284 );
2285 }
2286
2287 #[test]
2288 fn propagates_no_error_fields_from_event_to_span_with_context_activation() {
2289 propagates_no_error_fields_from_event_to_span_impl(true);
2290 }
2291
2292 #[test]
2293 fn propagates_no_error_fields_from_event_to_span_no_context_activation() {
2294 propagates_no_error_fields_from_event_to_span_impl(false);
2295 }
2296
2297 fn propagates_no_error_fields_from_event_to_span_impl(context_activation: bool) {
2298 let mut tracer = TestTracer::default();
2299 let subscriber = tracing_subscriber::registry().with(
2300 layer()
2301 .with_error_fields_to_exceptions(false)
2302 .with_tracer(tracer.clone())
2303 .with_context_activation(context_activation),
2304 );
2305
2306 let err = TestDynError::new("base error")
2307 .with_parent("intermediate error")
2308 .with_parent("user error");
2309
2310 tracing::subscriber::with_default(subscriber, || {
2311 let _guard = tracing::debug_span!("request",).entered();
2312
2313 tracing::error!(
2314 error = &err as &(dyn std::error::Error + 'static),
2315 "request error!"
2316 )
2317 });
2318
2319 let attributes = tracer.attributes();
2320
2321 assert_eq!(attributes[FIELD_EXCEPTION_MESSAGE].as_str(), "user error");
2322 assert_eq!(
2323 attributes[FIELD_EXCEPTION_STACKTRACE],
2324 Value::Array(
2325 vec![
2326 StringValue::from("intermediate error"),
2327 StringValue::from("base error")
2328 ]
2329 .into()
2330 )
2331 );
2332 }
2333
2334 #[test]
2335 fn tracing_error_compatibility_with_context_activation() {
2336 tracing_error_compatibility_impl(true);
2337 }
2338
2339 #[test]
2340 fn tracing_error_compatibility_no_context_activation() {
2341 tracing_error_compatibility_impl(false);
2342 }
2343
2344 fn tracing_error_compatibility_impl(context_activation: bool) {
2345 let tracer = TestTracer::default();
2346 let subscriber = tracing_subscriber::registry()
2347 .with(
2348 layer()
2349 .with_error_fields_to_exceptions(false)
2350 .with_tracer(tracer.clone())
2351 .with_context_activation(context_activation),
2352 )
2353 .with(tracing_error::ErrorLayer::default());
2354
2355 tracing::subscriber::with_default(subscriber, || {
2356 let span = tracing::info_span!("Blows up!", exception = tracing::field::Empty);
2357 let _entered = span.enter();
2358 let context = tracing_error::SpanTrace::capture();
2359
2360 span.record("exception", tracing::field::debug(&context));
2362 tracing::info!(exception = &tracing::field::debug(&context), "hello");
2364 });
2365
2366 }
2368
2369 #[derive(Debug, PartialEq)]
2370 struct ValueA(&'static str);
2371 #[derive(Debug, PartialEq)]
2372 struct ValueB(&'static str);
2373
2374 #[test]
2375 fn otel_context_propagation() {
2376 use opentelemetry::trace::Tracer;
2377 use tracing::span;
2378
2379 let mut tracer = TestTracer::default();
2380 let subscriber = tracing_subscriber::registry().with(layer().with_tracer(tracer.clone()));
2381
2382 tracing::subscriber::with_default(subscriber, || {
2383 let _outer_guard =
2385 OtelContext::attach(OtelContext::default().with_value(ValueA("outer")));
2386 assert_eq!(OtelContext::current().get(), Some(&ValueA("outer")));
2387 let root = span!(tracing::Level::TRACE, "tokio-tracing-span-parent");
2388 drop(_outer_guard);
2390 assert!(OtelContext::current().get::<ValueA>().is_none());
2391 let _enter_root = root.enter();
2393 assert_eq!(OtelContext::current().get(), Some(&ValueA("outer")));
2394 let _inner_guard =
2396 OtelContext::attach(OtelContext::current_with_value(ValueB("inner")));
2397 assert_eq!(OtelContext::current().get(), Some(&ValueA("outer")));
2398 assert_eq!(OtelContext::current().get(), Some(&ValueB("inner")));
2399 let child = span!(tracing::Level::TRACE, "tokio-tracing-span-child");
2400 drop(_inner_guard);
2402 assert_eq!(OtelContext::current().get(), Some(&ValueA("outer")));
2403 assert!(OtelContext::current().get::<ValueB>().is_none());
2404 let _enter_child = child.enter();
2406 assert_eq!(OtelContext::current().get(), Some(&ValueA("outer")));
2407 assert_eq!(OtelContext::current().get(), Some(&ValueB("inner")));
2408 let span = tracer
2411 .tracer
2412 .span_builder("otel-tracing-span")
2413 .start(&tracer);
2414 let _otel_guard = OtelContext::attach(OtelContext::current_with_span(span));
2415 let child2 = span!(tracing::Level::TRACE, "tokio-tracing-span-child2");
2416 drop(_otel_guard);
2417 drop(_enter_child);
2419 assert_eq!(OtelContext::current().get(), Some(&ValueA("outer")));
2420 assert!(OtelContext::current().get::<ValueB>().is_none());
2421 drop(_enter_root);
2423 assert!(OtelContext::current().get::<ValueA>().is_none());
2424 assert!(OtelContext::current().get::<ValueB>().is_none());
2425 let _ = child2.enter();
2426 });
2427
2428 let spans = tracer.spans();
2430 let parent = spans
2431 .iter()
2432 .find(|span| span.name == "tokio-tracing-span-parent")
2433 .unwrap();
2434 let child = spans
2435 .iter()
2436 .find(|span| span.name == "tokio-tracing-span-child")
2437 .unwrap();
2438 let child2 = spans
2439 .iter()
2440 .find(|span| span.name == "tokio-tracing-span-child2")
2441 .unwrap();
2442 let otel = spans
2443 .iter()
2444 .find(|span| span.name == "otel-tracing-span")
2445 .unwrap();
2446 assert_eq!(parent.parent_span_id, otel::SpanId::INVALID);
2448 assert_eq!(child.parent_span_id, parent.span_context.span_id());
2450 assert_eq!(otel.parent_span_id, child.span_context.span_id());
2452 assert_eq!(child2.parent_span_id, otel.span_context.span_id());
2454 }
2455
2456 #[test]
2457 fn parent_context() {
2458 let mut tracer = TestTracer::default();
2459 let subscriber = tracing_subscriber::registry().with(layer().with_tracer(tracer.clone()));
2460
2461 tracing::subscriber::with_default(subscriber, || {
2462 let root = trace_span!("root");
2463
2464 let child1 = trace_span!("child-1");
2465 let root_context = root.context(); let _ = child1.set_parent(root_context); let _enter_root = root.enter();
2469 drop(_enter_root);
2470
2471 let child2 = trace_span!("child-2");
2472 let _ = child2.set_parent(root.context());
2473 });
2474
2475 let spans = tracer.spans();
2477 let parent = spans.iter().find(|span| span.name == "root").unwrap();
2478 let child1 = spans.iter().find(|span| span.name == "child-1").unwrap();
2479 let child2 = spans.iter().find(|span| span.name == "child-2").unwrap();
2480 assert_eq!(parent.parent_span_id, otel::SpanId::INVALID);
2481 assert_eq!(child1.parent_span_id, parent.span_context.span_id());
2482 assert_eq!(child2.parent_span_id, parent.span_context.span_id());
2483 }
2484
2485 #[test]
2486 fn record_after_with_context_activation() {
2487 record_after_impl(true);
2488 }
2489
2490 #[test]
2491 fn record_after_no_context_activation() {
2492 record_after_impl(false);
2493 }
2494
2495 fn record_after_impl(context_activation: bool) {
2496 let mut tracer = TestTracer::default();
2497 let subscriber = tracing_subscriber::registry().with(
2498 layer()
2499 .with_tracer(tracer.clone())
2500 .with_context_activation(context_activation),
2501 );
2502
2503 tracing::subscriber::with_default(subscriber, || {
2504 let root = trace_span!("root", before = "before", after = "before");
2505
2506 root.record("before", "after");
2508
2509 let _enter_root = root.enter();
2511 drop(_enter_root);
2512
2513 root.record("after", "after");
2515 });
2516
2517 let spans = tracer.spans();
2520 let parent = spans.iter().find(|span| span.name == "root").unwrap();
2521 assert_eq!(parent.parent_span_id, otel::SpanId::INVALID);
2522 assert!(parent
2523 .attributes
2524 .iter()
2525 .filter(|kv| kv.key.as_str() == "before")
2526 .any(|kv| kv.value.as_str() == "after"));
2527
2528 assert!(parent
2529 .attributes
2530 .iter()
2531 .filter(|kv| kv.key.as_str() == "after")
2532 .any(|kv| kv.value.as_str() == "after"));
2533 }
2534
2535 #[test]
2536 fn parent_context_2() {
2537 let mut tracer = TestTracer::default();
2538 let subscriber = tracing_subscriber::registry().with(layer().with_tracer(tracer.clone()));
2539
2540 tracing::subscriber::with_default(subscriber, || {
2541 let root = trace_span!("root");
2542 _ = root.enter();
2543
2544 let child1 = trace_span!("child-1");
2545 let _ = child1.set_parent(root.context());
2546
2547 trace_span!(parent: &child1, "child-2");
2548 let _ = child1.set_parent(root.context()); trace_span!(parent: &child1, "child-3");
2551 });
2552
2553 let spans = tracer.spans();
2555 let root = spans.iter().find(|span| span.name == "root").unwrap();
2556 let child1 = spans.iter().find(|span| span.name == "child-1").unwrap();
2557 let child2 = spans.iter().find(|span| span.name == "child-2").unwrap();
2558 let child3 = spans.iter().find(|span| span.name == "child-3").unwrap();
2559 assert_eq!(root.parent_span_id, otel::SpanId::INVALID);
2560 assert_eq!(child1.parent_span_id, root.span_context.span_id());
2561 assert_eq!(child2.parent_span_id, child1.span_context.span_id());
2562
2563 assert_eq!(child3.parent_span_id, child1.span_context.span_id());
2565 }
2566
2567 #[test]
2568 fn follows_from_adds_link_with_context_activation() {
2569 follows_from_adds_link_impl(true);
2570 }
2571
2572 #[test]
2573 fn follows_from_adds_link_no_context_activation() {
2574 follows_from_adds_link_impl(false);
2575 }
2576
2577 fn follows_from_adds_link_impl(context_activation: bool) {
2578 use crate::OpenTelemetrySpanExt;
2579 let mut tracer = TestTracer::default();
2580 let subscriber = tracing_subscriber::registry().with(
2581 layer()
2582 .with_tracer(tracer.clone())
2583 .with_context_activation(context_activation),
2584 );
2585
2586 let span1_id = tracing::subscriber::with_default(subscriber, || {
2587 let span2 = tracing::debug_span!("span2");
2588 let span1 = tracing::debug_span!("span1");
2589
2590 let _ = span2.context();
2592
2593 span2.follows_from(&span1);
2595
2596 let _guard = span2.enter();
2598
2599 span1.context().span().span_context().span_id()
2601 });
2602
2603 let spans = tracer.spans();
2604 assert_eq!(spans.len(), 2, "Expected two spans to be exported");
2606 assert!(spans.iter().any(|span| span.name == "span1"));
2607 let span2 = spans
2608 .iter()
2609 .find(|span| span.name == "span2")
2610 .expect("Expected span2 to be exported");
2611
2612 let links = span2
2614 .links
2615 .iter()
2616 .map(|link| link.span_context.span_id())
2617 .collect::<Vec<_>>();
2618
2619 assert_eq!(
2621 links.len(),
2622 1,
2623 "Expected span to have one link from follows_from relationship"
2624 );
2625
2626 assert!(
2627 links.contains(&span1_id),
2628 "Link should point to the correct source span"
2629 );
2630 }
2631
2632 #[test]
2633 fn follows_from_multiple_links_with_context_activation() {
2634 follows_from_multiple_links_impl(true);
2635 }
2636
2637 #[test]
2638 fn follows_from_multiple_links_no_context_activation() {
2639 follows_from_multiple_links_impl(false);
2640 }
2641
2642 fn follows_from_multiple_links_impl(context_activation: bool) {
2643 use crate::OpenTelemetrySpanExt;
2644 let mut tracer = TestTracer::default();
2645 let subscriber = tracing_subscriber::registry().with(
2646 layer()
2647 .with_tracer(tracer.clone())
2648 .with_context_activation(context_activation),
2649 );
2650
2651 let (span1_id, span2_id) = tracing::subscriber::with_default(subscriber, || {
2652 let span3 = tracing::debug_span!("span3");
2653 let span2 = tracing::debug_span!("span2");
2654 let span1 = tracing::debug_span!("span1");
2655
2656 span3.follows_from(&span1);
2658 span3.follows_from(&span2);
2659
2660 let _guard = span3.enter();
2662
2663 (
2665 span1.context().span().span_context().span_id(),
2666 span2.context().span().span_context().span_id(),
2667 )
2668 });
2669
2670 let spans = tracer.spans();
2671 assert_eq!(spans.len(), 3, "Expected three spans to be exported");
2673 assert!(spans.iter().any(|span| span.name == "span1"));
2674 assert!(spans.iter().any(|span| span.name == "span2"));
2675 let span3 = spans
2676 .iter()
2677 .find(|span| span.name == "span3")
2678 .expect("Expected span3 to be exported");
2679
2680 let links = span3
2682 .links
2683 .iter()
2684 .map(|link| link.span_context.span_id())
2685 .collect::<Vec<_>>();
2686
2687 assert_eq!(
2689 links.len(),
2690 2,
2691 "Expected span to have two links from follows_from relationships"
2692 );
2693
2694 assert!(
2696 links[0] == span1_id && links[1] == span2_id,
2697 "Links should point to the correct source spans"
2698 );
2699 }
2700
2701 #[test]
2702 fn context_activation_disabled() {
2703 use tracing::span;
2704
2705 let mut tracer = TestTracer::default();
2706 let subscriber = tracing_subscriber::registry().with(
2707 layer()
2708 .with_tracer(tracer.clone())
2709 .with_context_activation(false),
2710 );
2711
2712 tracing::subscriber::with_default(subscriber, || {
2713 let _outer_guard =
2715 OtelContext::attach(OtelContext::default().with_value(ValueA("outer")));
2716 assert_eq!(OtelContext::current().get(), Some(&ValueA("outer")));
2717
2718 let root = span!(tracing::Level::TRACE, "tokio-tracing-span-parent");
2719
2720 drop(_outer_guard);
2722 assert!(OtelContext::current().get::<ValueA>().is_none());
2723
2724 let _enter_root = root.enter();
2727 assert!(OtelContext::current().get::<ValueA>().is_none());
2728
2729 let _inner_guard =
2731 OtelContext::attach(OtelContext::current_with_value(ValueB("inner")));
2732 assert!(OtelContext::current().get::<ValueA>().is_none());
2733 assert_eq!(OtelContext::current().get(), Some(&ValueB("inner")));
2734
2735 let child = span!(tracing::Level::TRACE, "tokio-tracing-span-child");
2736
2737 drop(_inner_guard);
2739 assert!(OtelContext::current().get::<ValueA>().is_none());
2740 assert!(OtelContext::current().get::<ValueB>().is_none());
2741
2742 let _enter_child = child.enter();
2745 assert!(OtelContext::current().get::<ValueA>().is_none());
2746 assert!(OtelContext::current().get::<ValueB>().is_none());
2747 });
2748
2749 let spans = tracer.spans();
2751 assert_eq!(spans.len(), 2);
2752 assert!(spans
2753 .iter()
2754 .any(|span| span.name == "tokio-tracing-span-parent"));
2755 assert!(spans
2756 .iter()
2757 .any(|span| span.name == "tokio-tracing-span-child"));
2758 }
2759}