opentelemetry/propagation/
mod.rs

1//! # OpenTelemetry Propagator interface
2//! Cross-cutting concerns send their state to the next process using Propagators, which are defined
3//! as objects used to read and write context data to and from messages exchanged by the applications.
4//!
5//! `Propagator`s leverage the [`Context`] to inject and extract data for each cross-cutting concern,
6//! such as `TraceContext` and [`Baggage`].
7//!
8//! The Propagators API is expected to be leveraged by users writing instrumentation libraries.
9//!
10//! Currently, the following `Propagator` types are supported:
11//! -  [`TextMapPropagator`], inject values into and extracts values from carriers as string key/value pairs
12//!
13//! A binary Propagator type will be added in
14//! the future, See [tracking issues](https://github.com/open-telemetry/opentelemetry-specification/issues/437)).
15//!
16//! `Propagator`s uses [`Injector`] and [`Extractor`] to read and write context data to and from messages.
17//! Each specific Propagator type defines its expected carrier type, such as a string map or a byte array.
18//!
19//! [`Baggage`]: crate::baggage::Baggage
20//! [`Context`]: crate::Context
21
22use std::collections::HashMap;
23use thiserror::Error;
24
25pub mod composite;
26pub mod text_map_propagator;
27
28pub use composite::TextMapCompositePropagator;
29pub use text_map_propagator::TextMapPropagator;
30
31/// Injector provides an interface for adding fields from an underlying struct like `HashMap`
32pub trait Injector {
33    /// Add a key and value to the underlying data.
34    fn set(&mut self, key: &str, value: String);
35}
36
37/// Extractor provides an interface for removing fields from an underlying struct like `HashMap`
38pub trait Extractor {
39    /// Get a value from a key from the underlying data.
40    fn get(&self, key: &str) -> Option<&str>;
41
42    /// Collect all the keys from the underlying data.
43    fn keys(&self) -> Vec<&str>;
44}
45
46impl<S: std::hash::BuildHasher> Injector for HashMap<String, String, S> {
47    /// Set a key and value in the HashMap.
48    fn set(&mut self, key: &str, value: String) {
49        self.insert(key.to_lowercase(), value);
50    }
51}
52
53impl<S: std::hash::BuildHasher> Extractor for HashMap<String, String, S> {
54    /// Get a value for a key from the HashMap.
55    fn get(&self, key: &str) -> Option<&str> {
56        self.get(&key.to_lowercase()).map(|v| v.as_str())
57    }
58
59    /// Collect all the keys from the HashMap.
60    fn keys(&self) -> Vec<&str> {
61        self.keys().map(|k| k.as_str()).collect::<Vec<_>>()
62    }
63}
64
65/// Error when extracting or injecting context data(i.e propagating) across application boundaries.
66#[derive(Error, Debug)]
67#[error("Cannot {} from {}, {}", ops, message, propagator_name)]
68pub struct PropagationError {
69    message: &'static str,
70    // which propagator does this error comes from
71    propagator_name: &'static str,
72    // are we extracting or injecting information across application boundaries
73    ops: &'static str,
74}
75
76impl PropagationError {
77    /// Error happens when extracting information
78    pub fn extract(message: &'static str, propagator_name: &'static str) -> Self {
79        PropagationError {
80            message,
81            propagator_name,
82            ops: "extract",
83        }
84    }
85
86    /// Error happens when extracting information
87    pub fn inject(message: &'static str, propagator_name: &'static str) -> Self {
88        PropagationError {
89            message,
90            propagator_name,
91            ops: "inject",
92        }
93    }
94}
95
96#[cfg(test)]
97mod tests {
98    use super::*;
99
100    #[test]
101    fn hash_map_get() {
102        let mut carrier = HashMap::new();
103        carrier.set("headerName", "value".to_string());
104
105        assert_eq!(
106            Extractor::get(&carrier, "HEADERNAME"),
107            Some("value"),
108            "case insensitive extraction"
109        );
110    }
111
112    #[test]
113    fn hash_map_keys() {
114        let mut carrier = HashMap::new();
115        carrier.set("headerName1", "value1".to_string());
116        carrier.set("headerName2", "value2".to_string());
117
118        let got = Extractor::keys(&carrier);
119        assert_eq!(got.len(), 2);
120        assert!(got.contains(&"headername1"));
121        assert!(got.contains(&"headername2"));
122    }
123}