1 use futures_util::future::BoxFuture;
2 use opentelemetry::{
3 propagation::TextMapPropagator,
4 sdk::{
5 export::trace::{ExportResult, SpanData, SpanExporter},
6 propagation::{BaggagePropagator, TextMapCompositePropagator, TraceContextPropagator},
7 trace::{Tracer, TracerProvider},
8 },
9 trace::{SpanContext, TraceContextExt, Tracer as _, TracerProvider as _},
10 Context,
11 };
12 use std::collections::{HashMap, HashSet};
13 use std::sync::{Arc, Mutex};
14 use tracing::Subscriber;
15 use tracing_opentelemetry::{layer, OpenTelemetrySpanExt};
16 use tracing_subscriber::prelude::*;
17
18 #[test]
trace_with_active_otel_context()19 fn trace_with_active_otel_context() {
20 let (cx, subscriber, exporter, provider) = build_sampled_context();
21 let attached = cx.attach();
22
23 tracing::subscriber::with_default(subscriber, || {
24 tracing::debug_span!("child");
25 });
26
27 drop(attached); // end implicit parent
28 drop(provider); // flush all spans
29
30 let spans = exporter.0.lock().unwrap();
31 assert_eq!(spans.len(), 2);
32 assert_shared_attrs_eq(&spans[0].span_context, &spans[1].span_context);
33 }
34
35 #[test]
trace_with_assigned_otel_context()36 fn trace_with_assigned_otel_context() {
37 let (cx, subscriber, exporter, provider) = build_sampled_context();
38
39 tracing::subscriber::with_default(subscriber, || {
40 let child = tracing::debug_span!("child");
41 child.set_parent(cx);
42 });
43
44 drop(provider); // flush all spans
45 let spans = exporter.0.lock().unwrap();
46 assert_eq!(spans.len(), 2);
47 assert_shared_attrs_eq(&spans[0].span_context, &spans[1].span_context);
48 }
49
50 #[test]
trace_root_with_children()51 fn trace_root_with_children() {
52 let (_tracer, provider, exporter, subscriber) = test_tracer();
53
54 tracing::subscriber::with_default(subscriber, || {
55 // Propagate trace information through tracing parent -> child
56 let root = tracing::debug_span!("root");
57 root.in_scope(|| tracing::debug_span!("child"));
58 });
59
60 drop(provider); // flush all spans
61 let spans = exporter.0.lock().unwrap();
62 assert_eq!(spans.len(), 2);
63 assert_shared_attrs_eq(&spans[0].span_context, &spans[1].span_context);
64 }
65
66 #[test]
inject_context_into_outgoing_requests()67 fn inject_context_into_outgoing_requests() {
68 let (_tracer, _provider, _exporter, subscriber) = test_tracer();
69 let propagator = test_propagator();
70 let carrier = test_carrier();
71 let cx = propagator.extract(&carrier);
72 let mut outgoing_req_carrier = HashMap::new();
73
74 tracing::subscriber::with_default(subscriber, || {
75 let root = tracing::debug_span!("root");
76 root.set_parent(cx);
77 let _g = root.enter();
78 let child = tracing::debug_span!("child");
79 propagator.inject_context(&child.context(), &mut outgoing_req_carrier);
80 });
81
82 // Ensure all values that should be passed between services are preserved
83 assert_carrier_attrs_eq(&carrier, &outgoing_req_carrier);
84 }
85
assert_shared_attrs_eq(sc_a: &SpanContext, sc_b: &SpanContext)86 fn assert_shared_attrs_eq(sc_a: &SpanContext, sc_b: &SpanContext) {
87 assert_eq!(sc_a.trace_id(), sc_b.trace_id());
88 assert_eq!(sc_a.trace_state(), sc_b.trace_state());
89 }
90
assert_carrier_attrs_eq( carrier_a: &HashMap<String, String>, carrier_b: &HashMap<String, String>, )91 fn assert_carrier_attrs_eq(
92 carrier_a: &HashMap<String, String>,
93 carrier_b: &HashMap<String, String>,
94 ) {
95 // Match baggage unordered
96 assert_eq!(
97 carrier_a
98 .get("baggage")
99 .map(|b| b.split_terminator(',').collect::<HashSet<_>>()),
100 carrier_b
101 .get("baggage")
102 .map(|b| b.split_terminator(',').collect())
103 );
104 // match trace parent values, except span id
105 assert_eq!(
106 carrier_a.get("traceparent").unwrap()[0..36],
107 carrier_b.get("traceparent").unwrap()[0..36],
108 );
109 // match tracestate values
110 assert_eq!(carrier_a.get("tracestate"), carrier_b.get("tracestate"));
111 }
112
test_tracer() -> (Tracer, TracerProvider, TestExporter, impl Subscriber)113 fn test_tracer() -> (Tracer, TracerProvider, TestExporter, impl Subscriber) {
114 let exporter = TestExporter::default();
115 let provider = TracerProvider::builder()
116 .with_simple_exporter(exporter.clone())
117 .build();
118 let tracer = provider.tracer("test");
119 let subscriber = tracing_subscriber::registry().with(layer().with_tracer(tracer.clone()));
120
121 (tracer, provider, exporter, subscriber)
122 }
123
test_propagator() -> TextMapCompositePropagator124 fn test_propagator() -> TextMapCompositePropagator {
125 let baggage_propagator = BaggagePropagator::new();
126 let trace_context_propagator = TraceContextPropagator::new();
127
128 TextMapCompositePropagator::new(vec![
129 Box::new(baggage_propagator),
130 Box::new(trace_context_propagator),
131 ])
132 }
133
test_carrier() -> HashMap<String, String>134 fn test_carrier() -> HashMap<String, String> {
135 let mut carrier = HashMap::new();
136 carrier.insert(
137 "baggage".to_string(),
138 "key2=value2,key1=value1;property1;property2,key3=value3;propertyKey=propertyValue"
139 .to_string(),
140 );
141 carrier.insert("tracestate".to_string(), "test1=test2".to_string());
142 carrier.insert(
143 "traceparent".to_string(),
144 "00-0af7651916cd43dd8448eb211c80319c-b7ad6b7169203331-01".to_string(),
145 );
146
147 carrier
148 }
149
build_sampled_context() -> (Context, impl Subscriber, TestExporter, TracerProvider)150 fn build_sampled_context() -> (Context, impl Subscriber, TestExporter, TracerProvider) {
151 let (tracer, provider, exporter, subscriber) = test_tracer();
152 let span = tracer.start("sampled");
153 let cx = Context::current_with_span(span);
154
155 (cx, subscriber, exporter, provider)
156 }
157
158 #[derive(Clone, Default, Debug)]
159 struct TestExporter(Arc<Mutex<Vec<SpanData>>>);
160
161 impl SpanExporter for TestExporter {
export(&mut self, mut batch: Vec<SpanData>) -> BoxFuture<'static, ExportResult>162 fn export(&mut self, mut batch: Vec<SpanData>) -> BoxFuture<'static, ExportResult> {
163 let spans = self.0.clone();
164 Box::pin(async move {
165 if let Ok(mut inner) = spans.lock() {
166 inner.append(&mut batch);
167 }
168 Ok(())
169 })
170 }
171 }
172