1use crate::stack::IdValueStack;
2use crate::{OtelData, OtelDataState};
3use opentelemetry::ContextGuard;
4use opentelemetry::{
5 trace::{self as otel, noop, Span, SpanBuilder, SpanKind, Status, TraceContextExt},
6 Context as OtelContext, Key, KeyValue, StringValue, Value,
7};
8use std::cell::RefCell;
9use std::thread;
10#[cfg(not(all(target_arch = "wasm32", not(target_os = "wasi"))))]
11use std::time::Instant;
12use std::{any::TypeId, borrow::Cow};
13use std::{fmt, vec};
14use std::{marker, mem::take};
15use tracing_core::span::{self, Attributes, Id, Record};
16use tracing_core::{field, Event, Subscriber};
17#[cfg(feature = "tracing-log")]
18use tracing_log::NormalizeEvent;
19use tracing_subscriber::layer::Context;
20use tracing_subscriber::registry::LookupSpan;
21use tracing_subscriber::Layer;
22#[cfg(all(target_arch = "wasm32", not(target_os = "wasi")))]
23use web_time::Instant;
24
25const SPAN_NAME_FIELD: &str = "otel.name";
26const SPAN_KIND_FIELD: &str = "otel.kind";
27const SPAN_STATUS_CODE_FIELD: &str = "otel.status_code";
28const SPAN_STATUS_DESCRIPTION_FIELD: &str = "otel.status_description";
29
30const EVENT_EXCEPTION_NAME: &str = "exception";
31const FIELD_EXCEPTION_MESSAGE: &str = "exception.message";
32const FIELD_EXCEPTION_STACKTRACE: &str = "exception.stacktrace";
33
34pub struct OpenTelemetryLayer<S, T> {
40 tracer: T,
41 location: bool,
42 tracked_inactivity: bool,
43 with_threads: bool,
44 with_level: bool,
45 with_target: bool,
46 context_activation: bool,
47 sem_conv_config: SemConvConfig,
48 with_context: WithContext,
49 _registry: marker::PhantomData<S>,
50}
51
52impl<S> Default for OpenTelemetryLayer<S, noop::NoopTracer>
53where
54 S: Subscriber + for<'span> LookupSpan<'span>,
55{
56 fn default() -> Self {
57 OpenTelemetryLayer::new(noop::NoopTracer::new())
58 }
59}
60
61pub fn layer<S>() -> OpenTelemetryLayer<S, noop::NoopTracer>
77where
78 S: Subscriber + for<'span> LookupSpan<'span>,
79{
80 OpenTelemetryLayer::default()
81}
82
83pub(crate) struct WithContext {
94 #[allow(clippy::type_complexity)]
98 pub(crate) with_context: fn(&tracing::Dispatch, &span::Id, f: &mut dyn FnMut(&mut OtelData)),
99
100 #[allow(clippy::type_complexity)]
105 pub(crate) with_activated_context:
106 fn(&tracing::Dispatch, &span::Id, f: &mut dyn FnMut(&mut OtelData)),
107}
108
109impl WithContext {
110 pub(crate) fn with_context(
114 &self,
115 dispatch: &tracing::Dispatch,
116 id: &span::Id,
117 mut f: impl FnMut(&mut OtelData),
118 ) {
119 (self.with_context)(dispatch, id, &mut f)
120 }
121
122 pub(crate) fn with_activated_context(
129 &self,
130 dispatch: &tracing::Dispatch,
131 id: &span::Id,
132 mut f: impl FnMut(&mut OtelData),
133 ) {
134 (self.with_activated_context)(dispatch, id, &mut f)
135 }
136}
137
138fn str_to_span_kind(s: &str) -> Option<otel::SpanKind> {
139 match s {
140 s if s.eq_ignore_ascii_case("server") => Some(otel::SpanKind::Server),
141 s if s.eq_ignore_ascii_case("client") => Some(otel::SpanKind::Client),
142 s if s.eq_ignore_ascii_case("producer") => Some(otel::SpanKind::Producer),
143 s if s.eq_ignore_ascii_case("consumer") => Some(otel::SpanKind::Consumer),
144 s if s.eq_ignore_ascii_case("internal") => Some(otel::SpanKind::Internal),
145 _ => None,
146 }
147}
148
149fn str_to_status(s: &str) -> otel::Status {
150 match s {
151 s if s.eq_ignore_ascii_case("ok") => otel::Status::Ok,
152 s if s.eq_ignore_ascii_case("error") => otel::Status::error(""),
153 _ => otel::Status::Unset,
154 }
155}
156
157#[derive(Default)]
158struct SpanBuilderUpdates {
159 name: Option<Cow<'static, str>>,
160 span_kind: Option<SpanKind>,
161 status: Option<Status>,
162 attributes: Option<Vec<KeyValue>>,
163}
164
165impl SpanBuilderUpdates {
166 fn update(self, span_builder: &mut SpanBuilder) {
167 let Self {
168 name,
169 span_kind,
170 status,
171 attributes,
172 } = self;
173
174 if let Some(name) = name {
175 span_builder.name = name;
176 }
177 if let Some(span_kind) = span_kind {
178 span_builder.span_kind = Some(span_kind);
179 }
180 if let Some(status) = status {
181 span_builder.status = status;
182 }
183 if let Some(attributes) = attributes {
184 if let Some(builder_attributes) = &mut span_builder.attributes {
185 builder_attributes.extend(attributes);
186 } else {
187 span_builder.attributes = Some(attributes);
188 }
189 }
190 }
191
192 fn update_span(self, span: &opentelemetry::trace::SpanRef<'_>) {
193 let Self {
194 status, attributes, ..
195 } = self;
196
197 if let Some(status) = status {
198 span.set_status(status);
199 }
200 if let Some(attributes) = attributes {
201 span.set_attributes(attributes);
202 }
203 }
204}
205
206struct SpanEventVisitor<'a, 'b> {
207 event_builder: &'a mut otel::Event,
208 span_builder_updates: &'b mut Option<SpanBuilderUpdates>,
209 sem_conv_config: SemConvConfig,
210}
211
212impl field::Visit for SpanEventVisitor<'_, '_> {
213 fn record_bool(&mut self, field: &field::Field, value: bool) {
217 match field.name() {
218 "message" => self.event_builder.name = value.to_string().into(),
219 #[cfg(feature = "tracing-log")]
221 name if name.starts_with("log.") => (),
222 name => {
223 self.event_builder
224 .attributes
225 .push(KeyValue::new(name, value));
226 }
227 }
228 }
229
230 fn record_f64(&mut self, field: &field::Field, value: f64) {
234 match field.name() {
235 "message" => self.event_builder.name = value.to_string().into(),
236 #[cfg(feature = "tracing-log")]
238 name if name.starts_with("log.") => (),
239 name => {
240 self.event_builder
241 .attributes
242 .push(KeyValue::new(name, value));
243 }
244 }
245 }
246
247 fn record_i64(&mut self, field: &field::Field, value: i64) {
251 match field.name() {
252 "message" => self.event_builder.name = value.to_string().into(),
253 #[cfg(feature = "tracing-log")]
255 name if name.starts_with("log.") => (),
256 name => {
257 self.event_builder
258 .attributes
259 .push(KeyValue::new(name, value));
260 }
261 }
262 }
263
264 fn record_str(&mut self, field: &field::Field, value: &str) {
268 match field.name() {
269 "message" => self.event_builder.name = value.to_string().into(),
270 "error" if self.event_builder.name.is_empty() => {
274 if self.sem_conv_config.error_events_to_status {
275 self.span_builder_updates
276 .get_or_insert_with(SpanBuilderUpdates::default)
277 .status
278 .replace(otel::Status::error(format!("{value:?}")));
279 }
280 if self.sem_conv_config.error_events_to_exceptions {
281 self.event_builder.name = EVENT_EXCEPTION_NAME.into();
282 self.event_builder
283 .attributes
284 .push(KeyValue::new(FIELD_EXCEPTION_MESSAGE, format!("{value:?}")));
285 } else {
286 self.event_builder
287 .attributes
288 .push(KeyValue::new("error", format!("{value:?}")));
289 }
290 }
291 #[cfg(feature = "tracing-log")]
293 name if name.starts_with("log.") => (),
294 name => {
295 self.event_builder
296 .attributes
297 .push(KeyValue::new(name, value.to_string()));
298 }
299 }
300 }
301
302 fn record_debug(&mut self, field: &field::Field, value: &dyn fmt::Debug) {
307 match field.name() {
308 "message" => self.event_builder.name = format!("{value:?}").into(),
309 "error" if self.event_builder.name.is_empty() => {
313 if self.sem_conv_config.error_events_to_status {
314 self.span_builder_updates
315 .get_or_insert_with(SpanBuilderUpdates::default)
316 .status
317 .replace(otel::Status::error(format!("{value:?}")));
318 }
319 if self.sem_conv_config.error_events_to_exceptions {
320 self.event_builder.name = EVENT_EXCEPTION_NAME.into();
321 self.event_builder
322 .attributes
323 .push(KeyValue::new(FIELD_EXCEPTION_MESSAGE, format!("{value:?}")));
324 } else {
325 self.event_builder
326 .attributes
327 .push(KeyValue::new("error", format!("{value:?}")));
328 }
329 }
330 #[cfg(feature = "tracing-log")]
332 name if name.starts_with("log.") => (),
333 name => {
334 self.event_builder
335 .attributes
336 .push(KeyValue::new(name, format!("{value:?}")));
337 }
338 }
339 }
340
341 fn record_error(
346 &mut self,
347 field: &tracing_core::Field,
348 value: &(dyn std::error::Error + 'static),
349 ) {
350 let mut chain: Vec<StringValue> = Vec::new();
351 let mut next_err = value.source();
352
353 while let Some(err) = next_err {
354 chain.push(err.to_string().into());
355 next_err = err.source();
356 }
357
358 let error_msg = value.to_string();
359
360 if self.sem_conv_config.error_fields_to_exceptions {
361 self.event_builder.attributes.push(KeyValue::new(
362 Key::new(FIELD_EXCEPTION_MESSAGE),
363 Value::String(StringValue::from(error_msg.clone())),
364 ));
365
366 self.event_builder.attributes.push(KeyValue::new(
373 Key::new(FIELD_EXCEPTION_STACKTRACE),
374 Value::Array(chain.clone().into()),
375 ));
376 }
377
378 if self.sem_conv_config.error_records_to_exceptions {
379 let attributes = self
380 .span_builder_updates
381 .get_or_insert_with(SpanBuilderUpdates::default)
382 .attributes
383 .get_or_insert_with(Vec::new);
384
385 attributes.push(KeyValue::new(
386 FIELD_EXCEPTION_MESSAGE,
387 Value::String(error_msg.clone().into()),
388 ));
389
390 attributes.push(KeyValue::new(
397 FIELD_EXCEPTION_STACKTRACE,
398 Value::Array(chain.clone().into()),
399 ));
400 }
401
402 self.event_builder.attributes.push(KeyValue::new(
403 Key::new(field.name()),
404 Value::String(StringValue::from(error_msg)),
405 ));
406 self.event_builder.attributes.push(KeyValue::new(
407 Key::new(format!("{}.chain", field.name())),
408 Value::Array(chain.into()),
409 ));
410 }
411}
412
413#[derive(Clone, Copy)]
415struct SemConvConfig {
416 error_fields_to_exceptions: bool,
421
422 error_records_to_exceptions: bool,
427
428 error_events_to_status: bool,
437
438 error_events_to_exceptions: bool,
446}
447
448struct SpanAttributeVisitor<'a> {
449 span_builder_updates: &'a mut SpanBuilderUpdates,
450 sem_conv_config: SemConvConfig,
451}
452
453impl SpanAttributeVisitor<'_> {
454 fn record(&mut self, attribute: KeyValue) {
455 self.span_builder_updates
456 .attributes
457 .get_or_insert_with(Vec::new)
458 .push(KeyValue::new(attribute.key, attribute.value));
459 }
460}
461
462impl field::Visit for SpanAttributeVisitor<'_> {
463 fn record_bool(&mut self, field: &field::Field, value: bool) {
467 self.record(KeyValue::new(field.name(), value));
468 }
469
470 fn record_f64(&mut self, field: &field::Field, value: f64) {
474 self.record(KeyValue::new(field.name(), value));
475 }
476
477 fn record_i64(&mut self, field: &field::Field, value: i64) {
481 self.record(KeyValue::new(field.name(), value));
482 }
483
484 fn record_str(&mut self, field: &field::Field, value: &str) {
488 match field.name() {
489 SPAN_NAME_FIELD => self.span_builder_updates.name = Some(value.to_string().into()),
490 SPAN_KIND_FIELD => self.span_builder_updates.span_kind = str_to_span_kind(value),
491 SPAN_STATUS_CODE_FIELD => self.span_builder_updates.status = Some(str_to_status(value)),
492 SPAN_STATUS_DESCRIPTION_FIELD => {
493 self.span_builder_updates.status = Some(otel::Status::error(value.to_string()))
494 }
495 _ => self.record(KeyValue::new(field.name(), value.to_string())),
496 }
497 }
498
499 fn record_debug(&mut self, field: &field::Field, value: &dyn fmt::Debug) {
504 match field.name() {
505 SPAN_NAME_FIELD => self.span_builder_updates.name = Some(format!("{value:?}").into()),
506 SPAN_KIND_FIELD => {
507 self.span_builder_updates.span_kind = str_to_span_kind(&format!("{value:?}"))
508 }
509 SPAN_STATUS_CODE_FIELD => {
510 self.span_builder_updates.status = Some(str_to_status(&format!("{value:?}")))
511 }
512 SPAN_STATUS_DESCRIPTION_FIELD => {
513 self.span_builder_updates.status = Some(otel::Status::error(format!("{value:?}")))
514 }
515 _ => self.record(KeyValue::new(
516 Key::new(field.name()),
517 Value::String(format!("{value:?}").into()),
518 )),
519 }
520 }
521
522 fn record_error(
527 &mut self,
528 field: &tracing_core::Field,
529 value: &(dyn std::error::Error + 'static),
530 ) {
531 let mut chain: Vec<StringValue> = Vec::new();
532 let mut next_err = value.source();
533
534 while let Some(err) = next_err {
535 chain.push(err.to_string().into());
536 next_err = err.source();
537 }
538
539 let error_msg = value.to_string();
540
541 if self.sem_conv_config.error_fields_to_exceptions {
542 self.record(KeyValue::new(
543 Key::new(FIELD_EXCEPTION_MESSAGE),
544 Value::from(error_msg.clone()),
545 ));
546
547 self.record(KeyValue::new(
554 Key::new(FIELD_EXCEPTION_STACKTRACE),
555 Value::Array(chain.clone().into()),
556 ));
557 }
558
559 self.record(KeyValue::new(
560 Key::new(field.name()),
561 Value::String(error_msg.into()),
562 ));
563 self.record(KeyValue::new(
564 Key::new(format!("{}.chain", field.name())),
565 Value::Array(chain.into()),
566 ));
567 }
568}
569
570impl<S, T> OpenTelemetryLayer<S, T>
571where
572 S: Subscriber + for<'span> LookupSpan<'span>,
573 T: otel::Tracer + 'static,
574 T::Span: Send + Sync,
575{
576 pub fn new(tracer: T) -> Self {
611 OpenTelemetryLayer {
612 tracer,
613 location: true,
614 tracked_inactivity: true,
615 with_threads: true,
616 with_level: false,
617 with_target: true,
618 context_activation: true,
619 sem_conv_config: SemConvConfig {
620 error_fields_to_exceptions: true,
621 error_records_to_exceptions: true,
622 error_events_to_exceptions: true,
623 error_events_to_status: true,
624 },
625 with_context: WithContext {
626 with_context: Self::get_context,
627 with_activated_context: Self::get_activated_context,
628 },
629 _registry: marker::PhantomData,
630 }
631 }
632
633 pub fn with_tracer<Tracer>(self, tracer: Tracer) -> OpenTelemetryLayer<S, Tracer>
667 where
668 Tracer: otel::Tracer + 'static,
669 Tracer::Span: Send + Sync,
670 {
671 OpenTelemetryLayer {
672 tracer,
673 location: self.location,
674 tracked_inactivity: self.tracked_inactivity,
675 with_threads: self.with_threads,
676 with_level: self.with_level,
677 with_target: self.with_target,
678 context_activation: self.context_activation,
679 sem_conv_config: self.sem_conv_config,
680 with_context: WithContext {
681 with_context: OpenTelemetryLayer::<S, Tracer>::get_context,
682 with_activated_context: OpenTelemetryLayer::<S, Tracer>::get_activated_context,
683 },
684 _registry: self._registry,
685 }
687 }
688
689 pub fn with_error_fields_to_exceptions(self, error_fields_to_exceptions: bool) -> Self {
705 Self {
706 sem_conv_config: SemConvConfig {
707 error_fields_to_exceptions,
708 ..self.sem_conv_config
709 },
710 ..self
711 }
712 }
713
714 pub fn with_error_events_to_status(self, error_events_to_status: bool) -> Self {
719 Self {
720 sem_conv_config: SemConvConfig {
721 error_events_to_status,
722 ..self.sem_conv_config
723 },
724 ..self
725 }
726 }
727
728 pub fn with_error_events_to_exceptions(self, error_events_to_exceptions: bool) -> Self {
739 Self {
740 sem_conv_config: SemConvConfig {
741 error_events_to_exceptions,
742 ..self.sem_conv_config
743 },
744 ..self
745 }
746 }
747
748 pub fn with_error_records_to_exceptions(self, error_records_to_exceptions: bool) -> Self {
764 Self {
765 sem_conv_config: SemConvConfig {
766 error_records_to_exceptions,
767 ..self.sem_conv_config
768 },
769 ..self
770 }
771 }
772
773 pub fn with_location(self, location: bool) -> Self {
783 Self { location, ..self }
784 }
785
786 pub fn with_tracked_inactivity(self, tracked_inactivity: bool) -> Self {
792 Self {
793 tracked_inactivity,
794 ..self
795 }
796 }
797
798 pub fn with_threads(self, threads: bool) -> Self {
806 Self {
807 with_threads: threads,
808 ..self
809 }
810 }
811
812 pub fn with_level(self, level: bool) -> Self {
819 Self {
820 with_level: level,
821 ..self
822 }
823 }
824
825 pub fn with_target(self, target: bool) -> Self {
829 Self {
830 with_target: target,
831 ..self
832 }
833 }
834
835 pub fn with_context_activation(self, context_activation: bool) -> Self {
843 Self {
844 context_activation,
845 ..self
846 }
847 }
848
849 fn parent_context(&self, attrs: &Attributes<'_>, ctx: &Context<'_, S>) -> OtelContext {
857 if let Some(parent) = attrs.parent() {
858 if let Some(span) = ctx.span(parent) {
871 let mut extensions = span.extensions_mut();
872 if let Some(otel_data) = extensions.get_mut::<OtelData>() {
873 return self.with_started_cx(otel_data, &|cx| cx.clone());
876 }
877 }
878 }
879
880 if attrs.is_contextual() {
881 if self.context_activation {
882 OtelContext::current()
885 } else {
886 ctx.lookup_current()
889 .and_then(|span| {
890 let mut extensions = span.extensions_mut();
891 extensions
892 .get_mut::<OtelData>()
893 .map(|data| self.with_started_cx(data, &|cx| cx.clone()))
894 })
895 .unwrap_or_else(OtelContext::current)
896 }
897 } else {
898 OtelContext::default()
899 }
900 }
901
902 fn get_context(dispatch: &tracing::Dispatch, id: &span::Id, f: &mut dyn FnMut(&mut OtelData)) {
915 let subscriber = dispatch
916 .downcast_ref::<S>()
917 .expect("subscriber should downcast to expected type; this is a bug!");
918 let span = subscriber
919 .span(id)
920 .expect("registry should have a span for the current ID");
921
922 let mut extensions = span.extensions_mut();
923 if let Some(otel_data) = extensions.get_mut::<OtelData>() {
924 f(otel_data);
925 }
926 }
927
928 fn get_activated_context(
929 dispatch: &tracing::Dispatch,
930 id: &span::Id,
931 f: &mut dyn FnMut(&mut OtelData),
932 ) {
933 let subscriber = dispatch
934 .downcast_ref::<S>()
935 .expect("subscriber should downcast to expected type; this is a bug!");
936 let span = subscriber
937 .span(id)
938 .expect("registry should have a span for the current ID");
939
940 let layer = dispatch
941 .downcast_ref::<OpenTelemetryLayer<S, T>>()
942 .expect("layer should downcast to expected type; this is a bug!");
943
944 let mut extensions = span.extensions_mut();
945 if let Some(otel_data) = extensions.get_mut::<OtelData>() {
946 layer.start_cx(otel_data);
948 f(otel_data);
949 }
950 }
951
952 fn extra_span_attrs(&self) -> usize {
953 let mut extra_attrs = 0;
954 if self.location {
955 extra_attrs += 3;
956 }
957 if self.with_threads {
958 extra_attrs += 2;
959 }
960 if self.with_level {
961 extra_attrs += 1;
962 }
963 if self.with_target {
964 extra_attrs += 1;
965 }
966 extra_attrs
967 }
968
969 fn start_cx(&self, otel_data: &mut OtelData) {
974 if let OtelDataState::Context { .. } = &otel_data.state {
975 } else if let OtelDataState::Builder { builder, parent_cx } = take(&mut otel_data.state) {
977 let span = builder.start_with_context(&self.tracer, &parent_cx);
978 let current_cx = parent_cx.with_span(span);
979 otel_data.state = OtelDataState::Context { current_cx };
980 }
981 }
982
983 fn with_started_cx<U>(&self, otel_data: &mut OtelData, f: &dyn Fn(&OtelContext) -> U) -> U {
984 self.start_cx(otel_data);
985 match &otel_data.state {
986 OtelDataState::Context { current_cx, .. } => f(current_cx),
987 _ => panic!("OtelDataState should be a Context after starting it; this is a bug!"),
988 }
989 }
990}
991
992thread_local! {
993 static THREAD_ID: u64 = {
994 thread_id_integer(thread::current().id())
1002 };
1003}
1004
1005thread_local! {
1006 static GUARD_STACK: RefCell<IdContextGuardStack> = RefCell::new(IdContextGuardStack::new());
1007}
1008
1009type IdContextGuardStack = IdValueStack<ContextGuard>;
1010
1011impl<S, T> Layer<S> for OpenTelemetryLayer<S, T>
1012where
1013 S: Subscriber + for<'span> LookupSpan<'span>,
1014 T: otel::Tracer + 'static,
1015 T::Span: Send + Sync,
1016{
1017 fn on_new_span(&self, attrs: &Attributes<'_>, id: &span::Id, ctx: Context<'_, S>) {
1022 let span = ctx.span(id).expect("Span not found, this is a bug");
1023 let mut extensions = span.extensions_mut();
1024
1025 if self.tracked_inactivity && extensions.get_mut::<Timings>().is_none() {
1026 extensions.insert(Timings::new());
1027 }
1028
1029 let parent_cx = self.parent_context(attrs, &ctx);
1030 let mut builder = self
1031 .tracer
1032 .span_builder(attrs.metadata().name())
1033 .with_start_time(crate::time::now());
1034
1035 let builder_attrs = builder.attributes.get_or_insert(Vec::with_capacity(
1036 attrs.fields().len() + self.extra_span_attrs(),
1037 ));
1038
1039 if self.location {
1040 let meta = attrs.metadata();
1041
1042 if let Some(filename) = meta.file() {
1043 builder_attrs.push(KeyValue::new("code.file.path", filename));
1044 }
1045
1046 if let Some(module) = meta.module_path() {
1047 builder_attrs.push(KeyValue::new("code.module.name", module));
1048 }
1049
1050 if let Some(line) = meta.line() {
1051 builder_attrs.push(KeyValue::new("code.line.number", line as i64));
1052 }
1053 }
1054
1055 if self.with_threads {
1056 THREAD_ID.with(|id| builder_attrs.push(KeyValue::new("thread.id", *id as i64)));
1057 if let Some(name) = std::thread::current().name() {
1058 builder_attrs.push(KeyValue::new("thread.name", name.to_string()));
1063 }
1064 }
1065
1066 if self.with_level {
1067 builder_attrs.push(KeyValue::new("level", attrs.metadata().level().as_str()));
1068 }
1069 if self.with_target {
1070 builder_attrs.push(KeyValue::new("target", attrs.metadata().target()));
1071 }
1072
1073 let mut updates = SpanBuilderUpdates::default();
1074 attrs.record(&mut SpanAttributeVisitor {
1075 span_builder_updates: &mut updates,
1076 sem_conv_config: self.sem_conv_config,
1077 });
1078
1079 updates.update(&mut builder);
1080 extensions.insert(OtelData {
1081 state: OtelDataState::Builder { builder, parent_cx },
1082 end_time: None,
1083 });
1084 }
1085
1086 fn on_enter(&self, id: &span::Id, ctx: Context<'_, S>) {
1087 if !self.context_activation && !self.tracked_inactivity {
1088 return;
1089 }
1090
1091 let span = ctx.span(id).expect("Span not found, this is a bug");
1092 let mut extensions = span.extensions_mut();
1093
1094 if self.context_activation {
1095 if let Some(otel_data) = extensions.get_mut::<OtelData>() {
1096 self.with_started_cx(otel_data, &|cx| {
1097 let guard = cx.clone().attach();
1098 GUARD_STACK.with(|stack| stack.borrow_mut().push(id.clone(), guard));
1099 });
1100 }
1101
1102 if !self.tracked_inactivity {
1103 return;
1104 }
1105 }
1106
1107 if let Some(timings) = extensions.get_mut::<Timings>() {
1108 if timings.entered_count == 0 {
1109 let now = Instant::now();
1110 timings.idle += (now - timings.last).as_nanos() as i64;
1111 timings.last = now;
1112 }
1113 timings.entered_count += 1;
1114 }
1115 }
1116
1117 fn on_exit(&self, id: &span::Id, ctx: Context<'_, S>) {
1118 let span = ctx.span(id).expect("Span not found, this is a bug");
1119 let mut extensions = span.extensions_mut();
1120
1121 if let Some(otel_data) = extensions.get_mut::<OtelData>() {
1122 otel_data.end_time = Some(crate::time::now());
1123 if self.context_activation {
1124 GUARD_STACK.with(|stack| stack.borrow_mut().pop(id));
1125 }
1126 }
1127
1128 if !self.tracked_inactivity {
1129 return;
1130 }
1131
1132 if let Some(timings) = extensions.get_mut::<Timings>() {
1133 timings.entered_count -= 1;
1134 if timings.entered_count == 0 {
1135 let now = Instant::now();
1136 timings.busy += (now - timings.last).as_nanos() as i64;
1137 timings.last = now;
1138 }
1139 }
1140 }
1141
1142 fn on_record(&self, id: &Id, values: &Record<'_>, ctx: Context<'_, S>) {
1146 let span = ctx.span(id).expect("Span not found, this is a bug");
1147 let mut updates = SpanBuilderUpdates::default();
1148 values.record(&mut SpanAttributeVisitor {
1149 span_builder_updates: &mut updates,
1150 sem_conv_config: self.sem_conv_config,
1151 });
1152 let mut extensions = span.extensions_mut();
1153 if let Some(otel_data) = extensions.get_mut::<OtelData>() {
1154 match &mut otel_data.state {
1155 OtelDataState::Builder { builder, .. } => {
1156 updates.update(builder);
1158 }
1159 OtelDataState::Context { current_cx, .. } => {
1160 updates.update_span(¤t_cx.span());
1162 }
1163 }
1164 }
1165 }
1166
1167 fn on_follows_from(&self, id: &Id, follows: &Id, ctx: Context<S>) {
1168 let span = ctx.span(id).expect("Span not found, this is a bug");
1169 let mut extensions = span.extensions_mut();
1170 let data = extensions
1171 .get_mut::<OtelData>()
1172 .expect("Missing otel data span extensions");
1173
1174 if let Some(follows_span) = ctx.span(follows) {
1178 let mut follows_extensions = follows_span.extensions_mut();
1179 let follows_data = follows_extensions
1180 .get_mut::<OtelData>()
1181 .expect("Missing otel data span extensions");
1182 let follows_context =
1183 self.with_started_cx(follows_data, &|cx| cx.span().span_context().clone());
1184 match &mut data.state {
1185 OtelDataState::Builder { builder, .. } => {
1186 if let Some(ref mut links) = builder.links {
1187 links.push(otel::Link::with_context(follows_context));
1188 } else {
1189 builder.links = Some(vec![otel::Link::with_context(follows_context)]);
1190 }
1191 }
1192 OtelDataState::Context { current_cx, .. } => {
1193 current_cx.span().add_link(follows_context, vec![]);
1194 }
1195 }
1196 }
1197 }
1198
1199 fn on_event(&self, event: &Event<'_>, ctx: Context<'_, S>) {
1208 if let Some(span) = event.parent().and_then(|id| ctx.span(id)).or_else(|| {
1210 event
1211 .is_contextual()
1212 .then(|| ctx.lookup_current())
1213 .flatten()
1214 }) {
1215 #[cfg(feature = "tracing-log")]
1218 let normalized_meta = event.normalized_metadata();
1219 #[cfg(feature = "tracing-log")]
1220 let meta = normalized_meta.as_ref().unwrap_or_else(|| event.metadata());
1221 #[cfg(not(feature = "tracing-log"))]
1222 let meta = event.metadata();
1223
1224 let target = Key::new("target");
1225
1226 #[cfg(feature = "tracing-log")]
1227 let target = if normalized_meta.is_some() {
1228 KeyValue::new(target, Value::String(meta.target().to_owned().into()))
1229 } else {
1230 KeyValue::new(target, Value::String(event.metadata().target().into()))
1231 };
1232
1233 #[cfg(not(feature = "tracing-log"))]
1234 let target = KeyValue::new(target, Value::String(meta.target().into()));
1235
1236 let mut otel_event = otel::Event::new(
1237 String::new(),
1238 crate::time::now(),
1239 vec![
1240 KeyValue::new(
1241 Key::new("level"),
1242 Value::String(meta.level().as_str().into()),
1243 ),
1244 target,
1245 ],
1246 0,
1247 );
1248
1249 let mut builder_updates = None;
1250 event.record(&mut SpanEventVisitor {
1251 event_builder: &mut otel_event,
1252 span_builder_updates: &mut builder_updates,
1253 sem_conv_config: self.sem_conv_config,
1254 });
1255
1256 if otel_event.name.is_empty() {
1262 otel_event.name = std::borrow::Cow::Borrowed(event.metadata().name());
1263 }
1264
1265 let mut extensions = span.extensions_mut();
1266
1267 if let Some(otel_data) = extensions.get_mut::<OtelData>() {
1268 if self.location {
1269 #[cfg(not(feature = "tracing-log"))]
1270 let normalized_meta: Option<tracing_core::Metadata<'_>> = None;
1271 let (file, module) = match &normalized_meta {
1272 Some(meta) => (
1273 meta.file().map(|s| Value::from(s.to_owned())),
1274 meta.module_path().map(|s| Value::from(s.to_owned())),
1275 ),
1276 None => (
1277 event.metadata().file().map(Value::from),
1278 event.metadata().module_path().map(Value::from),
1279 ),
1280 };
1281
1282 if let Some(file) = file {
1283 otel_event
1284 .attributes
1285 .push(KeyValue::new("code.file.path", file));
1286 }
1287 if let Some(module) = module {
1288 otel_event
1289 .attributes
1290 .push(KeyValue::new("code.module.name", module));
1291 }
1292 if let Some(line) = meta.line() {
1293 otel_event
1294 .attributes
1295 .push(KeyValue::new("code.line.number", line as i64));
1296 }
1297 }
1298
1299 match &mut otel_data.state {
1300 OtelDataState::Builder { builder, .. } => {
1301 if builder.status == otel::Status::Unset
1302 && *meta.level() == tracing_core::Level::ERROR
1303 {
1304 builder.status = otel::Status::error("");
1305 }
1306 if let Some(builder_updates) = builder_updates {
1307 builder_updates.update(builder);
1308 }
1309 if let Some(ref mut events) = builder.events {
1310 events.push(otel_event);
1311 } else {
1312 builder.events = Some(vec![otel_event]);
1313 }
1314 }
1315 OtelDataState::Context { current_cx, .. } => {
1316 let span = current_cx.span();
1317 if *meta.level() == tracing_core::Level::ERROR {
1319 span.set_status(otel::Status::error(""));
1320 }
1321 if let Some(builder_updates) = builder_updates {
1322 builder_updates.update_span(&span);
1323 }
1324 span.add_event(otel_event.name, otel_event.attributes);
1325 }
1326 }
1327 };
1328 }
1329 }
1330
1331 fn on_close(&self, id: span::Id, ctx: Context<'_, S>) {
1335 let span = ctx.span(&id).expect("Span not found, this is a bug");
1336 let (otel_data, timings) = {
1338 let mut extensions = span.extensions_mut();
1339 let timings = if self.tracked_inactivity {
1340 extensions.remove::<Timings>()
1341 } else {
1342 None
1343 };
1344 (extensions.remove::<OtelData>(), timings)
1345 };
1346
1347 if let Some(OtelData { state, end_time }) = otel_data {
1348 let timings = timings.map(|timings| {
1350 let busy_ns = Key::new("busy_ns");
1351 let idle_ns = Key::new("idle_ns");
1352
1353 vec![
1354 KeyValue::new(busy_ns, timings.busy),
1355 KeyValue::new(idle_ns, timings.idle),
1356 ]
1357 });
1358
1359 match state {
1360 OtelDataState::Builder { builder, parent_cx } => {
1361 let mut span = builder.start_with_context(&self.tracer, &parent_cx);
1363 if let Some(timings) = timings {
1364 span.set_attributes(timings)
1365 };
1366 if let Some(end_time) = end_time {
1367 span.end_with_timestamp(end_time);
1368 } else {
1369 span.end();
1370 }
1371 }
1372 OtelDataState::Context { current_cx } => {
1373 let span = current_cx.span();
1374 if let Some(timings) = timings {
1375 span.set_attributes(timings)
1376 };
1377 end_time
1378 .map_or_else(|| span.end(), |end_time| span.end_with_timestamp(end_time));
1379 }
1380 }
1381 }
1382 }
1383
1384 unsafe fn downcast_raw(&self, id: TypeId) -> Option<*const ()> {
1387 match id {
1388 id if id == TypeId::of::<Self>() => Some(self as *const _ as *const ()),
1389 id if id == TypeId::of::<WithContext>() => {
1390 Some(&self.with_context as *const _ as *const ())
1391 }
1392 _ => None,
1393 }
1394 }
1395}
1396
1397struct Timings {
1398 idle: i64,
1399 busy: i64,
1400 last: Instant,
1401 entered_count: u64,
1402}
1403
1404impl Timings {
1405 fn new() -> Self {
1406 Self {
1407 idle: 0,
1408 busy: 0,
1409 last: Instant::now(),
1410 entered_count: 0,
1411 }
1412 }
1413}
1414
1415fn thread_id_integer(id: thread::ThreadId) -> u64 {
1416 let thread_id = format!("{id:?}");
1417 thread_id
1418 .trim_start_matches("ThreadId(")
1419 .trim_end_matches(')')
1420 .parse::<u64>()
1421 .expect("thread ID should parse as an integer")
1422}
1423
1424#[cfg(test)]
1425mod tests {
1426 use crate::OpenTelemetrySpanExt;
1427
1428 use super::*;
1429 use opentelemetry::trace::{SpanContext, TraceFlags, TracerProvider};
1430 use opentelemetry_sdk::trace::SpanExporter;
1431 use std::{collections::HashMap, error::Error, fmt::Display, time::SystemTime};
1432 use tracing::trace_span;
1433 use tracing_subscriber::prelude::*;
1434
1435 #[derive(Debug, Clone)]
1436 struct TestTracer {
1437 tracer: opentelemetry_sdk::trace::Tracer,
1438 exporter: opentelemetry_sdk::trace::InMemorySpanExporter,
1439 }
1440
1441 impl TestTracer {
1442 fn spans(&mut self) -> Vec<opentelemetry_sdk::trace::SpanData> {
1443 self.exporter
1444 .force_flush()
1445 .expect("problems flushing spans");
1446 self.exporter
1447 .get_finished_spans()
1448 .expect("problems recording spans")
1449 }
1450
1451 fn with_data<T>(&mut self, f: impl FnOnce(&opentelemetry_sdk::trace::SpanData) -> T) -> T {
1452 let spans = self.spans();
1453 f(spans.first().expect("no spans recorded"))
1454 }
1455
1456 fn attributes(&mut self) -> HashMap<String, Value> {
1457 self.with_data(|data| {
1458 data.attributes
1459 .iter()
1460 .map(|kv| (kv.key.to_string(), kv.value.clone()))
1461 .collect()
1462 })
1463 }
1464 }
1465
1466 impl Default for TestTracer {
1467 fn default() -> Self {
1468 let exporter = opentelemetry_sdk::trace::InMemorySpanExporter::default();
1469 let provider = opentelemetry_sdk::trace::SdkTracerProvider::builder()
1470 .with_simple_exporter(exporter.clone())
1471 .build();
1472 let tracer = provider.tracer("test-tracer");
1473 Self { tracer, exporter }
1474 }
1475 }
1476
1477 impl opentelemetry::trace::Tracer for TestTracer {
1478 type Span = opentelemetry_sdk::trace::Span;
1479
1480 fn build_with_context(&self, builder: SpanBuilder, parent_cx: &OtelContext) -> Self::Span {
1481 self.tracer.build_with_context(builder, parent_cx)
1482 }
1483 }
1484
1485 #[derive(Debug, Clone)]
1486 struct TestSpan(otel::SpanContext);
1487 impl otel::Span for TestSpan {
1488 fn add_event_with_timestamp<T: Into<Cow<'static, str>>>(
1489 &mut self,
1490 _: T,
1491 _: SystemTime,
1492 _: Vec<KeyValue>,
1493 ) {
1494 }
1495 fn span_context(&self) -> &otel::SpanContext {
1496 &self.0
1497 }
1498 fn is_recording(&self) -> bool {
1499 false
1500 }
1501 fn set_attribute(&mut self, _attribute: KeyValue) {}
1502 fn set_status(&mut self, _status: otel::Status) {}
1503 fn update_name<T: Into<Cow<'static, str>>>(&mut self, _new_name: T) {}
1504 fn add_link(&mut self, _span_context: SpanContext, _attributes: Vec<KeyValue>) {}
1505 fn end_with_timestamp(&mut self, _timestamp: SystemTime) {}
1506 }
1507
1508 #[derive(Debug)]
1509 struct TestDynError {
1510 msg: &'static str,
1511 source: Option<Box<TestDynError>>,
1512 }
1513 impl Display for TestDynError {
1514 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1515 write!(f, "{}", self.msg)
1516 }
1517 }
1518 impl Error for TestDynError {
1519 fn source(&self) -> Option<&(dyn Error + 'static)> {
1520 match &self.source {
1521 Some(source) => Some(source),
1522 None => None,
1523 }
1524 }
1525 }
1526 impl TestDynError {
1527 fn new(msg: &'static str) -> Self {
1528 Self { msg, source: None }
1529 }
1530 fn with_parent(self, parent_msg: &'static str) -> Self {
1531 Self {
1532 msg: parent_msg,
1533 source: Some(Box::new(self)),
1534 }
1535 }
1536 }
1537
1538 #[test]
1539 fn dynamic_span_names() {
1540 let dynamic_name = "GET http://example.com".to_string();
1541 let mut tracer = TestTracer::default();
1542 let subscriber = tracing_subscriber::registry().with(layer().with_tracer(tracer.clone()));
1543
1544 tracing::subscriber::with_default(subscriber, || {
1545 tracing::debug_span!("static_name", otel.name = dynamic_name.as_str());
1546 });
1547
1548 let recorded_name = tracer.spans().first().unwrap().name.clone();
1549 assert_eq!(recorded_name, dynamic_name.as_str())
1550 }
1551
1552 #[test]
1553 fn span_kind() {
1554 let mut tracer = TestTracer::default();
1555 let subscriber = tracing_subscriber::registry().with(layer().with_tracer(tracer.clone()));
1556
1557 tracing::subscriber::with_default(subscriber, || {
1558 tracing::debug_span!("request", otel.kind = "server");
1559 });
1560
1561 let recorded_kind = tracer.with_data(|data| data.span_kind.clone());
1562 assert_eq!(recorded_kind, otel::SpanKind::Server)
1563 }
1564
1565 #[test]
1566 fn span_status_code() {
1567 let mut tracer = TestTracer::default();
1568 let subscriber = tracing_subscriber::registry().with(layer().with_tracer(tracer.clone()));
1569
1570 tracing::subscriber::with_default(subscriber, || {
1571 tracing::debug_span!("request", otel.status_code = ?otel::Status::Ok);
1572 });
1573
1574 let recorded_status = tracer.with_data(|data| data.status.clone());
1575 assert_eq!(recorded_status, otel::Status::Ok)
1576 }
1577
1578 #[test]
1579 fn span_status_description() {
1580 let mut tracer = TestTracer::default();
1581 let subscriber = tracing_subscriber::registry().with(layer().with_tracer(tracer.clone()));
1582
1583 let message = "message";
1584
1585 tracing::subscriber::with_default(subscriber, || {
1586 tracing::debug_span!("request", otel.status_description = message);
1587 });
1588
1589 let recorded_status_message = tracer.with_data(|data| data.status.clone());
1590
1591 assert_eq!(recorded_status_message, otel::Status::error(message))
1592 }
1593
1594 #[test]
1595 fn trace_id_from_existing_context_with_context_activation() {
1596 trace_id_from_existing_context_impl(true);
1597 }
1598
1599 #[test]
1600 fn trace_id_from_existing_context_no_context_activation() {
1601 trace_id_from_existing_context_impl(false);
1602 }
1603
1604 fn trace_id_from_existing_context_impl(context_activation: bool) {
1605 let mut tracer = TestTracer::default();
1606 let subscriber = tracing_subscriber::registry().with(
1607 layer()
1608 .with_tracer(tracer.clone())
1609 .with_context_activation(context_activation),
1610 );
1611 let trace_id = otel::TraceId::from(42u128);
1612 let existing_cx = OtelContext::current_with_span(TestSpan(otel::SpanContext::new(
1613 trace_id,
1614 otel::SpanId::from(1u64),
1615 TraceFlags::SAMPLED,
1616 false,
1617 Default::default(),
1618 )));
1619 let _g = existing_cx.attach();
1620
1621 tracing::subscriber::with_default(subscriber, || {
1622 tracing::debug_span!("request", otel.kind = "server");
1623 });
1624
1625 let recorded_trace_id = tracer.with_data(|data| data.span_context.trace_id());
1626 assert_eq!(recorded_trace_id, trace_id)
1627 }
1628
1629 #[test]
1630 fn includes_timings_with_context_activation() {
1631 includes_timings_impl(true);
1632 }
1633
1634 #[test]
1635 fn includes_timings_no_context_activation() {
1636 includes_timings_impl(false);
1637 }
1638
1639 fn includes_timings_impl(context_activation: bool) {
1640 let mut tracer = TestTracer::default();
1641 let subscriber = tracing_subscriber::registry().with(
1642 layer()
1643 .with_tracer(tracer.clone())
1644 .with_tracked_inactivity(true)
1645 .with_context_activation(context_activation),
1646 );
1647
1648 tracing::subscriber::with_default(subscriber, || {
1649 tracing::debug_span!("request");
1650 });
1651
1652 let attributes = tracer.attributes();
1653
1654 assert!(attributes.contains_key("idle_ns"));
1655 assert!(attributes.contains_key("busy_ns"));
1656 }
1657
1658 #[test]
1659 fn records_error_fields_with_context_activation() {
1660 records_error_fields_impl(true);
1661 }
1662
1663 #[test]
1664 fn records_error_fields_no_context_activation() {
1665 records_error_fields_impl(false);
1666 }
1667
1668 fn records_error_fields_impl(context_activation: bool) {
1669 let mut tracer = TestTracer::default();
1670 let subscriber = tracing_subscriber::registry().with(
1671 layer()
1672 .with_tracer(tracer.clone())
1673 .with_context_activation(context_activation),
1674 );
1675
1676 let err = TestDynError::new("base error")
1677 .with_parent("intermediate error")
1678 .with_parent("user error");
1679
1680 tracing::subscriber::with_default(subscriber, || {
1681 tracing::debug_span!(
1682 "request",
1683 error = &err as &(dyn std::error::Error + 'static)
1684 );
1685 });
1686
1687 let attributes = tracer.attributes();
1688
1689 assert_eq!(attributes["error"].as_str(), "user error");
1690 assert_eq!(
1691 attributes["error.chain"],
1692 Value::Array(
1693 vec![
1694 StringValue::from("intermediate error"),
1695 StringValue::from("base error")
1696 ]
1697 .into()
1698 )
1699 );
1700
1701 assert_eq!(attributes[FIELD_EXCEPTION_MESSAGE].as_str(), "user error");
1702 assert_eq!(
1703 attributes[FIELD_EXCEPTION_STACKTRACE],
1704 Value::Array(
1705 vec![
1706 StringValue::from("intermediate error"),
1707 StringValue::from("base error")
1708 ]
1709 .into()
1710 )
1711 );
1712 }
1713
1714 #[test]
1715 fn records_event_name_with_context_activation() {
1716 records_event_name_impl(true);
1717 }
1718
1719 #[test]
1720 fn records_event_name_no_context_activation() {
1721 records_event_name_impl(false);
1722 }
1723
1724 fn records_event_name_impl(context_activation: bool) {
1725 let mut tracer = TestTracer::default();
1726 let subscriber = tracing_subscriber::registry().with(
1727 layer()
1728 .with_tracer(tracer.clone())
1729 .with_context_activation(context_activation),
1730 );
1731
1732 tracing::subscriber::with_default(subscriber, || {
1733 tracing::debug_span!("test span").in_scope(|| {
1734 tracing::event!(tracing::Level::INFO, "event name 1"); tracing::event!(name: "event name 2", tracing::Level::INFO, field1 = "field1");
1736 tracing::event!(name: "event name 3", tracing::Level::INFO, error = "field2");
1737 tracing::event!(name: "event name 4", tracing::Level::INFO, message = "field3");
1738 tracing::event!(name: "event name 5", tracing::Level::INFO, name = "field4");
1739 });
1740 });
1741
1742 let events = tracer.with_data(|data| data.events.clone());
1743
1744 let mut iter = events.iter();
1745
1746 assert_eq!(iter.next().unwrap().name, "event name 1");
1747 assert_eq!(iter.next().unwrap().name, "event name 2");
1748 assert_eq!(iter.next().unwrap().name, "exception"); assert_eq!(iter.next().unwrap().name, "field3"); assert_eq!(iter.next().unwrap().name, "event name 5"); }
1752
1753 #[test]
1754 fn records_no_error_fields_with_context_activation() {
1755 records_no_error_fields_impl(true);
1756 }
1757
1758 #[test]
1759 fn records_no_error_fields_no_context_activation() {
1760 records_no_error_fields_impl(false);
1761 }
1762
1763 fn records_no_error_fields_impl(context_activation: bool) {
1764 let mut tracer = TestTracer::default();
1765 let subscriber = tracing_subscriber::registry().with(
1766 layer()
1767 .with_error_records_to_exceptions(false)
1768 .with_tracer(tracer.clone())
1769 .with_context_activation(context_activation),
1770 );
1771
1772 let err = TestDynError::new("base error")
1773 .with_parent("intermediate error")
1774 .with_parent("user error");
1775
1776 tracing::subscriber::with_default(subscriber, || {
1777 tracing::debug_span!(
1778 "request",
1779 error = &err as &(dyn std::error::Error + 'static)
1780 );
1781 });
1782
1783 let attributes = tracer.attributes();
1784
1785 assert_eq!(attributes["error"].as_str(), "user error");
1786 assert_eq!(
1787 attributes["error.chain"],
1788 Value::Array(
1789 vec![
1790 StringValue::from("intermediate error"),
1791 StringValue::from("base error")
1792 ]
1793 .into()
1794 )
1795 );
1796
1797 assert_eq!(attributes[FIELD_EXCEPTION_MESSAGE].as_str(), "user error");
1798 assert_eq!(
1799 attributes[FIELD_EXCEPTION_STACKTRACE],
1800 Value::Array(
1801 vec![
1802 StringValue::from("intermediate error"),
1803 StringValue::from("base error")
1804 ]
1805 .into()
1806 )
1807 );
1808 }
1809
1810 #[test]
1811 fn includes_span_location_with_context_activation() {
1812 includes_span_location_impl(true);
1813 }
1814
1815 #[test]
1816 fn includes_span_location_no_context_activation() {
1817 includes_span_location_impl(false);
1818 }
1819
1820 fn includes_span_location_impl(context_activation: bool) {
1821 let mut tracer = TestTracer::default();
1822 let subscriber = tracing_subscriber::registry().with(
1823 layer()
1824 .with_tracer(tracer.clone())
1825 .with_location(true)
1826 .with_context_activation(context_activation),
1827 );
1828
1829 tracing::subscriber::with_default(subscriber, || {
1830 tracing::debug_span!("request");
1831 });
1832
1833 let attributes = tracer.attributes();
1834
1835 assert!(attributes.contains_key("code.file.path"));
1836 assert!(attributes.contains_key("code.module.name"));
1837 assert!(attributes.contains_key("code.line.number"));
1838 }
1839
1840 #[test]
1841 fn excludes_span_location_with_context_activation() {
1842 excludes_span_location_impl(true);
1843 }
1844
1845 #[test]
1846 fn excludes_span_location_no_context_activation() {
1847 excludes_span_location_impl(false);
1848 }
1849
1850 fn excludes_span_location_impl(context_activation: bool) {
1851 let mut tracer = TestTracer::default();
1852 let subscriber = tracing_subscriber::registry().with(
1853 layer()
1854 .with_tracer(tracer.clone())
1855 .with_location(false)
1856 .with_context_activation(context_activation),
1857 );
1858
1859 tracing::subscriber::with_default(subscriber, || {
1860 tracing::debug_span!("request");
1861 });
1862
1863 let attributes = tracer.attributes();
1864
1865 assert!(!attributes.contains_key("code.file.path"));
1866 assert!(!attributes.contains_key("code.module.name"));
1867 assert!(!attributes.contains_key("code.line.number"));
1868 }
1869
1870 #[test]
1871 fn includes_thread_with_context_activation() {
1872 includes_thread_impl(true);
1873 }
1874
1875 #[test]
1876 fn includes_thread_no_context_activation() {
1877 includes_thread_impl(false);
1878 }
1879
1880 fn includes_thread_impl(context_activation: bool) {
1881 let thread = thread::current();
1882 let expected_name = thread
1883 .name()
1884 .map(|name| Value::String(name.to_owned().into()));
1885 let expected_id = Value::I64(thread_id_integer(thread.id()) as i64);
1886
1887 let mut tracer = TestTracer::default();
1888 let subscriber = tracing_subscriber::registry().with(
1889 layer()
1890 .with_tracer(tracer.clone())
1891 .with_threads(true)
1892 .with_context_activation(context_activation),
1893 );
1894
1895 tracing::subscriber::with_default(subscriber, || {
1896 tracing::debug_span!("request");
1897 });
1898
1899 let attributes = tracer.attributes();
1900
1901 assert_eq!(attributes.get("thread.name"), expected_name.as_ref());
1902 assert_eq!(attributes.get("thread.id"), Some(&expected_id));
1903 }
1904
1905 #[test]
1906 fn excludes_thread_with_context_activation() {
1907 excludes_thread_impl(true);
1908 }
1909
1910 #[test]
1911 fn excludes_thread_no_context_activation() {
1912 excludes_thread_impl(false);
1913 }
1914
1915 fn excludes_thread_impl(context_activation: bool) {
1916 let mut tracer = TestTracer::default();
1917 let subscriber = tracing_subscriber::registry().with(
1918 layer()
1919 .with_tracer(tracer.clone())
1920 .with_threads(false)
1921 .with_context_activation(context_activation),
1922 );
1923
1924 tracing::subscriber::with_default(subscriber, || {
1925 tracing::debug_span!("request");
1926 });
1927
1928 let attributes = tracer.attributes();
1929
1930 assert!(!attributes.contains_key("thread.name"));
1931 assert!(!attributes.contains_key("thread.id"));
1932 }
1933
1934 #[test]
1935 fn includes_level_with_context_activation() {
1936 includes_level_impl(true);
1937 }
1938
1939 #[test]
1940 fn includes_level_no_context_activation() {
1941 includes_level_impl(false);
1942 }
1943
1944 fn includes_level_impl(context_activation: bool) {
1945 let mut tracer = TestTracer::default();
1946 let subscriber = tracing_subscriber::registry().with(
1947 layer()
1948 .with_tracer(tracer.clone())
1949 .with_level(true)
1950 .with_context_activation(context_activation),
1951 );
1952
1953 tracing::subscriber::with_default(subscriber, || {
1954 tracing::debug_span!("request");
1955 });
1956
1957 let attributes = tracer.attributes();
1958
1959 assert!(attributes.contains_key("level"));
1960 }
1961
1962 #[test]
1963 fn excludes_level_with_context_activation() {
1964 excludes_level_impl(true);
1965 }
1966
1967 #[test]
1968 fn excludes_level_no_context_activation() {
1969 excludes_level_impl(false);
1970 }
1971
1972 fn excludes_level_impl(context_activation: bool) {
1973 let mut tracer = TestTracer::default();
1974 let subscriber = tracing_subscriber::registry().with(
1975 layer()
1976 .with_tracer(tracer.clone())
1977 .with_level(false)
1978 .with_context_activation(context_activation),
1979 );
1980
1981 tracing::subscriber::with_default(subscriber, || {
1982 tracing::debug_span!("request");
1983 });
1984
1985 let attributes = tracer.attributes();
1986
1987 assert!(!attributes.contains_key("level"));
1988 }
1989
1990 #[test]
1991 fn includes_target() {
1992 let mut tracer = TestTracer::default();
1993 let subscriber = tracing_subscriber::registry()
1994 .with(layer().with_tracer(tracer.clone()).with_target(true));
1995
1996 tracing::subscriber::with_default(subscriber, || {
1997 tracing::debug_span!("request");
1998 });
1999
2000 let attributes = tracer.attributes();
2001 let keys = attributes.keys().map(|k| k.as_str()).collect::<Vec<&str>>();
2002 assert!(keys.contains(&"target"));
2003 }
2004
2005 #[test]
2006 fn excludes_target() {
2007 let mut tracer = TestTracer::default();
2008 let subscriber = tracing_subscriber::registry()
2009 .with(layer().with_tracer(tracer.clone()).with_target(false));
2010
2011 tracing::subscriber::with_default(subscriber, || {
2012 tracing::debug_span!("request");
2013 });
2014
2015 let attributes = tracer.attributes();
2016 let keys = attributes.keys().map(|k| k.as_str()).collect::<Vec<&str>>();
2017 assert!(!keys.contains(&"target"));
2018 }
2019
2020 #[test]
2021 fn propagates_error_fields_from_event_to_span_with_context_activation() {
2022 propagates_error_fields_from_event_to_span_impl(true);
2023 }
2024
2025 #[test]
2026 fn propagates_error_fields_from_event_to_span_no_context_activation() {
2027 propagates_error_fields_from_event_to_span_impl(false);
2028 }
2029
2030 fn propagates_error_fields_from_event_to_span_impl(context_activation: bool) {
2031 let mut tracer = TestTracer::default();
2032 let subscriber = tracing_subscriber::registry().with(
2033 layer()
2034 .with_tracer(tracer.clone())
2035 .with_context_activation(context_activation),
2036 );
2037
2038 let err = TestDynError::new("base error")
2039 .with_parent("intermediate error")
2040 .with_parent("user error");
2041
2042 tracing::subscriber::with_default(subscriber, || {
2043 let _guard = tracing::debug_span!("request",).entered();
2044
2045 tracing::error!(
2046 error = &err as &(dyn std::error::Error + 'static),
2047 "request error!"
2048 )
2049 });
2050
2051 let attributes = tracer.attributes();
2052
2053 assert_eq!(attributes[FIELD_EXCEPTION_MESSAGE].as_str(), "user error");
2054 assert_eq!(
2055 attributes[FIELD_EXCEPTION_STACKTRACE],
2056 Value::Array(
2057 vec![
2058 StringValue::from("intermediate error"),
2059 StringValue::from("base error")
2060 ]
2061 .into()
2062 )
2063 );
2064 }
2065
2066 #[test]
2067 fn propagates_no_error_fields_from_event_to_span_with_context_activation() {
2068 propagates_no_error_fields_from_event_to_span_impl(true);
2069 }
2070
2071 #[test]
2072 fn propagates_no_error_fields_from_event_to_span_no_context_activation() {
2073 propagates_no_error_fields_from_event_to_span_impl(false);
2074 }
2075
2076 fn propagates_no_error_fields_from_event_to_span_impl(context_activation: bool) {
2077 let mut tracer = TestTracer::default();
2078 let subscriber = tracing_subscriber::registry().with(
2079 layer()
2080 .with_error_fields_to_exceptions(false)
2081 .with_tracer(tracer.clone())
2082 .with_context_activation(context_activation),
2083 );
2084
2085 let err = TestDynError::new("base error")
2086 .with_parent("intermediate error")
2087 .with_parent("user error");
2088
2089 tracing::subscriber::with_default(subscriber, || {
2090 let _guard = tracing::debug_span!("request",).entered();
2091
2092 tracing::error!(
2093 error = &err as &(dyn std::error::Error + 'static),
2094 "request error!"
2095 )
2096 });
2097
2098 let attributes = tracer.attributes();
2099
2100 assert_eq!(attributes[FIELD_EXCEPTION_MESSAGE].as_str(), "user error");
2101 assert_eq!(
2102 attributes[FIELD_EXCEPTION_STACKTRACE],
2103 Value::Array(
2104 vec![
2105 StringValue::from("intermediate error"),
2106 StringValue::from("base error")
2107 ]
2108 .into()
2109 )
2110 );
2111 }
2112
2113 #[test]
2114 fn tracing_error_compatibility_with_context_activation() {
2115 tracing_error_compatibility_impl(true);
2116 }
2117
2118 #[test]
2119 fn tracing_error_compatibility_no_context_activation() {
2120 tracing_error_compatibility_impl(false);
2121 }
2122
2123 fn tracing_error_compatibility_impl(context_activation: bool) {
2124 let tracer = TestTracer::default();
2125 let subscriber = tracing_subscriber::registry()
2126 .with(
2127 layer()
2128 .with_error_fields_to_exceptions(false)
2129 .with_tracer(tracer.clone())
2130 .with_context_activation(context_activation),
2131 )
2132 .with(tracing_error::ErrorLayer::default());
2133
2134 tracing::subscriber::with_default(subscriber, || {
2135 let span = tracing::info_span!("Blows up!", exception = tracing::field::Empty);
2136 let _entered = span.enter();
2137 let context = tracing_error::SpanTrace::capture();
2138
2139 span.record("exception", tracing::field::debug(&context));
2141 tracing::info!(exception = &tracing::field::debug(&context), "hello");
2143 });
2144
2145 }
2147
2148 #[derive(Debug, PartialEq)]
2149 struct ValueA(&'static str);
2150 #[derive(Debug, PartialEq)]
2151 struct ValueB(&'static str);
2152
2153 #[test]
2154 fn otel_context_propagation() {
2155 use opentelemetry::trace::Tracer;
2156 use tracing::span;
2157
2158 let mut tracer = TestTracer::default();
2159 let subscriber = tracing_subscriber::registry().with(layer().with_tracer(tracer.clone()));
2160
2161 tracing::subscriber::with_default(subscriber, || {
2162 let _outer_guard =
2164 OtelContext::attach(OtelContext::default().with_value(ValueA("outer")));
2165 assert_eq!(OtelContext::current().get(), Some(&ValueA("outer")));
2166 let root = span!(tracing::Level::TRACE, "tokio-tracing-span-parent");
2167 drop(_outer_guard);
2169 assert!(OtelContext::current().get::<ValueA>().is_none());
2170 let _enter_root = root.enter();
2172 assert_eq!(OtelContext::current().get(), Some(&ValueA("outer")));
2173 let _inner_guard =
2175 OtelContext::attach(OtelContext::current_with_value(ValueB("inner")));
2176 assert_eq!(OtelContext::current().get(), Some(&ValueA("outer")));
2177 assert_eq!(OtelContext::current().get(), Some(&ValueB("inner")));
2178 let child = span!(tracing::Level::TRACE, "tokio-tracing-span-child");
2179 drop(_inner_guard);
2181 assert_eq!(OtelContext::current().get(), Some(&ValueA("outer")));
2182 assert!(OtelContext::current().get::<ValueB>().is_none());
2183 let _enter_child = child.enter();
2185 assert_eq!(OtelContext::current().get(), Some(&ValueA("outer")));
2186 assert_eq!(OtelContext::current().get(), Some(&ValueB("inner")));
2187 let span = tracer
2190 .tracer
2191 .span_builder("otel-tracing-span")
2192 .start(&tracer);
2193 let _otel_guard = OtelContext::attach(OtelContext::current_with_span(span));
2194 let child2 = span!(tracing::Level::TRACE, "tokio-tracing-span-child2");
2195 drop(_otel_guard);
2196 drop(_enter_child);
2198 assert_eq!(OtelContext::current().get(), Some(&ValueA("outer")));
2199 assert!(OtelContext::current().get::<ValueB>().is_none());
2200 drop(_enter_root);
2202 assert!(OtelContext::current().get::<ValueA>().is_none());
2203 assert!(OtelContext::current().get::<ValueB>().is_none());
2204 let _ = child2.enter();
2205 });
2206
2207 let spans = tracer.spans();
2209 let parent = spans
2210 .iter()
2211 .find(|span| span.name == "tokio-tracing-span-parent")
2212 .unwrap();
2213 let child = spans
2214 .iter()
2215 .find(|span| span.name == "tokio-tracing-span-child")
2216 .unwrap();
2217 let child2 = spans
2218 .iter()
2219 .find(|span| span.name == "tokio-tracing-span-child2")
2220 .unwrap();
2221 let otel = spans
2222 .iter()
2223 .find(|span| span.name == "otel-tracing-span")
2224 .unwrap();
2225 assert_eq!(parent.parent_span_id, otel::SpanId::INVALID);
2227 assert_eq!(child.parent_span_id, parent.span_context.span_id());
2229 assert_eq!(otel.parent_span_id, child.span_context.span_id());
2231 assert_eq!(child2.parent_span_id, otel.span_context.span_id());
2233 }
2234
2235 #[test]
2236 fn parent_context() {
2237 let mut tracer = TestTracer::default();
2238 let subscriber = tracing_subscriber::registry().with(layer().with_tracer(tracer.clone()));
2239
2240 tracing::subscriber::with_default(subscriber, || {
2241 let root = trace_span!("root");
2242
2243 let child1 = trace_span!("child-1");
2244 let root_context = root.context(); let _ = child1.set_parent(root_context); let _enter_root = root.enter();
2248 drop(_enter_root);
2249
2250 let child2 = trace_span!("child-2");
2251 let _ = child2.set_parent(root.context());
2252 });
2253
2254 let spans = tracer.spans();
2256 let parent = spans.iter().find(|span| span.name == "root").unwrap();
2257 let child1 = spans.iter().find(|span| span.name == "child-1").unwrap();
2258 let child2 = spans.iter().find(|span| span.name == "child-2").unwrap();
2259 assert_eq!(parent.parent_span_id, otel::SpanId::INVALID);
2260 assert_eq!(child1.parent_span_id, parent.span_context.span_id());
2261 assert_eq!(child2.parent_span_id, parent.span_context.span_id());
2262 }
2263
2264 #[test]
2265 fn record_after_with_context_activation() {
2266 record_after_impl(true);
2267 }
2268
2269 #[test]
2270 fn record_after_no_context_activation() {
2271 record_after_impl(false);
2272 }
2273
2274 fn record_after_impl(context_activation: bool) {
2275 let mut tracer = TestTracer::default();
2276 let subscriber = tracing_subscriber::registry().with(
2277 layer()
2278 .with_tracer(tracer.clone())
2279 .with_context_activation(context_activation),
2280 );
2281
2282 tracing::subscriber::with_default(subscriber, || {
2283 let root = trace_span!("root", before = "before", after = "before");
2284
2285 root.record("before", "after");
2287
2288 let _enter_root = root.enter();
2290 drop(_enter_root);
2291
2292 root.record("after", "after");
2294 });
2295
2296 let spans = tracer.spans();
2299 let parent = spans.iter().find(|span| span.name == "root").unwrap();
2300 assert_eq!(parent.parent_span_id, otel::SpanId::INVALID);
2301 assert!(parent
2302 .attributes
2303 .iter()
2304 .filter(|kv| kv.key.as_str() == "before")
2305 .any(|kv| kv.value.as_str() == "after"));
2306
2307 assert!(parent
2308 .attributes
2309 .iter()
2310 .filter(|kv| kv.key.as_str() == "after")
2311 .any(|kv| kv.value.as_str() == "after"));
2312 }
2313
2314 #[test]
2315 fn parent_context_2() {
2316 let mut tracer = TestTracer::default();
2317 let subscriber = tracing_subscriber::registry().with(layer().with_tracer(tracer.clone()));
2318
2319 tracing::subscriber::with_default(subscriber, || {
2320 let root = trace_span!("root");
2321 _ = root.enter();
2322
2323 let child1 = trace_span!("child-1");
2324 let _ = child1.set_parent(root.context());
2325
2326 trace_span!(parent: &child1, "child-2");
2327 let _ = child1.set_parent(root.context()); trace_span!(parent: &child1, "child-3");
2330 });
2331
2332 let spans = tracer.spans();
2334 let root = spans.iter().find(|span| span.name == "root").unwrap();
2335 let child1 = spans.iter().find(|span| span.name == "child-1").unwrap();
2336 let child2 = spans.iter().find(|span| span.name == "child-2").unwrap();
2337 let child3 = spans.iter().find(|span| span.name == "child-3").unwrap();
2338 assert_eq!(root.parent_span_id, otel::SpanId::INVALID);
2339 assert_eq!(child1.parent_span_id, root.span_context.span_id());
2340 assert_eq!(child2.parent_span_id, child1.span_context.span_id());
2341
2342 assert_eq!(child3.parent_span_id, child1.span_context.span_id());
2344 }
2345
2346 #[test]
2347 fn follows_from_adds_link_with_context_activation() {
2348 follows_from_adds_link_impl(true);
2349 }
2350
2351 #[test]
2352 fn follows_from_adds_link_no_context_activation() {
2353 follows_from_adds_link_impl(false);
2354 }
2355
2356 fn follows_from_adds_link_impl(context_activation: bool) {
2357 use crate::OpenTelemetrySpanExt;
2358 let mut tracer = TestTracer::default();
2359 let subscriber = tracing_subscriber::registry().with(
2360 layer()
2361 .with_tracer(tracer.clone())
2362 .with_context_activation(context_activation),
2363 );
2364
2365 let span1_id = tracing::subscriber::with_default(subscriber, || {
2366 let span2 = tracing::debug_span!("span2");
2367 let span1 = tracing::debug_span!("span1");
2368
2369 let _ = span2.context();
2371
2372 span2.follows_from(&span1);
2374
2375 let _guard = span2.enter();
2377
2378 span1.context().span().span_context().span_id()
2380 });
2381
2382 let spans = tracer.spans();
2383 assert_eq!(spans.len(), 2, "Expected two spans to be exported");
2385 assert!(spans.iter().any(|span| span.name == "span1"));
2386 let span2 = spans
2387 .iter()
2388 .find(|span| span.name == "span2")
2389 .expect("Expected span2 to be exported");
2390
2391 let links = span2
2393 .links
2394 .iter()
2395 .map(|link| link.span_context.span_id())
2396 .collect::<Vec<_>>();
2397
2398 assert_eq!(
2400 links.len(),
2401 1,
2402 "Expected span to have one link from follows_from relationship"
2403 );
2404
2405 assert!(
2406 links.contains(&span1_id),
2407 "Link should point to the correct source span"
2408 );
2409 }
2410
2411 #[test]
2412 fn follows_from_multiple_links_with_context_activation() {
2413 follows_from_multiple_links_impl(true);
2414 }
2415
2416 #[test]
2417 fn follows_from_multiple_links_no_context_activation() {
2418 follows_from_multiple_links_impl(false);
2419 }
2420
2421 fn follows_from_multiple_links_impl(context_activation: bool) {
2422 use crate::OpenTelemetrySpanExt;
2423 let mut tracer = TestTracer::default();
2424 let subscriber = tracing_subscriber::registry().with(
2425 layer()
2426 .with_tracer(tracer.clone())
2427 .with_context_activation(context_activation),
2428 );
2429
2430 let (span1_id, span2_id) = tracing::subscriber::with_default(subscriber, || {
2431 let span3 = tracing::debug_span!("span3");
2432 let span2 = tracing::debug_span!("span2");
2433 let span1 = tracing::debug_span!("span1");
2434
2435 span3.follows_from(&span1);
2437 span3.follows_from(&span2);
2438
2439 let _guard = span3.enter();
2441
2442 (
2444 span1.context().span().span_context().span_id(),
2445 span2.context().span().span_context().span_id(),
2446 )
2447 });
2448
2449 let spans = tracer.spans();
2450 assert_eq!(spans.len(), 3, "Expected three spans to be exported");
2452 assert!(spans.iter().any(|span| span.name == "span1"));
2453 assert!(spans.iter().any(|span| span.name == "span2"));
2454 let span3 = spans
2455 .iter()
2456 .find(|span| span.name == "span3")
2457 .expect("Expected span3 to be exported");
2458
2459 let links = span3
2461 .links
2462 .iter()
2463 .map(|link| link.span_context.span_id())
2464 .collect::<Vec<_>>();
2465
2466 assert_eq!(
2468 links.len(),
2469 2,
2470 "Expected span to have two links from follows_from relationships"
2471 );
2472
2473 assert!(
2475 links[0] == span1_id && links[1] == span2_id,
2476 "Links should point to the correct source spans"
2477 );
2478 }
2479
2480 #[test]
2481 fn context_activation_disabled() {
2482 use tracing::span;
2483
2484 let mut tracer = TestTracer::default();
2485 let subscriber = tracing_subscriber::registry().with(
2486 layer()
2487 .with_tracer(tracer.clone())
2488 .with_context_activation(false),
2489 );
2490
2491 tracing::subscriber::with_default(subscriber, || {
2492 let _outer_guard =
2494 OtelContext::attach(OtelContext::default().with_value(ValueA("outer")));
2495 assert_eq!(OtelContext::current().get(), Some(&ValueA("outer")));
2496
2497 let root = span!(tracing::Level::TRACE, "tokio-tracing-span-parent");
2498
2499 drop(_outer_guard);
2501 assert!(OtelContext::current().get::<ValueA>().is_none());
2502
2503 let _enter_root = root.enter();
2506 assert!(OtelContext::current().get::<ValueA>().is_none());
2507
2508 let _inner_guard =
2510 OtelContext::attach(OtelContext::current_with_value(ValueB("inner")));
2511 assert!(OtelContext::current().get::<ValueA>().is_none());
2512 assert_eq!(OtelContext::current().get(), Some(&ValueB("inner")));
2513
2514 let child = span!(tracing::Level::TRACE, "tokio-tracing-span-child");
2515
2516 drop(_inner_guard);
2518 assert!(OtelContext::current().get::<ValueA>().is_none());
2519 assert!(OtelContext::current().get::<ValueB>().is_none());
2520
2521 let _enter_child = child.enter();
2524 assert!(OtelContext::current().get::<ValueA>().is_none());
2525 assert!(OtelContext::current().get::<ValueB>().is_none());
2526 });
2527
2528 let spans = tracer.spans();
2530 assert_eq!(spans.len(), 2);
2531 assert!(spans
2532 .iter()
2533 .any(|span| span.name == "tokio-tracing-span-parent"));
2534 assert!(spans
2535 .iter()
2536 .any(|span| span.name == "tokio-tracing-span-child"));
2537 }
2538}