telemetry_rust/http.rs
1//! HTTP utilities for OpenTelemetry context propagation through headers.
2
3// Originally retired from davidB/tracing-opentelemetry-instrumentation-sdk
4// which is licensed under CC0 1.0 Universal
5// https://github.com/davidB/tracing-opentelemetry-instrumentation-sdk/blob/d3609ac2cc699d3a24fbf89754053cc8e938e3bf/LICENSE
6
7use opentelemetry::Context;
8use opentelemetry::propagation::{Extractor, Injector};
9use tracing_opentelemetry_instrumentation_sdk as otel;
10
11/// HTTP header injector for OpenTelemetry context propagation.
12///
13/// This struct implements the [`Injector`] trait to inject OpenTelemetry trace context
14/// into HTTP headers. It wraps an HTTP header map and provides the necessary interface
15/// for propagators to inject trace context information.
16///
17/// # Usage
18///
19/// Typically used internally by propagation functions, but can be used directly:
20///
21/// ```rust
22/// use http::HeaderMap;
23/// use telemetry_rust::http::HeaderInjector;
24///
25/// let mut headers = HeaderMap::new();
26/// let mut injector = HeaderInjector(&mut headers);
27/// // Use with OpenTelemetry propagators...
28/// ```
29pub struct HeaderInjector<'a>(pub &'a mut http::HeaderMap);
30
31impl Injector for HeaderInjector<'_> {
32 /// Set a key and value in the `HeaderMap`. Does nothing if the key or value are not valid inputs.
33 fn set(&mut self, key: &str, value: String) {
34 if let Ok(name) = http::header::HeaderName::from_bytes(key.as_bytes()) {
35 if let Ok(val) = http::header::HeaderValue::from_str(&value) {
36 self.0.insert(name, val);
37 }
38 }
39 }
40}
41
42/// HTTP header extractor for OpenTelemetry context propagation.
43///
44/// This struct implements the [`Extractor`] trait to extract OpenTelemetry trace context
45/// from HTTP headers. It wraps an HTTP header map and provides the necessary interface
46/// for propagators to extract trace context information from incoming requests.
47///
48/// # Usage
49///
50/// Typically used internally by propagation functions, but can be used directly:
51///
52/// ```rust
53/// use http::HeaderMap;
54/// use telemetry_rust::http::HeaderExtractor;
55///
56/// let headers = HeaderMap::new();
57/// let extractor = HeaderExtractor(&headers);
58/// // Use with OpenTelemetry propagators...
59/// ```
60pub struct HeaderExtractor<'a>(pub &'a http::HeaderMap);
61
62impl Extractor for HeaderExtractor<'_> {
63 /// Get a value for a key from the `HeaderMap`. If the value is not valid ASCII, returns None.
64 fn get(&self, key: &str) -> Option<&str> {
65 self.0.get(key).and_then(|value| value.to_str().ok())
66 }
67
68 /// Collect all the keys from the `HeaderMap`.
69 fn keys(&self) -> Vec<&str> {
70 self.0
71 .keys()
72 .map(http::HeaderName::as_str)
73 .collect::<Vec<_>>()
74 }
75}
76
77/// Injects OpenTelemetry context from a specific context into HTTP headers.
78///
79/// This function takes an existing OpenTelemetry context and injects its trace
80/// information into the provided HTTP headers using the globally configured
81/// text map propagator.
82///
83/// # Arguments
84///
85/// - `context`: The OpenTelemetry context to inject
86/// - `headers`: Mutable reference to HTTP headers where context will be injected
87///
88/// # Examples
89///
90/// ```rust
91/// use http::HeaderMap;
92/// use opentelemetry::Context;
93/// use telemetry_rust::http::inject_context_on_context;
94///
95/// let context = Context::current();
96/// let mut headers = HeaderMap::new();
97/// inject_context_on_context(&context, &mut headers);
98/// ```
99pub fn inject_context_on_context(context: &Context, headers: &mut http::HeaderMap) {
100 let mut injector = HeaderInjector(headers);
101 opentelemetry::global::get_text_map_propagator(|propagator| {
102 propagator.inject_context(context, &mut injector);
103 });
104}
105
106/// Injects the current OpenTelemetry context into HTTP headers.
107///
108/// This convenience function automatically finds the current OpenTelemetry context
109/// and injects its trace information into the provided HTTP headers using the
110/// globally configured text map propagator.
111///
112/// # Arguments
113///
114/// - `headers`: Mutable reference to HTTP headers where context will be injected
115///
116/// # Examples
117///
118/// ```rust
119/// use http::HeaderMap;
120/// use telemetry_rust::http::inject_context;
121///
122/// let mut headers = HeaderMap::new();
123/// inject_context(&mut headers);
124/// ```
125pub fn inject_context(headers: &mut http::HeaderMap) {
126 let mut injector = HeaderInjector(headers);
127 opentelemetry::global::get_text_map_propagator(|propagator| {
128 propagator.inject_context(&otel::find_current_context(), &mut injector);
129 });
130}
131
132/// Extracts OpenTelemetry context from HTTP headers.
133///
134/// This function extracts trace context information from HTTP headers using the
135/// globally configured text map propagator. If no trace context is found in the
136/// headers, it returns an unsampled context.
137///
138/// # Arguments
139///
140/// - `headers`: Reference to HTTP headers to extract context from
141///
142/// # Returns
143///
144/// An OpenTelemetry [`Context`] containing the extracted trace information, or
145/// an unsampled context if no trace data was found.
146///
147/// # Examples
148///
149/// ```rust
150/// use http::HeaderMap;
151/// use telemetry_rust::http::extract_context;
152///
153/// let headers = HeaderMap::new();
154/// let context = extract_context(&headers);
155/// ```
156// If remote request has no span data the propagator defaults to an unsampled context
157#[must_use]
158pub fn extract_context(headers: &http::HeaderMap) -> Context {
159 let extractor = HeaderExtractor(headers);
160 opentelemetry::global::get_text_map_propagator(|propagator| {
161 propagator.extract(&extractor)
162 })
163}