opentelemetry_spanprocessor_any/propagation/mod.rs
1//! # OpenTelemetry Propagator interface
2//!
3//! Propagators API consists of two main formats:
4//!
5//! - `BinaryFormat` is used to serialize and deserialize a value
6//! into a binary representation.
7//! - `TextMapFormat` is used to inject and extract a value as
8//! text into injectors and extractors that travel in-band across process boundaries.
9//!
10//! Deserializing must set `is_remote` to true on the returned
11//! `SpanContext`.
12//!
13//! ## Binary Format
14//!
15//! `BinaryFormat` is a formatter to serialize and deserialize a value
16//! into a binary format.
17//!
18//! `BinaryFormat` MUST expose the APIs that serializes values into bytes,
19//! and deserializes values from bytes.
20//!
21//! ### ToBytes
22//!
23//! Serializes the given value into the on-the-wire representation.
24//!
25//! Required arguments:
26//!
27//! - the value to serialize, can be `SpanContext` or `DistributedContext`.
28//!
29//! Returns the on-the-wire byte representation of the value.
30//!
31//! ### FromBytes
32//!
33//! Creates a value from the given on-the-wire encoded representation.
34//!
35//! If the value could not be parsed, the underlying implementation
36//! SHOULD decide to return ether an empty value, an invalid value, or
37//! a valid value.
38//!
39//! Required arguments:
40//!
41//! - on-the-wire byte representation of the value.
42//!
43//! Returns a value deserialized from bytes.
44//!
45//! ## TextMap Format
46//!
47//! `TextMapFormat` is a formatter that injects and extracts a value
48//! as text into injectors and extractors that travel in-band across process boundaries.
49//!
50//! Encoding is expected to conform to the HTTP Header Field semantics.
51//! Values are often encoded as RPC/HTTP request headers.
52//!
53//! The carrier of propagated data on both the client (injector) and
54//! server (extractor) side is usually a http request. Propagation is
55//! usually implemented via library-specific request interceptors, where
56//! the client-side injects values and the server-side extracts them.
57//!
58//! `TextMapFormat` MUST expose the APIs that injects values into injectors,
59//! and extracts values from extractors.
60//!
61//! ### Fields
62//!
63//! The propagation fields defined. If your injector is reused, you should
64//! delete the fields here before calling `inject`.
65//!
66//! For example, if the injector is a single-use or immutable request object,
67//! you don't need to clear fields as they couldn't have been set before.
68//! If it is a mutable, retryable object, successive calls should clear
69//! these fields first.
70//!
71//! The use cases of this are:
72//!
73//! - allow pre-allocation of fields, especially in systems like gRPC
74//! Metadata
75//! - allow a single-pass over an iterator
76//!
77//! Returns list of fields that will be used by this formatter.
78//!
79//! ### Inject
80//!
81//! Injects the value downstream. For example, as http headers.
82//!
83//! Required arguments:
84//!
85//! - the `SpanContext` to be injected.
86//! - the injector that holds propagation fields. For example, an outgoing
87//! message or http request.
88//! - the `Setter` invoked for each propagation key to add or remove.
89//!
90//! #### Setter argument
91//!
92//! Setter is an argument in `Inject` that puts value into given field.
93//!
94//! `Setter` allows a `TextMapFormat` to set propagated fields into a
95//! injector.
96//!
97//! `Setter` MUST be stateless and allowed to be saved as a constant to
98//! avoid runtime allocations. One of the ways to implement it is `Setter`
99//! class with `Put` method as described below.
100//!
101//! ##### Put
102//!
103//! Replaces a propagated field with the given value.
104//!
105//! Required arguments:
106//!
107//! - the injector holds propagation fields. For example, an outgoing message
108//! or http request.
109//! - the key of the field.
110//! - the value of the field.
111//!
112//! The implementation SHOULD preserve casing (e.g. it should not transform
113//! `Content-Type` to `content-type`) if the used protocol is case insensitive,
114//! otherwise it MUST preserve casing.
115//!
116//! ### Extract
117//!
118//! Extracts the value from upstream. For example, as http headers.
119//!
120//! If the value could not be parsed, the underlying implementation will
121//! decide to return an object representing either an empty value, an invalid
122//! value, or a valid value.
123//!
124//! Required arguments:
125//!
126//! - the extractor holds propagation fields. For example, an outgoing message
127//! or http request.
128//! - the instance of `Getter` invoked for each propagation key to get.
129//!
130//! Returns the non-null extracted value.
131//!
132//! #### Getter argument
133//!
134//! Getter is an argument in `Extract` that get value from given field
135//!
136//! `Getter` allows a `TextMapFormat` to read propagated fields from a
137//! extractor.
138//!
139//! `Getter` MUST be stateless and allowed to be saved as a constant to avoid
140//! runtime allocations. One of the ways to implement it is `Getter` class
141//! with `Get` method as described below.
142//!
143//! ##### Get
144//!
145//! The Get function MUST return the first value of the given propagation
146//! key or return `None` if the key doesn't exist.
147//!
148//! Required arguments:
149//!
150//! - the extractor of propagation fields, such as an HTTP request.
151//! - the key of the field.
152//!
153//! The `get` function is responsible for handling case sensitivity. If
154//! the getter is intended to work with an HTTP request object, the getter
155//! MUST be case insensitive. To improve compatibility with other text-based
156//! protocols, text format implementations MUST ensure to always use the
157//! canonical casing for their attributes. NOTE: Canonical casing for HTTP
158//! headers is usually title case (e.g. `Content-Type` instead of `content-type`).
159//!
160//! ##### Keys
161//!
162//! The Keys function returns a vector of the propagation keys.
163//!
164use std::collections::HashMap;
165
166pub mod text_map_propagator;
167
168pub use text_map_propagator::TextMapPropagator;
169
170/// Injector provides an interface for adding fields from an underlying struct like `HashMap`
171pub trait Injector {
172 /// Add a key and value to the underlying data.
173 fn set(&mut self, key: &str, value: String);
174}
175
176/// Extractor provides an interface for removing fields from an underlying struct like `HashMap`
177pub trait Extractor {
178 /// Get a value from a key from the underlying data.
179 fn get(&self, key: &str) -> Option<&str>;
180
181 /// Collect all the keys from the underlying data.
182 fn keys(&self) -> Vec<&str>;
183}
184
185impl<S: std::hash::BuildHasher> Injector for HashMap<String, String, S> {
186 /// Set a key and value in the HashMap.
187 fn set(&mut self, key: &str, value: String) {
188 self.insert(key.to_lowercase(), value);
189 }
190}
191
192impl<S: std::hash::BuildHasher> Extractor for HashMap<String, String, S> {
193 /// Get a value for a key from the HashMap.
194 fn get(&self, key: &str) -> Option<&str> {
195 self.get(&key.to_lowercase()).map(|v| v.as_str())
196 }
197
198 /// Collect all the keys from the HashMap.
199 fn keys(&self) -> Vec<&str> {
200 self.keys().map(|k| k.as_str()).collect::<Vec<_>>()
201 }
202}
203
204#[cfg(test)]
205mod tests {
206 use super::*;
207 use std::collections::HashMap;
208
209 #[test]
210 fn hash_map_get() {
211 let mut carrier = HashMap::new();
212 carrier.set("headerName", "value".to_string());
213
214 assert_eq!(
215 Extractor::get(&carrier, "HEADERNAME"),
216 Some("value"),
217 "case insensitive extraction"
218 );
219 }
220
221 #[test]
222 fn hash_map_keys() {
223 let mut carrier = HashMap::new();
224 carrier.set("headerName1", "value1".to_string());
225 carrier.set("headerName2", "value2".to_string());
226
227 let got = Extractor::keys(&carrier);
228 assert_eq!(got.len(), 2);
229 assert!(got.contains(&"headername1"));
230 assert!(got.contains(&"headername2"));
231 }
232}