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