1use std::convert::TryFrom;
2
3use crate::{OtelData, PreSampledTracer};
4use once_cell::unsync;
5use opentelemetry::{
6 trace::{self as otel, noop, SpanBuilder, SpanKind, Status, TraceContextExt},
7 Context as OtelContext, Key, KeyValue, StringValue, Value,
8};
9use std::fmt;
10use std::marker;
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 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_MESSAGE_FIELD: &str = "otel.status_message";
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 max_events_per_span: Option<usize>,
43 tracked_inactivity: bool,
44 with_threads: bool,
45 sem_conv_config: SemConvConfig,
46 get_context: WithContext,
47 _registry: marker::PhantomData<S>,
48}
49
50impl<S> Default for OpenTelemetryLayer<S, noop::NoopTracer>
51where
52 S: Subscriber + for<'span> LookupSpan<'span>,
53{
54 fn default() -> Self {
55 OpenTelemetryLayer::new(noop::NoopTracer::new())
56 }
57}
58
59pub fn layer<S>() -> OpenTelemetryLayer<S, noop::NoopTracer>
75where
76 S: Subscriber + for<'span> LookupSpan<'span>,
77{
78 OpenTelemetryLayer::default()
79}
80
81pub(crate) struct WithContext(
87 #[allow(clippy::type_complexity)]
88 fn(&tracing::Dispatch, &span::Id, f: &mut dyn FnMut(&mut OtelData, &dyn PreSampledTracer)),
89);
90
91impl WithContext {
92 pub(crate) fn with_context(
95 &self,
96 dispatch: &tracing::Dispatch,
97 id: &span::Id,
98 mut f: impl FnMut(&mut OtelData, &dyn PreSampledTracer),
99 ) {
100 (self.0)(dispatch, id, &mut f)
101 }
102}
103
104#[derive(Debug)]
108struct DroppedOtelEvents {
109 dropped_count: usize,
111}
112
113impl DroppedOtelEvents {
114 fn new() -> Self {
115 DroppedOtelEvents { dropped_count: 0 }
116 }
117
118 fn add_dropped(&mut self, additional_count: usize) {
119 self.dropped_count = self.dropped_count.saturating_add(additional_count);
120 }
121}
122
123fn str_to_span_kind(s: &str) -> Option<otel::SpanKind> {
124 match s {
125 s if s.eq_ignore_ascii_case("server") => Some(otel::SpanKind::Server),
126 s if s.eq_ignore_ascii_case("client") => Some(otel::SpanKind::Client),
127 s if s.eq_ignore_ascii_case("producer") => Some(otel::SpanKind::Producer),
128 s if s.eq_ignore_ascii_case("consumer") => Some(otel::SpanKind::Consumer),
129 s if s.eq_ignore_ascii_case("internal") => Some(otel::SpanKind::Internal),
130 _ => None,
131 }
132}
133
134fn str_to_status(s: &str) -> otel::Status {
135 match s {
136 s if s.eq_ignore_ascii_case("ok") => otel::Status::Ok,
137 s if s.eq_ignore_ascii_case("error") => otel::Status::error(""),
138 _ => otel::Status::Unset,
139 }
140}
141
142#[derive(Default)]
143struct SpanBuilderUpdates {
144 name: Option<Cow<'static, str>>,
145 span_kind: Option<SpanKind>,
146 status: Option<Status>,
147 attributes: Option<Vec<KeyValue>>,
148}
149
150impl SpanBuilderUpdates {
151 fn update(self, span_builder: &mut SpanBuilder) {
152 let Self {
153 name,
154 span_kind,
155 status,
156 attributes,
157 } = self;
158
159 if let Some(name) = name {
160 span_builder.name = name;
161 }
162 if let Some(span_kind) = span_kind {
163 span_builder.span_kind = Some(span_kind);
164 }
165 if let Some(status) = status {
166 span_builder.status = status;
167 }
168 if let Some(attributes) = attributes {
169 if let Some(builder_attributes) = &mut span_builder.attributes {
170 builder_attributes.extend(attributes);
171 } else {
172 span_builder.attributes = Some(attributes);
173 }
174 }
175 }
176}
177
178struct SpanEventVisitor<'a, 'b> {
179 event_builder: &'a mut otel::Event,
180 span_builder_updates: &'b mut Option<SpanBuilderUpdates>,
181 sem_conv_config: SemConvConfig,
182}
183
184impl<'a, 'b> field::Visit for SpanEventVisitor<'a, 'b> {
185 fn record_bool(&mut self, field: &field::Field, value: bool) {
189 match field.name() {
190 "message" => self.event_builder.name = value.to_string().into(),
191 #[cfg(feature = "tracing-log")]
193 name if name.starts_with("log.") => (),
194 name => {
195 self.event_builder
196 .attributes
197 .push(KeyValue::new(name, value));
198 }
199 }
200 }
201
202 fn record_f64(&mut self, field: &field::Field, value: f64) {
206 match field.name() {
207 "message" => self.event_builder.name = value.to_string().into(),
208 #[cfg(feature = "tracing-log")]
210 name if name.starts_with("log.") => (),
211 name => {
212 self.event_builder
213 .attributes
214 .push(KeyValue::new(name, value));
215 }
216 }
217 }
218
219 fn record_i64(&mut self, field: &field::Field, value: i64) {
223 match field.name() {
224 "message" => self.event_builder.name = value.to_string().into(),
225 #[cfg(feature = "tracing-log")]
227 name if name.starts_with("log.") => (),
228 name => {
229 self.event_builder
230 .attributes
231 .push(KeyValue::new(name, value));
232 }
233 }
234 }
235
236 fn record_str(&mut self, field: &field::Field, value: &str) {
240 match field.name() {
241 "message" => self.event_builder.name = value.to_string().into(),
242 "error" if self.event_builder.name.is_empty() => {
246 if self.sem_conv_config.error_events_to_status {
247 self.span_builder_updates
248 .get_or_insert_with(SpanBuilderUpdates::default)
249 .status
250 .replace(otel::Status::error(format!("{:?}", value)));
251 }
252 if self.sem_conv_config.error_events_to_exceptions {
253 self.event_builder.name = EVENT_EXCEPTION_NAME.into();
254 self.event_builder.attributes.push(KeyValue::new(
255 FIELD_EXCEPTION_MESSAGE,
256 format!("{:?}", value),
257 ));
258 } else {
259 self.event_builder
260 .attributes
261 .push(KeyValue::new("error", format!("{:?}", value)));
262 }
263 }
264 #[cfg(feature = "tracing-log")]
266 name if name.starts_with("log.") => (),
267 name => {
268 self.event_builder
269 .attributes
270 .push(KeyValue::new(name, value.to_string()));
271 }
272 }
273 }
274
275 fn record_debug(&mut self, field: &field::Field, value: &dyn fmt::Debug) {
280 match field.name() {
281 "message" => self.event_builder.name = format!("{:?}", value).into(),
282 "error" if self.event_builder.name.is_empty() => {
286 if self.sem_conv_config.error_events_to_status {
287 self.span_builder_updates
288 .get_or_insert_with(SpanBuilderUpdates::default)
289 .status
290 .replace(otel::Status::error(format!("{:?}", value)));
291 }
292 if self.sem_conv_config.error_events_to_exceptions {
293 self.event_builder.name = EVENT_EXCEPTION_NAME.into();
294 self.event_builder.attributes.push(KeyValue::new(
295 FIELD_EXCEPTION_MESSAGE,
296 format!("{:?}", value),
297 ));
298 } else {
299 self.event_builder
300 .attributes
301 .push(KeyValue::new("error", format!("{:?}", value)));
302 }
303 }
304 #[cfg(feature = "tracing-log")]
306 name if name.starts_with("log.") => (),
307 name => {
308 self.event_builder
309 .attributes
310 .push(KeyValue::new(name, format!("{:?}", value)));
311 }
312 }
313 }
314
315 fn record_error(
320 &mut self,
321 field: &tracing_core::Field,
322 value: &(dyn std::error::Error + 'static),
323 ) {
324 let mut chain: Vec<StringValue> = Vec::new();
325 let mut next_err = value.source();
326
327 while let Some(err) = next_err {
328 chain.push(err.to_string().into());
329 next_err = err.source();
330 }
331
332 let error_msg = value.to_string();
333
334 if self.sem_conv_config.error_fields_to_exceptions {
335 self.event_builder
336 .attributes
337 .push(Key::new(FIELD_EXCEPTION_MESSAGE).string(error_msg.clone()));
338
339 self.event_builder
346 .attributes
347 .push(Key::new(FIELD_EXCEPTION_STACKTRACE).array(chain.clone()));
348 }
349
350 if self.sem_conv_config.error_records_to_exceptions {
351 let attributes = self
352 .span_builder_updates
353 .get_or_insert_with(SpanBuilderUpdates::default)
354 .attributes
355 .get_or_insert_with(Vec::new);
356
357 attributes.push(KeyValue::new(
358 FIELD_EXCEPTION_MESSAGE,
359 Value::String(error_msg.clone().into()),
360 ));
361
362 attributes.push(KeyValue::new(
369 FIELD_EXCEPTION_STACKTRACE,
370 Value::Array(chain.clone().into()),
371 ));
372 }
373
374 self.event_builder
375 .attributes
376 .push(Key::new(field.name()).string(error_msg));
377 self.event_builder
378 .attributes
379 .push(Key::new(format!("{}.chain", field.name())).array(chain));
380 }
381}
382
383#[derive(Clone, Copy)]
385struct SemConvConfig {
386 error_fields_to_exceptions: bool,
391
392 error_records_to_exceptions: bool,
397
398 error_events_to_status: bool,
407
408 error_events_to_exceptions: bool,
416}
417
418struct SpanAttributeVisitor<'a> {
419 span_builder_updates: &'a mut SpanBuilderUpdates,
420 sem_conv_config: SemConvConfig,
421}
422
423impl<'a> SpanAttributeVisitor<'a> {
424 fn record(&mut self, attribute: KeyValue) {
425 self.span_builder_updates
426 .attributes
427 .get_or_insert_with(Vec::new)
428 .push(KeyValue::new(attribute.key, attribute.value));
429 }
430}
431
432impl<'a> field::Visit for SpanAttributeVisitor<'a> {
433 fn record_bool(&mut self, field: &field::Field, value: bool) {
437 self.record(KeyValue::new(field.name(), value));
438 }
439
440 fn record_f64(&mut self, field: &field::Field, value: f64) {
444 self.record(KeyValue::new(field.name(), value));
445 }
446
447 fn record_i64(&mut self, field: &field::Field, value: i64) {
451 self.record(KeyValue::new(field.name(), value));
452 }
453
454 fn record_str(&mut self, field: &field::Field, value: &str) {
458 match field.name() {
459 SPAN_NAME_FIELD => self.span_builder_updates.name = Some(value.to_string().into()),
460 SPAN_KIND_FIELD => self.span_builder_updates.span_kind = str_to_span_kind(value),
461 SPAN_STATUS_CODE_FIELD => self.span_builder_updates.status = Some(str_to_status(value)),
462 SPAN_STATUS_MESSAGE_FIELD => {
463 self.span_builder_updates.status = Some(otel::Status::error(value.to_string()))
464 }
465 _ => self.record(KeyValue::new(field.name(), value.to_string())),
466 }
467 }
468
469 fn record_debug(&mut self, field: &field::Field, value: &dyn fmt::Debug) {
474 match field.name() {
475 SPAN_NAME_FIELD => self.span_builder_updates.name = Some(format!("{:?}", value).into()),
476 SPAN_KIND_FIELD => {
477 self.span_builder_updates.span_kind = str_to_span_kind(&format!("{:?}", value))
478 }
479 SPAN_STATUS_CODE_FIELD => {
480 self.span_builder_updates.status = Some(str_to_status(&format!("{:?}", value)))
481 }
482 SPAN_STATUS_MESSAGE_FIELD => {
483 self.span_builder_updates.status = Some(otel::Status::error(format!("{:?}", value)))
484 }
485 _ => self.record(Key::new(field.name()).string(format!("{:?}", value))),
486 }
487 }
488
489 fn record_error(
494 &mut self,
495 field: &tracing_core::Field,
496 value: &(dyn std::error::Error + 'static),
497 ) {
498 let mut chain: Vec<StringValue> = Vec::new();
499 let mut next_err = value.source();
500
501 while let Some(err) = next_err {
502 chain.push(err.to_string().into());
503 next_err = err.source();
504 }
505
506 let error_msg = value.to_string();
507
508 if self.sem_conv_config.error_fields_to_exceptions {
509 self.record(Key::new(FIELD_EXCEPTION_MESSAGE).string(error_msg.clone()));
510
511 self.record(Key::new(FIELD_EXCEPTION_STACKTRACE).array(chain.clone()));
518 }
519
520 self.record(Key::new(field.name()).string(error_msg));
521 self.record(Key::new(format!("{}.chain", field.name())).array(chain));
522 }
523}
524
525impl<S, T> OpenTelemetryLayer<S, T>
526where
527 S: Subscriber + for<'span> LookupSpan<'span>,
528 T: otel::Tracer + PreSampledTracer + 'static,
529{
530 pub fn new(tracer: T) -> Self {
563 OpenTelemetryLayer {
564 tracer,
565 location: true,
566 max_events_per_span: None,
567 tracked_inactivity: true,
568 with_threads: true,
569 sem_conv_config: SemConvConfig {
570 error_fields_to_exceptions: true,
571 error_records_to_exceptions: true,
572 error_events_to_exceptions: true,
573 error_events_to_status: true,
574 },
575
576 get_context: WithContext(Self::get_context),
577 _registry: marker::PhantomData,
578 }
579 }
580
581 pub fn with_tracer<Tracer>(self, tracer: Tracer) -> OpenTelemetryLayer<S, Tracer>
613 where
614 Tracer: otel::Tracer + PreSampledTracer + 'static,
615 {
616 OpenTelemetryLayer {
617 tracer,
618 location: self.location,
619 max_events_per_span: self.max_events_per_span,
620 tracked_inactivity: self.tracked_inactivity,
621 with_threads: self.with_threads,
622 sem_conv_config: self.sem_conv_config,
623 get_context: WithContext(OpenTelemetryLayer::<S, Tracer>::get_context),
624 _registry: self._registry,
625 }
626 }
627
628 pub fn with_error_fields_to_exceptions(self, error_fields_to_exceptions: bool) -> Self {
644 Self {
645 sem_conv_config: SemConvConfig {
646 error_fields_to_exceptions,
647 ..self.sem_conv_config
648 },
649 ..self
650 }
651 }
652
653 pub fn with_error_events_to_status(self, error_events_to_status: bool) -> Self {
659 Self {
660 sem_conv_config: SemConvConfig {
661 error_events_to_status,
662 ..self.sem_conv_config
663 },
664 ..self
665 }
666 }
667
668 pub fn with_error_events_to_exceptions(self, error_events_to_exceptions: bool) -> Self {
679 Self {
680 sem_conv_config: SemConvConfig {
681 error_events_to_exceptions,
682 ..self.sem_conv_config
683 },
684 ..self
685 }
686 }
687
688 pub fn with_error_records_to_exceptions(self, error_records_to_exceptions: bool) -> Self {
704 Self {
705 sem_conv_config: SemConvConfig {
706 error_records_to_exceptions,
707 ..self.sem_conv_config
708 },
709 ..self
710 }
711 }
712
713 pub fn with_location(self, location: bool) -> Self {
723 Self { location, ..self }
724 }
725
726 pub fn with_tracked_inactivity(self, tracked_inactivity: bool) -> Self {
730 Self {
731 tracked_inactivity,
732 ..self
733 }
734 }
735
736 pub fn with_threads(self, threads: bool) -> Self {
744 Self {
745 with_threads: threads,
746 ..self
747 }
748 }
749
750 pub fn max_events_per_span(self, num: usize) -> Self {
761 Self {
762 max_events_per_span: Some(num),
763 ..self
764 }
765 }
766
767 fn parent_context(&self, attrs: &Attributes<'_>, ctx: &Context<'_, S>) -> OtelContext {
775 if let Some(parent) = attrs.parent() {
776 if let Some(span) = ctx.span(parent) {
789 let mut extensions = span.extensions_mut();
790 return extensions
791 .get_mut::<OtelData>()
792 .map(|builder| self.tracer.sampled_context(builder))
793 .unwrap_or_default();
794 }
795 }
796
797 if attrs.is_contextual() {
799 ctx.lookup_current()
800 .and_then(|span| {
801 let mut extensions = span.extensions_mut();
802 extensions
803 .get_mut::<OtelData>()
804 .map(|builder| self.tracer.sampled_context(builder))
805 })
806 .unwrap_or_else(OtelContext::current)
807 } else {
809 OtelContext::new()
810 }
811 }
812
813 fn get_context(
814 dispatch: &tracing::Dispatch,
815 id: &span::Id,
816 f: &mut dyn FnMut(&mut OtelData, &dyn PreSampledTracer),
817 ) {
818 let subscriber = dispatch
819 .downcast_ref::<S>()
820 .expect("subscriber should downcast to expected type; this is a bug!");
821 let span = subscriber
822 .span(id)
823 .expect("registry should have a span for the current ID");
824 let layer = dispatch
825 .downcast_ref::<OpenTelemetryLayer<S, T>>()
826 .expect("layer should downcast to expected type; this is a bug!");
827
828 let mut extensions = span.extensions_mut();
829 if let Some(builder) = extensions.get_mut::<OtelData>() {
830 f(builder, &layer.tracer);
831 }
832 }
833
834 fn extra_span_attrs(&self) -> usize {
835 let mut extra_attrs = 0;
836 if self.location {
837 extra_attrs += 3;
838 }
839 if self.with_threads {
840 extra_attrs += 2;
841 }
842 extra_attrs
843 }
844}
845
846thread_local! {
847 static THREAD_ID: unsync::Lazy<u64> = unsync::Lazy::new(|| {
848 thread_id_integer(thread::current().id())
856 });
857}
858
859impl<S, T> Layer<S> for OpenTelemetryLayer<S, T>
860where
861 S: Subscriber + for<'span> LookupSpan<'span>,
862 T: otel::Tracer + PreSampledTracer + 'static,
863{
864 fn on_new_span(&self, attrs: &Attributes<'_>, id: &span::Id, ctx: Context<'_, S>) {
869 let span = ctx.span(id).expect("Span not found, this is a bug");
870 let mut extensions = span.extensions_mut();
871
872 if self.tracked_inactivity && extensions.get_mut::<Timings>().is_none() {
873 extensions.insert(Timings::new());
874 }
875
876 let parent_cx = self.parent_context(attrs, &ctx);
877 let mut builder = self
878 .tracer
879 .span_builder(attrs.metadata().name())
880 .with_start_time(crate::time::now())
881 .with_span_id(self.tracer.new_span_id());
883
884 if !parent_cx.has_active_span() {
886 builder.trace_id = Some(self.tracer.new_trace_id());
887 }
888
889 let builder_attrs = builder.attributes.get_or_insert(Vec::with_capacity(
890 attrs.fields().len() + self.extra_span_attrs(),
891 ));
892
893 if self.location {
894 let meta = attrs.metadata();
895
896 if let Some(filename) = meta.file() {
897 builder_attrs.push(KeyValue::new("code.filepath", filename));
898 }
899
900 if let Some(module) = meta.module_path() {
901 builder_attrs.push(KeyValue::new("code.namespace", module));
902 }
903
904 if let Some(line) = meta.line() {
905 builder_attrs.push(KeyValue::new("code.lineno", line as i64));
906 }
907 }
908
909 if self.with_threads {
910 THREAD_ID.with(|id| builder_attrs.push(KeyValue::new("thread.id", **id as i64)));
911 if let Some(name) = std::thread::current().name() {
912 builder_attrs.push(KeyValue::new("thread.name", name.to_string()));
917 }
918 }
919
920 let mut updates = SpanBuilderUpdates::default();
921 attrs.record(&mut SpanAttributeVisitor {
922 span_builder_updates: &mut updates,
923 sem_conv_config: self.sem_conv_config,
924 });
925
926 updates.update(&mut builder);
927 extensions.insert(OtelData { builder, parent_cx });
928 }
929
930 fn on_enter(&self, id: &span::Id, ctx: Context<'_, S>) {
931 if !self.tracked_inactivity {
932 return;
933 }
934
935 let span = ctx.span(id).expect("Span not found, this is a bug");
936 let mut extensions = span.extensions_mut();
937
938 if let Some(timings) = extensions.get_mut::<Timings>() {
939 let now = Instant::now();
940 timings.idle += (now - timings.last).as_nanos() as i64;
941 timings.last = now;
942 }
943 }
944
945 fn on_exit(&self, id: &span::Id, ctx: Context<'_, S>) {
946 let span = ctx.span(id).expect("Span not found, this is a bug");
947 let mut extensions = span.extensions_mut();
948
949 if let Some(otel_data) = extensions.get_mut::<OtelData>() {
950 otel_data.builder.end_time = Some(crate::time::now());
951 }
952
953 if !self.tracked_inactivity {
954 return;
955 }
956
957 if let Some(timings) = extensions.get_mut::<Timings>() {
958 let now = Instant::now();
959 timings.busy += (now - timings.last).as_nanos() as i64;
960 timings.last = now;
961 }
962 }
963
964 fn on_record(&self, id: &Id, values: &Record<'_>, ctx: Context<'_, S>) {
968 let span = ctx.span(id).expect("Span not found, this is a bug");
969 let mut updates = SpanBuilderUpdates::default();
970 values.record(&mut SpanAttributeVisitor {
971 span_builder_updates: &mut updates,
972 sem_conv_config: self.sem_conv_config,
973 });
974 let mut extensions = span.extensions_mut();
975 if let Some(data) = extensions.get_mut::<OtelData>() {
976 updates.update(&mut data.builder);
977 }
978 }
979
980 fn on_follows_from(&self, id: &Id, follows: &Id, ctx: Context<S>) {
981 let span = ctx.span(id).expect("Span not found, this is a bug");
982 let mut extensions = span.extensions_mut();
983 let data = extensions
984 .get_mut::<OtelData>()
985 .expect("Missing otel data span extensions");
986
987 if let Some(follows_span) = ctx.span(follows) {
991 let mut follows_extensions = follows_span.extensions_mut();
992 let follows_data = follows_extensions
993 .get_mut::<OtelData>()
994 .expect("Missing otel data span extensions");
995
996 let follows_context = self
997 .tracer
998 .sampled_context(follows_data)
999 .span()
1000 .span_context()
1001 .clone();
1002 let follows_link = otel::Link::with_context(follows_context);
1003 if let Some(ref mut links) = data.builder.links {
1004 links.push(follows_link);
1005 } else {
1006 data.builder.links = Some(vec![follows_link]);
1007 }
1008 }
1009 }
1010
1011 fn on_event(&self, event: &Event<'_>, ctx: Context<'_, S>) {
1020 if let Some(span) = event.parent().and_then(|id| ctx.span(id)).or_else(|| {
1022 event
1023 .is_contextual()
1024 .then(|| ctx.lookup_current())
1025 .flatten()
1026 }) {
1027 #[cfg(feature = "tracing-log")]
1030 let normalized_meta = event.normalized_metadata();
1031 #[cfg(feature = "tracing-log")]
1032 let meta = normalized_meta.as_ref().unwrap_or_else(|| event.metadata());
1033 #[cfg(not(feature = "tracing-log"))]
1034 let meta = event.metadata();
1035
1036 let target = Key::new("target");
1037
1038 #[cfg(feature = "tracing-log")]
1039 let target = if normalized_meta.is_some() {
1040 target.string(meta.target().to_owned())
1041 } else {
1042 target.string(event.metadata().target())
1043 };
1044
1045 #[cfg(not(feature = "tracing-log"))]
1046 let target = target.string(meta.target());
1047
1048 let mut otel_event = otel::Event::new(
1049 String::new(),
1050 crate::time::now(),
1051 vec![Key::new("level").string(meta.level().as_str()), target],
1052 0,
1053 );
1054
1055 let mut builder_updates = None;
1056 event.record(&mut SpanEventVisitor {
1057 event_builder: &mut otel_event,
1058 span_builder_updates: &mut builder_updates,
1059 sem_conv_config: self.sem_conv_config,
1060 });
1061
1062 let mut extensions = span.extensions_mut();
1063 let otel_data = extensions.get_mut::<OtelData>();
1064
1065 let dropped_events = if let Some(otel_data) = otel_data {
1066 let builder = &mut otel_data.builder;
1067
1068 if builder.status == otel::Status::Unset
1069 && *meta.level() == tracing_core::Level::ERROR
1070 {
1071 builder.status = otel::Status::error("")
1072 }
1073
1074 if let Some(builder_updates) = builder_updates {
1075 builder_updates.update(builder);
1076 }
1077
1078 if self.location {
1079 #[cfg(not(feature = "tracing-log"))]
1080 let normalized_meta: Option<tracing_core::Metadata<'_>> = None;
1081 let (file, module) = match &normalized_meta {
1082 Some(meta) => (
1083 meta.file().map(|s| Value::from(s.to_owned())),
1084 meta.module_path().map(|s| Value::from(s.to_owned())),
1085 ),
1086 None => (
1087 event.metadata().file().map(Value::from),
1088 event.metadata().module_path().map(Value::from),
1089 ),
1090 };
1091
1092 if let Some(file) = file {
1093 otel_event
1094 .attributes
1095 .push(KeyValue::new("code.filepath", file));
1096 }
1097 if let Some(module) = module {
1098 otel_event
1099 .attributes
1100 .push(KeyValue::new("code.namespace", module));
1101 }
1102 if let Some(line) = meta.line() {
1103 otel_event
1104 .attributes
1105 .push(KeyValue::new("code.lineno", line as i64));
1106 }
1107 }
1108
1109 if let Some(ref mut events) = builder.events {
1110 events.push(otel_event);
1111 } else {
1112 builder.events = Some(vec![otel_event]);
1113 }
1114
1115 let dropped_events = match (self.max_events_per_span, &mut builder.events) {
1117 (Some(max_num_events), Some(ref mut events))
1118 if events.len() > max_num_events =>
1119 {
1120 let num_events_to_drain = (events.len() + 2 - 1) / 2;
1126 events.drain(0..num_events_to_drain);
1127
1128 Some(num_events_to_drain)
1129 }
1130 _ => None,
1131 };
1132
1133 dropped_events
1134 } else {
1135 None
1136 };
1137
1138 match (dropped_events, extensions.get_mut::<DroppedOtelEvents>()) {
1140 (Some(additional_dropped), Some(current_dropped)) => {
1141 current_dropped.add_dropped(additional_dropped);
1142 }
1143 (Some(additional_dropped), None) => {
1144 let mut count = DroppedOtelEvents::new();
1145 count.add_dropped(additional_dropped);
1146 extensions.insert(count);
1147 }
1148 _ => (),
1149 }
1150 };
1151 }
1152
1153 fn on_close(&self, id: span::Id, ctx: Context<'_, S>) {
1157 let span = ctx.span(&id).expect("Span not found, this is a bug");
1158 let mut extensions = span.extensions_mut();
1159
1160 if let Some(OtelData {
1161 mut builder,
1162 parent_cx,
1163 }) = extensions.remove::<OtelData>()
1164 {
1165 if self.tracked_inactivity {
1166 if let Some(timings) = extensions.get_mut::<Timings>() {
1168 let busy_ns = Key::new("busy_ns");
1169 let idle_ns = Key::new("idle_ns");
1170
1171 let attributes = builder
1172 .attributes
1173 .get_or_insert_with(|| Vec::with_capacity(2));
1174 attributes.push(KeyValue::new(busy_ns, timings.busy));
1175 attributes.push(KeyValue::new(idle_ns, timings.idle));
1176 }
1177 }
1178
1179 if let Some(DroppedOtelEvents { dropped_count }) =
1181 extensions.remove::<DroppedOtelEvents>()
1182 {
1183 let k = Key::from_static_str("dropped_event_count");
1184 let v = Value::I64(i64::try_from(dropped_count).unwrap_or(i64::MAX));
1185
1186 if let Some(ref mut attributes) = builder.attributes {
1187 if let Some(index) = attributes.iter().position(|kv| kv.key == k) {
1188 attributes[index] = KeyValue::new(k, v);
1189 } else {
1190 attributes.push(KeyValue::new(k, v));
1191 }
1192 } else {
1193 builder.attributes = Some(vec![KeyValue::new(k, v)]);
1194 }
1195 }
1196
1197 builder.start_with_context(&self.tracer, &parent_cx);
1199 }
1200 }
1201
1202 unsafe fn downcast_raw(&self, id: TypeId) -> Option<*const ()> {
1205 match id {
1206 id if id == TypeId::of::<Self>() => Some(self as *const _ as *const ()),
1207 id if id == TypeId::of::<WithContext>() => {
1208 Some(&self.get_context as *const _ as *const ())
1209 }
1210 _ => None,
1211 }
1212 }
1213}
1214
1215struct Timings {
1216 idle: i64,
1217 busy: i64,
1218 last: Instant,
1219}
1220
1221impl Timings {
1222 fn new() -> Self {
1223 Self {
1224 idle: 0,
1225 busy: 0,
1226 last: Instant::now(),
1227 }
1228 }
1229}
1230
1231fn thread_id_integer(id: thread::ThreadId) -> u64 {
1232 let thread_id = format!("{:?}", id);
1233 thread_id
1234 .trim_start_matches("ThreadId(")
1235 .trim_end_matches(')')
1236 .parse::<u64>()
1237 .expect("thread ID should parse as an integer")
1238}
1239
1240#[cfg(test)]
1241mod tests {
1242 use super::*;
1243 use opentelemetry::trace::{SpanContext, TraceFlags};
1244 use std::{
1245 collections::HashMap,
1246 error::Error,
1247 fmt::Display,
1248 sync::{Arc, Mutex},
1249 time::SystemTime,
1250 };
1251 use tracing_subscriber::prelude::*;
1252 use tracing_subscriber::registry::LookupSpan;
1253
1254 #[derive(Debug, Clone)]
1255 struct TestTracer(Arc<Mutex<Option<OtelData>>>);
1256 impl otel::Tracer for TestTracer {
1257 type Span = noop::NoopSpan;
1258 fn start_with_context<T>(&self, _name: T, _context: &OtelContext) -> Self::Span
1259 where
1260 T: Into<Cow<'static, str>>,
1261 {
1262 noop::NoopSpan::DEFAULT
1263 }
1264 fn span_builder<T>(&self, name: T) -> otel::SpanBuilder
1265 where
1266 T: Into<Cow<'static, str>>,
1267 {
1268 otel::SpanBuilder::from_name(name)
1269 }
1270 fn build_with_context(
1271 &self,
1272 builder: otel::SpanBuilder,
1273 parent_cx: &OtelContext,
1274 ) -> Self::Span {
1275 *self.0.lock().unwrap() = Some(OtelData {
1276 builder,
1277 parent_cx: parent_cx.clone(),
1278 });
1279 noop::NoopSpan::DEFAULT
1280 }
1281 }
1282
1283 impl PreSampledTracer for TestTracer {
1284 fn sampled_context(&self, _builder: &mut crate::OtelData) -> OtelContext {
1285 OtelContext::new()
1286 }
1287 fn new_trace_id(&self) -> otel::TraceId {
1288 otel::TraceId::INVALID
1289 }
1290 fn new_span_id(&self) -> otel::SpanId {
1291 otel::SpanId::INVALID
1292 }
1293 }
1294
1295 impl TestTracer {
1296 fn with_data<T>(&self, f: impl FnOnce(&OtelData) -> T) -> T {
1297 let lock = self.0.lock().unwrap();
1298 let data = lock.as_ref().expect("no span data has been recorded yet");
1299 f(data)
1300 }
1301 }
1302
1303 #[derive(Debug, Clone)]
1304 struct TestSpan(otel::SpanContext);
1305 impl otel::Span for TestSpan {
1306 fn add_event_with_timestamp<T: Into<Cow<'static, str>>>(
1307 &mut self,
1308 _: T,
1309 _: SystemTime,
1310 _: Vec<KeyValue>,
1311 ) {
1312 }
1313 fn span_context(&self) -> &otel::SpanContext {
1314 &self.0
1315 }
1316 fn is_recording(&self) -> bool {
1317 false
1318 }
1319 fn set_attribute(&mut self, _attribute: KeyValue) {}
1320 fn set_status(&mut self, _status: otel::Status) {}
1321 fn update_name<T: Into<Cow<'static, str>>>(&mut self, _new_name: T) {}
1322 fn add_link(&mut self, _span_context: SpanContext, _attributes: Vec<KeyValue>) {}
1323 fn end_with_timestamp(&mut self, _timestamp: SystemTime) {}
1324 }
1325
1326 #[derive(Debug)]
1327 struct TestDynError {
1328 msg: &'static str,
1329 source: Option<Box<TestDynError>>,
1330 }
1331 impl Display for TestDynError {
1332 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1333 write!(f, "{}", self.msg)
1334 }
1335 }
1336 impl Error for TestDynError {
1337 fn source(&self) -> Option<&(dyn Error + 'static)> {
1338 match &self.source {
1339 Some(source) => Some(source),
1340 None => None,
1341 }
1342 }
1343 }
1344 impl TestDynError {
1345 fn new(msg: &'static str) -> Self {
1346 Self { msg, source: None }
1347 }
1348 fn with_parent(self, parent_msg: &'static str) -> Self {
1349 Self {
1350 msg: parent_msg,
1351 source: Some(Box::new(self)),
1352 }
1353 }
1354 }
1355
1356 #[test]
1357 fn dynamic_span_names() {
1358 let dynamic_name = "GET http://example.com".to_string();
1359 let tracer = TestTracer(Arc::new(Mutex::new(None)));
1360 let subscriber = tracing_subscriber::registry().with(layer().with_tracer(tracer.clone()));
1361
1362 tracing::subscriber::with_default(subscriber, || {
1363 tracing::debug_span!("static_name", otel.name = dynamic_name.as_str());
1364 });
1365
1366 let recorded_name = tracer
1367 .0
1368 .lock()
1369 .unwrap()
1370 .as_ref()
1371 .map(|b| b.builder.name.clone());
1372 assert_eq!(recorded_name, Some(dynamic_name.into()))
1373 }
1374
1375 #[test]
1376 fn span_kind() {
1377 let tracer = TestTracer(Arc::new(Mutex::new(None)));
1378 let subscriber = tracing_subscriber::registry().with(layer().with_tracer(tracer.clone()));
1379
1380 tracing::subscriber::with_default(subscriber, || {
1381 tracing::debug_span!("request", otel.kind = "server");
1382 });
1383
1384 let recorded_kind = tracer.with_data(|data| data.builder.span_kind.clone());
1385 assert_eq!(recorded_kind, Some(otel::SpanKind::Server))
1386 }
1387
1388 #[test]
1389 fn span_status_code() {
1390 let tracer = TestTracer(Arc::new(Mutex::new(None)));
1391 let subscriber = tracing_subscriber::registry().with(layer().with_tracer(tracer.clone()));
1392
1393 tracing::subscriber::with_default(subscriber, || {
1394 tracing::debug_span!("request", otel.status_code = ?otel::Status::Ok);
1395 });
1396
1397 let recorded_status = tracer.with_data(|data| data.builder.status.clone());
1398 assert_eq!(recorded_status, otel::Status::Ok)
1399 }
1400
1401 #[test]
1402 fn span_status_message() {
1403 let tracer = TestTracer(Arc::new(Mutex::new(None)));
1404 let subscriber = tracing_subscriber::registry().with(layer().with_tracer(tracer.clone()));
1405
1406 let message = "message";
1407
1408 tracing::subscriber::with_default(subscriber, || {
1409 tracing::debug_span!("request", otel.status_message = message);
1410 });
1411
1412 let recorded_status_message = tracer
1413 .0
1414 .lock()
1415 .unwrap()
1416 .as_ref()
1417 .unwrap()
1418 .builder
1419 .status
1420 .clone();
1421
1422 assert_eq!(recorded_status_message, otel::Status::error(message))
1423 }
1424
1425 #[test]
1426 fn trace_id_from_existing_context() {
1427 let tracer = TestTracer(Arc::new(Mutex::new(None)));
1428 let subscriber = tracing_subscriber::registry().with(layer().with_tracer(tracer.clone()));
1429 let trace_id = otel::TraceId::from(42u128);
1430 let existing_cx = OtelContext::current_with_span(TestSpan(otel::SpanContext::new(
1431 trace_id,
1432 otel::SpanId::from(1u64),
1433 TraceFlags::default(),
1434 false,
1435 Default::default(),
1436 )));
1437 let _g = existing_cx.attach();
1438
1439 tracing::subscriber::with_default(subscriber, || {
1440 tracing::debug_span!("request", otel.kind = "server");
1441 });
1442
1443 let recorded_trace_id =
1444 tracer.with_data(|data| data.parent_cx.span().span_context().trace_id());
1445 assert_eq!(recorded_trace_id, trace_id)
1446 }
1447
1448 #[test]
1449 fn includes_timings() {
1450 let tracer = TestTracer(Arc::new(Mutex::new(None)));
1451 let subscriber = tracing_subscriber::registry().with(
1452 layer()
1453 .with_tracer(tracer.clone())
1454 .with_tracked_inactivity(true),
1455 );
1456
1457 tracing::subscriber::with_default(subscriber, || {
1458 tracing::debug_span!("request");
1459 });
1460
1461 let attributes = tracer.with_data(|data| data.builder.attributes.as_ref().unwrap().clone());
1462 let keys = attributes
1463 .iter()
1464 .map(|kv| kv.key.as_str())
1465 .collect::<Vec<&str>>();
1466 assert!(keys.contains(&"idle_ns"));
1467 assert!(keys.contains(&"busy_ns"));
1468 }
1469
1470 #[test]
1471 fn records_error_fields() {
1472 let tracer = TestTracer(Arc::new(Mutex::new(None)));
1473 let subscriber = tracing_subscriber::registry().with(layer().with_tracer(tracer.clone()));
1474
1475 let err = TestDynError::new("base error")
1476 .with_parent("intermediate error")
1477 .with_parent("user error");
1478
1479 tracing::subscriber::with_default(subscriber, || {
1480 tracing::debug_span!(
1481 "request",
1482 error = &err as &(dyn std::error::Error + 'static)
1483 );
1484 });
1485
1486 let attributes = tracer
1487 .0
1488 .lock()
1489 .unwrap()
1490 .as_ref()
1491 .unwrap()
1492 .builder
1493 .attributes
1494 .as_ref()
1495 .unwrap()
1496 .clone();
1497
1498 let key_values = attributes
1499 .into_iter()
1500 .map(|kv| (kv.key.as_str().to_owned(), kv.value))
1501 .collect::<HashMap<_, _>>();
1502
1503 assert_eq!(key_values["error"].as_str(), "user error");
1504 assert_eq!(
1505 key_values["error.chain"],
1506 Value::Array(
1507 vec![
1508 StringValue::from("intermediate error"),
1509 StringValue::from("base error")
1510 ]
1511 .into()
1512 )
1513 );
1514
1515 assert_eq!(key_values[FIELD_EXCEPTION_MESSAGE].as_str(), "user error");
1516 assert_eq!(
1517 key_values[FIELD_EXCEPTION_STACKTRACE],
1518 Value::Array(
1519 vec![
1520 StringValue::from("intermediate error"),
1521 StringValue::from("base error")
1522 ]
1523 .into()
1524 )
1525 );
1526 }
1527
1528 #[test]
1529 fn records_no_error_fields() {
1530 let tracer = TestTracer(Arc::new(Mutex::new(None)));
1531 let subscriber = tracing_subscriber::registry().with(
1532 layer()
1533 .with_error_records_to_exceptions(false)
1534 .with_tracer(tracer.clone()),
1535 );
1536
1537 let err = TestDynError::new("base error")
1538 .with_parent("intermediate error")
1539 .with_parent("user error");
1540
1541 tracing::subscriber::with_default(subscriber, || {
1542 tracing::debug_span!(
1543 "request",
1544 error = &err as &(dyn std::error::Error + 'static)
1545 );
1546 });
1547
1548 let attributes = tracer
1549 .0
1550 .lock()
1551 .unwrap()
1552 .as_ref()
1553 .unwrap()
1554 .builder
1555 .attributes
1556 .as_ref()
1557 .unwrap()
1558 .clone();
1559
1560 let key_values = attributes
1561 .into_iter()
1562 .map(|kv| (kv.key.as_str().to_owned(), kv.value))
1563 .collect::<HashMap<_, _>>();
1564
1565 assert_eq!(key_values["error"].as_str(), "user error");
1566 assert_eq!(
1567 key_values["error.chain"],
1568 Value::Array(
1569 vec![
1570 StringValue::from("intermediate error"),
1571 StringValue::from("base error")
1572 ]
1573 .into()
1574 )
1575 );
1576
1577 assert_eq!(key_values[FIELD_EXCEPTION_MESSAGE].as_str(), "user error");
1578 assert_eq!(
1579 key_values[FIELD_EXCEPTION_STACKTRACE],
1580 Value::Array(
1581 vec![
1582 StringValue::from("intermediate error"),
1583 StringValue::from("base error")
1584 ]
1585 .into()
1586 )
1587 );
1588 }
1589
1590 #[test]
1591 fn includes_span_location() {
1592 let tracer = TestTracer(Arc::new(Mutex::new(None)));
1593 let subscriber = tracing_subscriber::registry()
1594 .with(layer().with_tracer(tracer.clone()).with_location(true));
1595
1596 tracing::subscriber::with_default(subscriber, || {
1597 tracing::debug_span!("request");
1598 });
1599
1600 let attributes = tracer.with_data(|data| data.builder.attributes.as_ref().unwrap().clone());
1601 let keys = attributes
1602 .iter()
1603 .map(|kv| kv.key.as_str())
1604 .collect::<Vec<&str>>();
1605 assert!(keys.contains(&"code.filepath"));
1606 assert!(keys.contains(&"code.namespace"));
1607 assert!(keys.contains(&"code.lineno"));
1608 }
1609
1610 #[test]
1611 fn excludes_span_location() {
1612 let tracer = TestTracer(Arc::new(Mutex::new(None)));
1613 let subscriber = tracing_subscriber::registry()
1614 .with(layer().with_tracer(tracer.clone()).with_location(false));
1615
1616 tracing::subscriber::with_default(subscriber, || {
1617 tracing::debug_span!("request");
1618 });
1619
1620 let attributes = tracer.with_data(|data| data.builder.attributes.as_ref().unwrap().clone());
1621 let keys = attributes
1622 .iter()
1623 .map(|kv| kv.key.as_str())
1624 .collect::<Vec<&str>>();
1625 assert!(!keys.contains(&"code.filepath"));
1626 assert!(!keys.contains(&"code.namespace"));
1627 assert!(!keys.contains(&"code.lineno"));
1628 }
1629
1630 #[test]
1631 fn includes_thread() {
1632 let thread = thread::current();
1633 let expected_name = thread
1634 .name()
1635 .map(|name| Value::String(name.to_owned().into()));
1636 let expected_id = Value::I64(thread_id_integer(thread.id()) as i64);
1637
1638 let tracer = TestTracer(Arc::new(Mutex::new(None)));
1639 let subscriber = tracing_subscriber::registry()
1640 .with(layer().with_tracer(tracer.clone()).with_threads(true));
1641
1642 tracing::subscriber::with_default(subscriber, || {
1643 tracing::debug_span!("request");
1644 });
1645
1646 let attributes = tracer
1647 .with_data(|data| data.builder.attributes.as_ref().unwrap().clone())
1648 .drain(..)
1649 .map(|kv| (kv.key.as_str().to_string(), kv.value))
1650 .collect::<HashMap<_, _>>();
1651 assert_eq!(attributes.get("thread.name"), expected_name.as_ref());
1652 assert_eq!(attributes.get("thread.id"), Some(&expected_id));
1653 }
1654
1655 #[test]
1656 fn excludes_thread() {
1657 let tracer = TestTracer(Arc::new(Mutex::new(None)));
1658 let subscriber = tracing_subscriber::registry()
1659 .with(layer().with_tracer(tracer.clone()).with_threads(false));
1660
1661 tracing::subscriber::with_default(subscriber, || {
1662 tracing::debug_span!("request");
1663 });
1664
1665 let attributes = tracer.with_data(|data| data.builder.attributes.as_ref().unwrap().clone());
1666 let keys = attributes
1667 .iter()
1668 .map(|kv| kv.key.as_str())
1669 .collect::<Vec<&str>>();
1670 assert!(!keys.contains(&"thread.name"));
1671 assert!(!keys.contains(&"thread.id"));
1672 }
1673
1674 #[test]
1675 fn propagates_error_fields_from_event_to_span() {
1676 let tracer = TestTracer(Arc::new(Mutex::new(None)));
1677 let subscriber = tracing_subscriber::registry().with(layer().with_tracer(tracer.clone()));
1678
1679 let err = TestDynError::new("base error")
1680 .with_parent("intermediate error")
1681 .with_parent("user error");
1682
1683 tracing::subscriber::with_default(subscriber, || {
1684 let _guard = tracing::debug_span!("request",).entered();
1685
1686 tracing::error!(
1687 error = &err as &(dyn std::error::Error + 'static),
1688 "request error!"
1689 )
1690 });
1691
1692 let attributes = tracer
1693 .0
1694 .lock()
1695 .unwrap()
1696 .as_ref()
1697 .unwrap()
1698 .builder
1699 .attributes
1700 .as_ref()
1701 .unwrap()
1702 .clone();
1703
1704 let key_values = attributes
1705 .into_iter()
1706 .map(|kv| (kv.key.as_str().to_owned(), kv.value))
1707 .collect::<HashMap<_, _>>();
1708
1709 assert_eq!(key_values[FIELD_EXCEPTION_MESSAGE].as_str(), "user error");
1710 assert_eq!(
1711 key_values[FIELD_EXCEPTION_STACKTRACE],
1712 Value::Array(
1713 vec![
1714 StringValue::from("intermediate error"),
1715 StringValue::from("base error")
1716 ]
1717 .into()
1718 )
1719 );
1720 }
1721
1722 #[test]
1723 fn propagates_no_error_fields_from_event_to_span() {
1724 let tracer = TestTracer(Arc::new(Mutex::new(None)));
1725 let subscriber = tracing_subscriber::registry().with(
1726 layer()
1727 .with_error_fields_to_exceptions(false)
1728 .with_tracer(tracer.clone()),
1729 );
1730
1731 let err = TestDynError::new("base error")
1732 .with_parent("intermediate error")
1733 .with_parent("user error");
1734
1735 tracing::subscriber::with_default(subscriber, || {
1736 let _guard = tracing::debug_span!("request",).entered();
1737
1738 tracing::error!(
1739 error = &err as &(dyn std::error::Error + 'static),
1740 "request error!"
1741 )
1742 });
1743
1744 let attributes = tracer
1745 .0
1746 .lock()
1747 .unwrap()
1748 .as_ref()
1749 .unwrap()
1750 .builder
1751 .attributes
1752 .as_ref()
1753 .unwrap()
1754 .clone();
1755
1756 let key_values = attributes
1757 .into_iter()
1758 .map(|kv| (kv.key.as_str().to_owned(), kv.value))
1759 .collect::<HashMap<_, _>>();
1760
1761 assert_eq!(key_values[FIELD_EXCEPTION_MESSAGE].as_str(), "user error");
1762 assert_eq!(
1763 key_values[FIELD_EXCEPTION_STACKTRACE],
1764 Value::Array(
1765 vec![
1766 StringValue::from("intermediate error"),
1767 StringValue::from("base error")
1768 ]
1769 .into()
1770 )
1771 );
1772 }
1773
1774 #[test]
1775 fn tracks_dropped_events() {
1776 let tracer = TestTracer(Arc::new(Mutex::new(None)));
1777 let subscriber = tracing_subscriber::registry()
1778 .with(layer().with_tracer(tracer.clone()).max_events_per_span(4));
1779 let subscriber = Arc::new(subscriber);
1780
1781 tracing::subscriber::with_default(subscriber.clone(), || {
1782 let span = tracing::debug_span!("request");
1783 let _span_guard = span.enter();
1784
1785 tracing::info!("event 1");
1787 tracing::info!("event 2");
1788 tracing::info!("event 3");
1789 tracing::info!("event 4");
1790
1791 let data = span.id().and_then(|id| subscriber.span(&id)).unwrap();
1792 let extensions = data.extensions();
1793
1794 assert!(extensions.get::<DroppedOtelEvents>().is_none());
1796
1797 let otel_data = extensions.get::<OtelData>().unwrap();
1799 let otel_events = otel_data.builder.events.as_ref().unwrap();
1800 assert_eq!(otel_events.len(), 4);
1801
1802 drop(extensions);
1803 drop(data);
1804
1805 tracing::info!("event 5");
1807
1808 let data = span.id().and_then(|id| subscriber.span(&id)).unwrap();
1809 let extensions = data.extensions();
1810
1811 let dropped_events = extensions.get::<DroppedOtelEvents>().unwrap();
1813 assert_eq!(dropped_events.dropped_count, 3);
1814
1815 let otel_data = extensions.get::<OtelData>().unwrap();
1816 let otel_events = otel_data.builder.events.as_ref().unwrap();
1817 assert_eq!(otel_events.len(), 2);
1819
1820 drop(extensions);
1821 drop(data);
1822
1823 for i in 6..99 {
1825 tracing::info!("event {}", i);
1826 }
1827
1828 let data = span.id().and_then(|id| subscriber.span(&id)).unwrap();
1829 let extensions = data.extensions();
1830
1831 let dropped_events = extensions.get::<DroppedOtelEvents>().unwrap();
1833 assert_eq!(dropped_events.dropped_count, 96);
1834
1835 let otel_data = extensions.get::<OtelData>().unwrap();
1836 let otel_events = otel_data.builder.events.as_ref().unwrap();
1837 assert_eq!(otel_events.len(), 2);
1838
1839 assert_eq!(otel_events.capacity(), 8);
1842 });
1843
1844 let attributes = tracer.with_data(|data| data.builder.attributes.as_ref().unwrap().clone());
1846 let attributes = attributes
1847 .into_iter()
1848 .map(|kv| (kv.key.as_str().to_owned(), kv.value))
1849 .collect::<HashMap<_, _>>();
1850
1851 let dropped_count_value = attributes.get("dropped_event_count").unwrap();
1852 let dropped_count: i64 = dropped_count_value.as_str().parse().unwrap();
1853
1854 assert_eq!(dropped_count, 96);
1855 }
1856
1857 #[test]
1858 fn max_events_per_span_zero() {
1859 let tracer = TestTracer(Arc::new(Mutex::new(None)));
1860 let subscriber = tracing_subscriber::registry()
1861 .with(layer().with_tracer(tracer.clone()).max_events_per_span(0));
1862 let subscriber = Arc::new(subscriber);
1863
1864 tracing::subscriber::with_default(subscriber.clone(), || {
1865 let span = tracing::debug_span!("request");
1866 let _span_guard = span.enter();
1867
1868 tracing::info!("event 1");
1870
1871 let data = span.id().and_then(|id| subscriber.span(&id)).unwrap();
1872 let extensions = data.extensions();
1873
1874 let dropped_events = extensions.get::<DroppedOtelEvents>().unwrap();
1876 assert_eq!(dropped_events.dropped_count, 1);
1877
1878 let otel_data = extensions.get::<OtelData>().unwrap();
1879 let otel_events = otel_data.builder.events.as_ref().unwrap();
1880 assert!(otel_events.is_empty());
1881 });
1882
1883 let attributes = tracer.with_data(|data| data.builder.attributes.as_ref().unwrap().clone());
1885 let attributes = attributes
1886 .into_iter()
1887 .map(|kv| (kv.key.as_str().to_owned(), kv.value))
1888 .collect::<HashMap<_, _>>();
1889
1890 let dropped_count_value = attributes.get("dropped_event_count").unwrap();
1891 let dropped_count: i64 = dropped_count_value.as_str().parse().unwrap();
1892
1893 assert_eq!(dropped_count, 1);
1894 }
1895
1896 #[test]
1897 fn exclude_max_events_per_span() {
1898 let tracer = TestTracer(Arc::new(Mutex::new(None)));
1899 let subscriber = tracing_subscriber::registry().with(layer().with_tracer(tracer.clone()));
1900 let subscriber = Arc::new(subscriber);
1901
1902 tracing::subscriber::with_default(subscriber.clone(), || {
1903 let span = tracing::debug_span!("request");
1904 let _span_guard = span.enter();
1905
1906 tracing::info!("event 1");
1908
1909 let data = span.id().and_then(|id| subscriber.span(&id)).unwrap();
1910 let extensions = data.extensions();
1911
1912 assert!(extensions.get::<DroppedOtelEvents>().is_none());
1914 });
1915
1916 let attributes = tracer.with_data(|data| data.builder.attributes.as_ref().unwrap().clone());
1918 let attributes = attributes
1919 .into_iter()
1920 .map(|kv| (kv.key.as_str().to_owned(), kv.value))
1921 .collect::<HashMap<_, _>>();
1922
1923 assert!(!attributes.contains_key("dropped_event_count"));
1924 }
1925
1926 #[test]
1927 fn max_events_per_span_one() {
1928 let tracer = TestTracer(Arc::new(Mutex::new(None)));
1929 let subscriber = tracing_subscriber::registry()
1930 .with(layer().with_tracer(tracer.clone()).max_events_per_span(1));
1931 let subscriber = Arc::new(subscriber);
1932
1933 tracing::subscriber::with_default(subscriber.clone(), || {
1934 let span = tracing::debug_span!("request");
1935 let _span_guard = span.enter();
1936
1937 tracing::info!("event 1");
1939
1940 let data = span.id().and_then(|id| subscriber.span(&id)).unwrap();
1941 let extensions = data.extensions();
1942
1943 assert!(extensions.get::<DroppedOtelEvents>().is_none());
1945
1946 let otel_data = extensions.get::<OtelData>().unwrap();
1948 let otel_events = otel_data.builder.events.as_ref().unwrap();
1949 assert_eq!(otel_events.len(), 1);
1950
1951 drop(extensions);
1952 drop(data);
1953
1954 tracing::info!("event 2");
1956
1957 let data = span.id().and_then(|id| subscriber.span(&id)).unwrap();
1958 let extensions = data.extensions();
1959
1960 let dropped_events = extensions.get::<DroppedOtelEvents>().unwrap();
1962 assert_eq!(dropped_events.dropped_count, 1);
1963
1964 let otel_data = extensions.get::<OtelData>().unwrap();
1966 let otel_events = otel_data.builder.events.as_ref().unwrap();
1967 assert_eq!(otel_events.len(), 1);
1968 });
1969
1970 let attributes = tracer.with_data(|data| data.builder.attributes.as_ref().unwrap().clone());
1972 let attributes = attributes
1973 .into_iter()
1974 .map(|kv| (kv.key.as_str().to_owned(), kv.value))
1975 .collect::<HashMap<_, _>>();
1976
1977 let dropped_count_value = attributes.get("dropped_event_count").unwrap();
1978 let dropped_count: i64 = dropped_count_value.as_str().parse().unwrap();
1979
1980 assert_eq!(dropped_count, 1);
1981 }
1982
1983 #[test]
1984 fn max_events_enabled_not_exceeding_max() {
1985 let tracer = TestTracer(Arc::new(Mutex::new(None)));
1986 let subscriber = tracing_subscriber::registry()
1987 .with(layer().with_tracer(tracer.clone()).max_events_per_span(32));
1988 let subscriber = Arc::new(subscriber);
1989
1990 tracing::subscriber::with_default(subscriber.clone(), || {
1991 tracing::debug_span!("request");
1992 });
1993
1994 let attributes = tracer.with_data(|data| data.builder.attributes.as_ref().unwrap().clone());
1996 let attributes = attributes
1997 .into_iter()
1998 .map(|kv| (kv.key.as_str().to_owned(), kv.value))
1999 .collect::<HashMap<_, _>>();
2000
2001 assert!(!attributes.contains_key("dropped_event_count"));
2002 }
2003
2004 #[test]
2005 fn tracing_error_compatibility() {
2006 let tracer = TestTracer(Arc::new(Mutex::new(None)));
2007 let subscriber = tracing_subscriber::registry()
2008 .with(
2009 layer()
2010 .with_error_fields_to_exceptions(false)
2011 .with_tracer(tracer.clone()),
2012 )
2013 .with(tracing_error::ErrorLayer::default());
2014
2015 tracing::subscriber::with_default(subscriber, || {
2016 let span = tracing::info_span!("Blows up!", exception = tracing::field::Empty);
2017 let _entered = span.enter();
2018 let context = tracing_error::SpanTrace::capture();
2019
2020 span.record("exception", &tracing::field::debug(&context));
2022 tracing::info!(exception = &tracing::field::debug(&context), "hello");
2024 });
2025
2026 }
2028}