1mod config;
10mod events;
11mod export;
12mod id_generator;
13mod links;
14mod provider;
15mod sampler;
16mod span;
17mod span_limit;
18mod span_processor;
19#[cfg(feature = "experimental_trace_batch_span_processor_with_async_runtime")]
20pub mod span_processor_with_async_runtime;
22mod tracer;
23
24pub use config::Config;
25pub use events::SpanEvents;
26pub use export::{SpanData, SpanExporter};
27
28#[cfg(any(feature = "testing", test))]
30#[cfg_attr(docsrs, doc(cfg(any(feature = "testing", test))))]
31pub mod in_memory_exporter;
32#[cfg(any(feature = "testing", test))]
33#[cfg_attr(docsrs, doc(cfg(any(feature = "testing", test))))]
34pub use in_memory_exporter::{InMemorySpanExporter, InMemorySpanExporterBuilder};
35
36pub use id_generator::{IdGenerator, RandomIdGenerator};
37pub use links::SpanLinks;
38pub use provider::{SdkTracerProvider, TracerProviderBuilder};
39pub use sampler::{Sampler, SamplingDecision, SamplingResult, ShouldSample};
40pub use span::Span;
41pub use span_limit::SpanLimits;
42pub use span_processor::{
43 BatchConfig, BatchConfigBuilder, BatchSpanProcessor, BatchSpanProcessorBuilder,
44 SimpleSpanProcessor, SpanProcessor, OTEL_BSP_EXPORT_TIMEOUT, OTEL_BSP_EXPORT_TIMEOUT_DEFAULT,
45 OTEL_BSP_MAX_EXPORT_BATCH_SIZE, OTEL_BSP_MAX_EXPORT_BATCH_SIZE_DEFAULT,
46 OTEL_BSP_MAX_QUEUE_SIZE, OTEL_BSP_MAX_QUEUE_SIZE_DEFAULT, OTEL_BSP_SCHEDULE_DELAY,
47 OTEL_BSP_SCHEDULE_DELAY_DEFAULT,
48};
49
50pub use tracer::SdkTracer;
51pub use tracer::SdkTracer as Tracer; #[cfg(feature = "jaeger_remote_sampler")]
54pub use sampler::{JaegerRemoteSampler, JaegerRemoteSamplerBuilder};
55
56#[cfg(feature = "experimental_trace_batch_span_processor_with_async_runtime")]
57#[cfg(test)]
58mod runtime_tests;
59
60#[cfg(all(test, feature = "testing"))]
61mod tests {
62 use super::*;
63 use crate::error::OTelSdkResult;
64 use crate::trace::{SamplingDecision, SamplingResult};
65 use crate::{
66 trace::span_limit::{DEFAULT_MAX_EVENT_PER_SPAN, DEFAULT_MAX_LINKS_PER_SPAN},
67 trace::{InMemorySpanExporter, InMemorySpanExporterBuilder},
68 };
69 use opentelemetry::{
70 baggage::BaggageExt,
71 trace::{get_active_span, SpanKind, Status, TraceContextExt, TraceState},
72 };
73 use opentelemetry::{testing::trace::TestSpan, InstrumentationScope};
74 use opentelemetry::{
75 trace::{
76 Event, Link, Span, SpanBuilder, SpanContext, SpanId, TraceFlags, TraceId, Tracer,
77 TracerProvider,
78 },
79 Context, KeyValue,
80 };
81 use std::time::Duration;
82
83 #[test]
85 fn nested_span_in_get_active_span_with_simple_processor() {
86 let exporter = InMemorySpanExporterBuilder::new().build();
87 let provider = SdkTracerProvider::builder()
88 .with_span_processor(SimpleSpanProcessor::new(exporter.clone()))
89 .build();
90 let tracer = provider.tracer("test_tracer");
91
92 get_active_span(|_span| {
93 let _nested = tracer.span_builder("nested").start(&tracer);
94 });
95 }
96
97 #[test]
98 fn span_modification_via_context() {
99 let exporter = InMemorySpanExporterBuilder::new().build();
100 let provider = SdkTracerProvider::builder()
101 .with_span_processor(SimpleSpanProcessor::new(exporter.clone()))
102 .build();
103 let tracer = provider.tracer("test_tracer");
104
105 #[derive(Debug, PartialEq)]
106 struct ValueA(u64);
107
108 let span = tracer.start("span-name");
109
110 let cx = Context::current();
112 assert!(!cx.has_active_span());
113
114 let cx_with_span = cx.with_span(span);
116 assert!(cx_with_span.has_active_span());
117 assert!(!cx.has_active_span());
118
119 let span_ref = cx_with_span.span();
123 span_ref.set_attribute(KeyValue::new("attribute1", "value1"));
124
125 let cx_with_span_and_more = cx_with_span.with_value(ValueA(1));
127
128 let span_ref_new = cx_with_span_and_more.span();
131 span_ref_new.set_attribute(KeyValue::new("attribute2", "value2"));
132
133 span_ref_new.end();
134
135 let exported_spans = exporter
136 .get_finished_spans()
137 .expect("Spans are expected to be exported.");
138 assert_eq!(exported_spans.len(), 1);
140 let span = &exported_spans[0];
141 assert_eq!(span.attributes.len(), 2);
142 }
143
144 #[derive(Debug)]
145 struct BaggageInspectingSpanProcessor;
146 impl SpanProcessor for BaggageInspectingSpanProcessor {
147 fn on_start(&self, span: &mut crate::trace::Span, cx: &Context) {
148 let baggage = cx.baggage();
149 if let Some(baggage_value) = baggage.get("bag-key") {
150 span.set_attribute(KeyValue::new("bag-key", baggage_value.to_string()));
151 } else {
152 unreachable!("Baggage should be present in the context");
153 }
154 }
155
156 fn on_end(&self, span: SpanData) {
157 Context::current();
160
161 assert!(
167 span.attributes
168 .iter()
169 .any(|kv| kv.key.as_str() == "bag-key"),
170 "Baggage should have been stored as span attribute in on_start"
171 );
172 }
173
174 fn force_flush(&self) -> crate::error::OTelSdkResult {
175 Ok(())
176 }
177
178 fn shutdown_with_timeout(&self, _timeout: Duration) -> OTelSdkResult {
179 Ok(())
180 }
181 }
182
183 #[test]
184 fn span_and_baggage() {
185 let provider = SdkTracerProvider::builder()
186 .with_span_processor(BaggageInspectingSpanProcessor)
187 .build();
188
189 let cx_with_baggage =
190 Context::current_with_baggage(vec![KeyValue::new("bag-key", "bag-value")]);
191
192 assert_eq!(
194 cx_with_baggage
195 .baggage()
196 .get("bag-key")
197 .unwrap()
198 .to_string(),
199 "bag-value"
200 );
201
202 let _cx_guard1 = cx_with_baggage.attach();
204 assert_eq!(
206 Context::current()
207 .baggage()
208 .get("bag-key")
209 .unwrap()
210 .to_string(),
211 "bag-value"
212 );
213
214 let tracer = provider.tracer("test_tracer");
215 let mut span = tracer
216 .span_builder("span-name")
217 .start_with_context(&tracer, &Context::current());
218 span.set_attribute(KeyValue::new("attribute1", "value1"));
219
220 let cx = Context::current();
223 assert!(!cx.has_active_span());
224
225 let cx_with_baggage_and_span = cx.with_span(span);
227 assert!(cx_with_baggage_and_span.has_active_span());
228 assert_eq!(
229 cx_with_baggage_and_span
230 .baggage()
231 .get("bag-key")
232 .unwrap()
233 .to_string(),
234 "bag-value"
235 );
236
237 let _cx_guard2 = cx_with_baggage_and_span.attach();
238 assert!(Context::current().has_active_span());
240 assert_eq!(
241 Context::current()
242 .baggage()
243 .get("bag-key")
244 .unwrap()
245 .to_string(),
246 "bag-value"
247 );
248 }
249
250 #[test]
251 fn tracer_in_span() {
252 let exporter = InMemorySpanExporterBuilder::new().build();
254 let provider = SdkTracerProvider::builder()
255 .with_span_processor(SimpleSpanProcessor::new(exporter.clone()))
256 .build();
257
258 let tracer = provider.tracer("test_tracer");
260 tracer.in_span("span_name", |cx| {
261 let span = cx.span();
262 assert!(span.is_recording());
263 span.update_name("span_name_updated");
264 span.set_attribute(KeyValue::new("attribute1", "value1"));
265 span.add_event("test-event".to_string(), vec![]);
266 span.add_link(
267 SpanContext::new(
268 TraceId::from(47),
269 SpanId::from(11),
270 TraceFlags::default(),
271 false,
272 Default::default(),
273 ),
274 vec![],
275 );
276 });
277
278 let exported_spans = exporter
280 .get_finished_spans()
281 .expect("Spans are expected to be exported.");
282 assert_eq!(exported_spans.len(), 1);
283 let span = &exported_spans[0];
284 assert_eq!(span.name, "span_name_updated");
285 assert_eq!(span.instrumentation_scope.name(), "test_tracer");
286 assert_eq!(span.attributes.len(), 1);
287 assert_eq!(span.events.len(), 1);
288 assert_eq!(span.events[0].name, "test-event");
289 assert_eq!(span.links.len(), 1);
290 assert_eq!(span.links[0].span_context.trace_id(), TraceId::from(47));
291 assert_eq!(span.links[0].span_context.span_id(), SpanId::from(11));
292 assert_eq!(span.span_context.trace_flags(), TraceFlags::SAMPLED);
293 assert!(!span.span_context.is_remote());
294 assert_eq!(span.status, Status::Unset);
295 }
296
297 #[test]
298 fn tracer_start() {
299 let exporter = InMemorySpanExporterBuilder::new().build();
301 let provider = SdkTracerProvider::builder()
302 .with_span_processor(SimpleSpanProcessor::new(exporter.clone()))
303 .build();
304
305 let tracer = provider.tracer("test_tracer");
307 let mut span = tracer.start("span_name");
308 span.set_attribute(KeyValue::new("attribute1", "value1"));
309 span.add_event("test-event".to_string(), vec![]);
310 span.set_status(Status::error("cancelled"));
311 span.end();
312
313 span.update_name("span_name_updated");
315
316 let exported_spans = exporter
318 .get_finished_spans()
319 .expect("Spans are expected to be exported.");
320 assert_eq!(exported_spans.len(), 1);
321 let span = &exported_spans[0];
322 assert_eq!(span.name, "span_name");
323 assert_eq!(span.instrumentation_scope.name(), "test_tracer");
324 assert_eq!(span.attributes.len(), 1);
325 assert_eq!(span.events.len(), 1);
326 assert_eq!(span.events[0].name, "test-event");
327 assert_eq!(span.span_context.trace_flags(), TraceFlags::SAMPLED);
328 assert!(!span.span_context.is_remote());
329 let status_expected = Status::error("cancelled");
330 assert_eq!(span.status, status_expected);
331 }
332
333 #[test]
334 fn tracer_span_builder() {
335 let exporter = InMemorySpanExporterBuilder::new().build();
337 let provider = SdkTracerProvider::builder()
338 .with_span_processor(SimpleSpanProcessor::new(exporter.clone()))
339 .build();
340
341 let tracer = provider.tracer("test_tracer");
343 let mut span = tracer
344 .span_builder("span_name")
345 .with_kind(SpanKind::Server)
346 .start(&tracer);
347 span.set_attribute(KeyValue::new("attribute1", "value1"));
348 span.add_event("test-event".to_string(), vec![]);
349 span.set_status(Status::Ok);
350 drop(span);
351
352 let exported_spans = exporter
354 .get_finished_spans()
355 .expect("Spans are expected to be exported.");
356 assert_eq!(exported_spans.len(), 1);
357 let span = &exported_spans[0];
358 assert_eq!(span.name, "span_name");
359 assert_eq!(span.span_kind, SpanKind::Server);
360 assert_eq!(span.instrumentation_scope.name(), "test_tracer");
361 assert_eq!(span.attributes.len(), 1);
362 assert_eq!(span.events.len(), 1);
363 assert_eq!(span.events[0].name, "test-event");
364 assert_eq!(span.span_context.trace_flags(), TraceFlags::SAMPLED);
365 assert!(!span.span_context.is_remote());
366 assert_eq!(span.status, Status::Ok);
367 }
368
369 #[test]
370 fn exceed_span_links_limit() {
371 let exporter = InMemorySpanExporterBuilder::new().build();
373 let provider = SdkTracerProvider::builder()
374 .with_span_processor(SimpleSpanProcessor::new(exporter.clone()))
375 .build();
376
377 let tracer = provider.tracer("test_tracer");
379
380 let mut links = Vec::new();
381 for _i in 0..(DEFAULT_MAX_LINKS_PER_SPAN * 2) {
382 links.push(Link::with_context(SpanContext::new(
383 TraceId::from(12),
384 SpanId::from(12),
385 TraceFlags::default(),
386 false,
387 Default::default(),
388 )))
389 }
390
391 let span_builder = SpanBuilder::from_name("span_name").with_links(links);
392 let mut span = tracer.build(span_builder);
393 span.end();
394
395 let exported_spans = exporter
397 .get_finished_spans()
398 .expect("Spans are expected to be exported.");
399 assert_eq!(exported_spans.len(), 1);
400 let span = &exported_spans[0];
401 assert_eq!(span.name, "span_name");
402 assert_eq!(span.links.len(), DEFAULT_MAX_LINKS_PER_SPAN as usize);
403 }
404
405 #[test]
406 fn exceed_span_events_limit() {
407 let exporter = InMemorySpanExporterBuilder::new().build();
409 let provider = SdkTracerProvider::builder()
410 .with_span_processor(SimpleSpanProcessor::new(exporter.clone()))
411 .build();
412
413 let tracer = provider.tracer("test_tracer");
415
416 let mut events = Vec::new();
417 for _i in 0..(DEFAULT_MAX_EVENT_PER_SPAN * 2) {
418 events.push(Event::with_name("test event"))
419 }
420
421 let span_builder = SpanBuilder::from_name("span_name").with_events(events);
423 let mut span = tracer.build(span_builder);
424
425 span.add_event("test event again, after span builder", Vec::new());
427 span.add_event("test event once again, after span builder", Vec::new());
428 span.end();
429
430 let exported_spans = exporter
432 .get_finished_spans()
433 .expect("Spans are expected to be exported.");
434 assert_eq!(exported_spans.len(), 1);
435 let span = &exported_spans[0];
436 assert_eq!(span.name, "span_name");
437 assert_eq!(span.events.len(), DEFAULT_MAX_EVENT_PER_SPAN as usize);
438 assert_eq!(span.events.dropped_count, DEFAULT_MAX_EVENT_PER_SPAN + 2);
439 }
440
441 #[test]
442 fn trace_state_for_dropped_sampler() {
443 let exporter = InMemorySpanExporterBuilder::new().build();
444 let provider = SdkTracerProvider::builder()
445 .with_sampler(Sampler::AlwaysOff)
446 .with_span_processor(SimpleSpanProcessor::new(exporter.clone()))
447 .build();
448
449 let tracer = provider.tracer("test");
450 let trace_state = TraceState::from_key_value(vec![("foo", "bar")]).unwrap();
451
452 let parent_context = Context::new().with_span(TestSpan(SpanContext::new(
453 TraceId::from(10000),
454 SpanId::from(20),
455 TraceFlags::SAMPLED,
456 true,
457 trace_state.clone(),
458 )));
459
460 let span = tracer.start_with_context("span", &parent_context);
461 assert_eq!(
462 span.span_context().trace_state().get("foo"),
463 trace_state.get("foo")
464 )
465 }
466
467 #[derive(Clone, Debug, Default)]
468 struct TestRecordOnlySampler {}
469
470 impl ShouldSample for TestRecordOnlySampler {
471 fn should_sample(
472 &self,
473 parent_context: Option<&Context>,
474 _trace_id: TraceId,
475 _name: &str,
476 _span_kind: &SpanKind,
477 _attributes: &[KeyValue],
478 _links: &[Link],
479 ) -> SamplingResult {
480 let trace_state = parent_context
481 .unwrap()
482 .span()
483 .span_context()
484 .trace_state()
485 .clone();
486 SamplingResult {
487 decision: SamplingDecision::RecordOnly,
488 attributes: vec![KeyValue::new("record_only_key", "record_only_value")],
489 trace_state,
490 }
491 }
492 }
493
494 #[test]
495 fn trace_state_for_record_only_sampler() {
496 let exporter = InMemorySpanExporterBuilder::new().build();
497 let provider = SdkTracerProvider::builder()
498 .with_sampler(TestRecordOnlySampler::default())
499 .with_span_processor(SimpleSpanProcessor::new(exporter.clone()))
500 .build();
501
502 let tracer = provider.tracer("test");
503 let trace_state = TraceState::from_key_value(vec![("foo", "bar")]).unwrap();
504
505 let parent_context = Context::new().with_span(TestSpan(SpanContext::new(
506 TraceId::from(10000),
507 SpanId::from(20),
508 TraceFlags::SAMPLED,
509 true,
510 trace_state.clone(),
511 )));
512
513 let span = tracer.build_with_context(
514 SpanBuilder::from_name("span")
515 .with_attributes(vec![KeyValue::new("extra_attr_key", "extra_attr_value")]),
516 &parent_context,
517 );
518 assert!(!span.span_context().trace_flags().is_sampled());
519 assert_eq!(
520 span.exported_data().unwrap().attributes,
521 vec![
522 KeyValue::new("extra_attr_key", "extra_attr_value"),
523 KeyValue::new("record_only_key", "record_only_value")
524 ]
525 );
526 assert_eq!(span.span_context().trace_state().get("foo"), Some("bar"));
527 }
528
529 #[test]
530 fn tracer_attributes() {
531 let provider = SdkTracerProvider::builder().build();
532 let scope = InstrumentationScope::builder("basic")
533 .with_attributes(vec![KeyValue::new("test_k", "test_v")])
534 .build();
535
536 let tracer = provider.tracer_with_scope(scope);
537 let instrumentation_scope = tracer.instrumentation_scope();
538 assert!(instrumentation_scope
539 .attributes()
540 .eq(&[KeyValue::new("test_k", "test_v")]));
541 }
542
543 #[tokio::test(flavor = "multi_thread", worker_threads = 1)]
544 async fn empty_tracer_name_retained() {
545 async fn tracer_name_retained_helper(
546 tracer: super::SdkTracer,
547 provider: SdkTracerProvider,
548 exporter: InMemorySpanExporter,
549 ) {
550 tracer.start("my_span").end();
552
553 assert!(provider.force_flush().is_ok());
555
556 let finished_spans = exporter
558 .get_finished_spans()
559 .expect("spans are expected to be exported.");
560 assert_eq!(finished_spans.len(), 1, "There should be a single span");
561
562 let tracer_name = finished_spans[0].instrumentation_scope.name();
563 assert_eq!(tracer_name, "", "The tracer name should be an empty string");
564
565 exporter.reset();
566 }
567
568 let exporter = InMemorySpanExporter::default();
569 let span_processor = SimpleSpanProcessor::new(exporter.clone());
570 let tracer_provider = SdkTracerProvider::builder()
571 .with_span_processor(span_processor)
572 .build();
573
574 let tracer1 = tracer_provider.tracer("");
576 tracer_name_retained_helper(tracer1, tracer_provider.clone(), exporter.clone()).await;
577
578 let tracer_scope = InstrumentationScope::builder("").build();
579 let tracer2 = tracer_provider.tracer_with_scope(tracer_scope);
580 tracer_name_retained_helper(tracer2, tracer_provider, exporter).await;
581 }
582
583 #[test]
584 fn trace_suppression() {
585 let exporter = InMemorySpanExporter::default();
587 let span_processor = SimpleSpanProcessor::new(exporter.clone());
588 let tracer_provider = SdkTracerProvider::builder()
589 .with_span_processor(span_processor)
590 .build();
591
592 let tracer = tracer_provider.tracer("test");
594 {
595 let _suppressed_context = Context::enter_telemetry_suppressed_scope();
596 let _span = tracer.span_builder("span_name").start(&tracer);
598 }
599
600 let finished_spans = exporter.get_finished_spans().expect("this should not fail");
602 assert_eq!(
603 finished_spans.len(),
604 0,
605 "There should be a no spans as span emission is done inside a suppressed context"
606 );
607 }
608}