• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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