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}