1use reqwest::header::{HeaderName, HeaderValue};
2use reqwest::Request;
3use std::str::FromStr;
4use tracing::Span;
5
6pub fn inject_opentelemetry_context_into_request(mut request: Request) -> Request {
8 #[cfg(feature = "opentelemetry_0_20")]
9 opentelemetry_0_20_pkg::global::get_text_map_propagator(|injector| {
10 use tracing_opentelemetry_0_21_pkg::OpenTelemetrySpanExt;
11 let context = Span::current().context();
12 injector.inject_context(&context, &mut RequestCarrier::new(&mut request))
13 });
14
15 #[cfg(feature = "opentelemetry_0_21")]
16 opentelemetry_0_21_pkg::global::get_text_map_propagator(|injector| {
17 use tracing_opentelemetry_0_22_pkg::OpenTelemetrySpanExt;
18 let context = Span::current().context();
19 injector.inject_context(&context, &mut RequestCarrier::new(&mut request))
20 });
21
22 #[cfg(feature = "opentelemetry_0_22")]
23 opentelemetry_0_22_pkg::global::get_text_map_propagator(|injector| {
24 use tracing_opentelemetry_0_23_pkg::OpenTelemetrySpanExt;
25 let context = Span::current().context();
26 injector.inject_context(&context, &mut RequestCarrier::new(&mut request))
27 });
28
29 #[cfg(feature = "opentelemetry_0_23")]
30 opentelemetry_0_23_pkg::global::get_text_map_propagator(|injector| {
31 use tracing_opentelemetry_0_24_pkg::OpenTelemetrySpanExt;
32 let context = Span::current().context();
33 injector.inject_context(&context, &mut RequestCarrier::new(&mut request))
34 });
35
36 #[cfg(feature = "opentelemetry_0_24")]
37 opentelemetry_0_24_pkg::global::get_text_map_propagator(|injector| {
38 use tracing_opentelemetry_0_25_pkg::OpenTelemetrySpanExt;
39 let context = Span::current().context();
40 injector.inject_context(&context, &mut RequestCarrier::new(&mut request))
41 });
42
43 #[cfg(feature = "opentelemetry_0_25")]
44 opentelemetry_0_25_pkg::global::get_text_map_propagator(|injector| {
45 use tracing_opentelemetry_0_26_pkg::OpenTelemetrySpanExt;
46 let context = Span::current().context();
47 injector.inject_context(&context, &mut RequestCarrier::new(&mut request))
48 });
49
50 #[cfg(feature = "opentelemetry_0_26")]
51 opentelemetry_0_26_pkg::global::get_text_map_propagator(|injector| {
52 use tracing_opentelemetry_0_27_pkg::OpenTelemetrySpanExt;
53 let context = Span::current().context();
54 injector.inject_context(&context, &mut RequestCarrier::new(&mut request))
55 });
56
57 #[cfg(feature = "opentelemetry_0_27")]
58 opentelemetry_0_27_pkg::global::get_text_map_propagator(|injector| {
59 use tracing_opentelemetry_0_28_pkg::OpenTelemetrySpanExt;
60 let context = Span::current().context();
61 injector.inject_context(&context, &mut RequestCarrier::new(&mut request))
62 });
63
64 #[cfg(feature = "opentelemetry_0_28")]
65 opentelemetry_0_28_pkg::global::get_text_map_propagator(|injector| {
66 use tracing_opentelemetry_0_29_pkg::OpenTelemetrySpanExt;
67 let context = Span::current().context();
68 injector.inject_context(&context, &mut RequestCarrier::new(&mut request))
69 });
70
71 request
72}
73
74struct RequestCarrier<'a> {
81 request: &'a mut Request,
82}
83
84impl<'a> RequestCarrier<'a> {
85 pub fn new(request: &'a mut Request) -> Self {
86 RequestCarrier { request }
87 }
88}
89
90impl RequestCarrier<'_> {
91 fn set_inner(&mut self, key: &str, value: String) {
92 let header_name = HeaderName::from_str(key).expect("Must be header name");
93 let header_value = HeaderValue::from_str(&value).expect("Must be a header value");
94 self.request.headers_mut().insert(header_name, header_value);
95 }
96}
97
98#[cfg(feature = "opentelemetry_0_20")]
99impl opentelemetry_0_20_pkg::propagation::Injector for RequestCarrier<'_> {
100 fn set(&mut self, key: &str, value: String) {
101 self.set_inner(key, value)
102 }
103}
104
105#[cfg(feature = "opentelemetry_0_21")]
106impl opentelemetry_0_21_pkg::propagation::Injector for RequestCarrier<'_> {
107 fn set(&mut self, key: &str, value: String) {
108 self.set_inner(key, value)
109 }
110}
111
112#[cfg(feature = "opentelemetry_0_22")]
113impl opentelemetry_0_22_pkg::propagation::Injector for RequestCarrier<'_> {
114 fn set(&mut self, key: &str, value: String) {
115 self.set_inner(key, value)
116 }
117}
118
119#[cfg(feature = "opentelemetry_0_23")]
120impl opentelemetry_0_23_pkg::propagation::Injector for RequestCarrier<'_> {
121 fn set(&mut self, key: &str, value: String) {
122 self.set_inner(key, value)
123 }
124}
125
126#[cfg(feature = "opentelemetry_0_24")]
127impl opentelemetry_0_24_pkg::propagation::Injector for RequestCarrier<'_> {
128 fn set(&mut self, key: &str, value: String) {
129 self.set_inner(key, value)
130 }
131}
132
133#[cfg(feature = "opentelemetry_0_25")]
134impl opentelemetry_0_25_pkg::propagation::Injector for RequestCarrier<'_> {
135 fn set(&mut self, key: &str, value: String) {
136 self.set_inner(key, value)
137 }
138}
139
140#[cfg(feature = "opentelemetry_0_26")]
141impl opentelemetry_0_26_pkg::propagation::Injector for RequestCarrier<'_> {
142 fn set(&mut self, key: &str, value: String) {
143 self.set_inner(key, value)
144 }
145}
146
147#[cfg(feature = "opentelemetry_0_27")]
148impl opentelemetry_0_27_pkg::propagation::Injector for RequestCarrier<'_> {
149 fn set(&mut self, key: &str, value: String) {
150 self.set_inner(key, value)
151 }
152}
153
154#[cfg(feature = "opentelemetry_0_28")]
155impl opentelemetry_0_28_pkg::propagation::Injector for RequestCarrier<'_> {
156 fn set(&mut self, key: &str, value: String) {
157 self.set_inner(key, value)
158 }
159}
160
161#[cfg(test)]
162mod test {
163 use std::sync::OnceLock;
164
165 use crate::{DisableOtelPropagation, TracingMiddleware};
166 use reqwest::Response;
167 use reqwest_middleware::{ClientBuilder, ClientWithMiddleware, Extension};
168 use tracing::{info_span, Instrument, Level};
169
170 use tracing_subscriber::{filter, layer::SubscriberExt, Registry};
171 use wiremock::{matchers::any, Mock, MockServer, ResponseTemplate};
172
173 async fn make_echo_request_in_otel_context(client: ClientWithMiddleware) -> Response {
174 static TELEMETRY: OnceLock<()> = OnceLock::new();
175
176 TELEMETRY.get_or_init(|| {
177 let subscriber = Registry::default().with(
178 filter::Targets::new().with_target("reqwest_tracing::otel::test", Level::DEBUG),
179 );
180
181 #[cfg(feature = "opentelemetry_0_20")]
182 let subscriber = {
183 use opentelemetry_0_20_pkg::trace::TracerProvider;
184 use opentelemetry_stdout_0_1::SpanExporterBuilder;
185
186 let exporter = SpanExporterBuilder::default()
187 .with_writer(std::io::sink())
188 .build();
189
190 let provider = opentelemetry_0_20_pkg::sdk::trace::TracerProvider::builder()
191 .with_simple_exporter(exporter)
192 .build();
193
194 let tracer = provider.versioned_tracer("reqwest", None::<&str>, None::<&str>, None);
195 let _ = opentelemetry_0_20_pkg::global::set_tracer_provider(provider);
196 opentelemetry_0_20_pkg::global::set_text_map_propagator(
197 opentelemetry_0_20_pkg::sdk::propagation::TraceContextPropagator::new(),
198 );
199
200 let telemetry = tracing_opentelemetry_0_21_pkg::layer().with_tracer(tracer);
201 subscriber.with(telemetry)
202 };
203
204 #[cfg(feature = "opentelemetry_0_21")]
205 let subscriber = {
206 use opentelemetry_0_21_pkg::trace::TracerProvider;
207 use opentelemetry_stdout_0_2::SpanExporterBuilder;
208
209 let exporter = SpanExporterBuilder::default()
210 .with_writer(std::io::sink())
211 .build();
212
213 let provider = opentelemetry_sdk_0_21::trace::TracerProvider::builder()
214 .with_simple_exporter(exporter)
215 .build();
216
217 let tracer = provider.versioned_tracer("reqwest", None::<&str>, None::<&str>, None);
218 let _ = opentelemetry_0_21_pkg::global::set_tracer_provider(provider);
219 opentelemetry_0_21_pkg::global::set_text_map_propagator(
220 opentelemetry_sdk_0_21::propagation::TraceContextPropagator::new(),
221 );
222
223 let telemetry = tracing_opentelemetry_0_22_pkg::layer().with_tracer(tracer);
224 subscriber.with(telemetry)
225 };
226
227 #[cfg(feature = "opentelemetry_0_22")]
228 let subscriber = {
229 use opentelemetry_0_22_pkg::trace::TracerProvider;
230 use opentelemetry_stdout_0_3::SpanExporterBuilder;
231
232 let exporter = SpanExporterBuilder::default()
233 .with_writer(std::io::sink())
234 .build();
235
236 let provider = opentelemetry_sdk_0_22::trace::TracerProvider::builder()
237 .with_simple_exporter(exporter)
238 .build();
239
240 let tracer = provider.versioned_tracer("reqwest", None::<&str>, None::<&str>, None);
241 let _ = opentelemetry_0_22_pkg::global::set_tracer_provider(provider);
242 opentelemetry_0_22_pkg::global::set_text_map_propagator(
243 opentelemetry_sdk_0_22::propagation::TraceContextPropagator::new(),
244 );
245
246 let telemetry = tracing_opentelemetry_0_23_pkg::layer().with_tracer(tracer);
247 subscriber.with(telemetry)
248 };
249
250 #[cfg(feature = "opentelemetry_0_23")]
251 let subscriber = {
252 use opentelemetry_0_23_pkg::trace::TracerProvider;
253 use opentelemetry_stdout_0_4::SpanExporterBuilder;
254
255 let exporter = SpanExporterBuilder::default()
256 .with_writer(std::io::sink())
257 .build();
258
259 let provider = opentelemetry_sdk_0_23::trace::TracerProvider::builder()
260 .with_simple_exporter(exporter)
261 .build();
262
263 let tracer = provider.tracer_builder("reqwest").build();
264 let _ = opentelemetry_0_23_pkg::global::set_tracer_provider(provider);
265 opentelemetry_0_23_pkg::global::set_text_map_propagator(
266 opentelemetry_sdk_0_23::propagation::TraceContextPropagator::new(),
267 );
268
269 let telemetry = tracing_opentelemetry_0_24_pkg::layer().with_tracer(tracer);
270 subscriber.with(telemetry)
271 };
272
273 #[cfg(feature = "opentelemetry_0_24")]
274 let subscriber = {
275 use opentelemetry_0_24_pkg::trace::TracerProvider;
276 use opentelemetry_stdout_0_5::SpanExporterBuilder;
277
278 let exporter = SpanExporterBuilder::default()
279 .with_writer(std::io::sink())
280 .build();
281
282 let provider = opentelemetry_sdk_0_24::trace::TracerProvider::builder()
283 .with_simple_exporter(exporter)
284 .build();
285
286 let tracer = provider.tracer_builder("reqwest").build();
287 let _ = opentelemetry_0_24_pkg::global::set_tracer_provider(provider);
288 opentelemetry_0_24_pkg::global::set_text_map_propagator(
289 opentelemetry_sdk_0_24::propagation::TraceContextPropagator::new(),
290 );
291
292 let telemetry = tracing_opentelemetry_0_25_pkg::layer().with_tracer(tracer);
293 subscriber.with(telemetry)
294 };
295
296 #[cfg(feature = "opentelemetry_0_25")]
297 let subscriber = {
298 use opentelemetry_0_25_pkg::trace::TracerProvider;
299
300 let provider = opentelemetry_sdk_0_25::trace::TracerProvider::builder().build();
301
302 let tracer = provider.tracer_builder("reqwest").build();
303 let _ = opentelemetry_0_25_pkg::global::set_tracer_provider(provider);
304 opentelemetry_0_25_pkg::global::set_text_map_propagator(
305 opentelemetry_sdk_0_25::propagation::TraceContextPropagator::new(),
306 );
307
308 let telemetry = tracing_opentelemetry_0_26_pkg::layer().with_tracer(tracer);
309 subscriber.with(telemetry)
310 };
311
312 #[cfg(feature = "opentelemetry_0_26")]
313 let subscriber = {
314 use opentelemetry_0_26_pkg::trace::TracerProvider;
315
316 let provider = opentelemetry_sdk_0_26::trace::TracerProvider::builder().build();
317
318 let tracer = provider.tracer_builder("reqwest").build();
319 let _ = opentelemetry_0_26_pkg::global::set_tracer_provider(provider);
320 opentelemetry_0_26_pkg::global::set_text_map_propagator(
321 opentelemetry_sdk_0_26::propagation::TraceContextPropagator::new(),
322 );
323
324 let telemetry = tracing_opentelemetry_0_27_pkg::layer().with_tracer(tracer);
325 subscriber.with(telemetry)
326 };
327
328 #[cfg(feature = "opentelemetry_0_27")]
329 let subscriber = {
330 use opentelemetry_0_27_pkg::trace::TracerProvider;
331
332 let provider = opentelemetry_sdk_0_27::trace::TracerProvider::builder().build();
333
334 let tracer = provider.tracer("reqwest");
335 let _ = opentelemetry_0_27_pkg::global::set_tracer_provider(provider);
336 opentelemetry_0_27_pkg::global::set_text_map_propagator(
337 opentelemetry_sdk_0_27::propagation::TraceContextPropagator::new(),
338 );
339
340 let telemetry = tracing_opentelemetry_0_28_pkg::layer().with_tracer(tracer);
341 subscriber.with(telemetry)
342 };
343
344 #[cfg(feature = "opentelemetry_0_28")]
345 let subscriber = {
346 use opentelemetry_0_28_pkg::trace::TracerProvider;
347
348 let provider = opentelemetry_sdk_0_28::trace::SdkTracerProvider::builder().build();
349
350 let tracer = provider.tracer("reqwest");
351 let _ = opentelemetry_0_28_pkg::global::set_tracer_provider(provider);
352 opentelemetry_0_28_pkg::global::set_text_map_propagator(
353 opentelemetry_sdk_0_28::propagation::TraceContextPropagator::new(),
354 );
355
356 let telemetry = tracing_opentelemetry_0_29_pkg::layer().with_tracer(tracer);
357 subscriber.with(telemetry)
358 };
359
360 tracing::subscriber::set_global_default(subscriber).unwrap();
361 });
362
363 let server = MockServer::start().await;
365 Mock::given(any())
366 .respond_with(|req: &wiremock::Request| {
367 req.headers
368 .iter()
369 .fold(ResponseTemplate::new(200), |resp, (k, v)| {
370 resp.append_header(k.clone(), v.clone())
371 })
372 })
373 .mount(&server)
374 .await;
375
376 client
377 .get(server.uri())
378 .send()
379 .instrument(info_span!("some_span"))
380 .await
381 .unwrap()
382 }
383
384 #[tokio::test]
385 async fn tracing_middleware_propagates_otel_data_even_when_the_span_is_disabled() {
386 let client = ClientBuilder::new(reqwest::Client::new())
387 .with(TracingMiddleware::default())
388 .build();
389
390 let resp = make_echo_request_in_otel_context(client).await;
391
392 assert!(
393 resp.headers().contains_key("traceparent"),
394 "by default, the tracing middleware will propagate otel contexts"
395 );
396 }
397
398 #[tokio::test]
399 async fn context_no_propagated() {
400 let client = ClientBuilder::new(reqwest::Client::new())
401 .with_init(Extension(DisableOtelPropagation))
402 .with(TracingMiddleware::default())
403 .build();
404
405 let resp = make_echo_request_in_otel_context(client).await;
406
407 assert!(
408 !resp.headers().contains_key("traceparent"),
409 "request should not contain traceparent if context propagation is disabled"
410 );
411 }
412}