opentelemetry_spanprocessor_any/sdk/resource/
mod.rs1mod env;
26mod os;
27mod process;
28
29pub use env::EnvResourceDetector;
30pub use env::SdkProvidedResourceDetector;
31pub use os::OsResourceDetector;
32pub use process::ProcessResourceDetector;
33
34#[cfg(feature = "metrics")]
35use crate::attributes;
36use crate::{Key, KeyValue, Value};
37#[cfg(feature = "serialize")]
38use serde::{Deserialize, Serialize};
39use std::collections::{btree_map, BTreeMap};
40use std::ops::Deref;
41use std::time::Duration;
42
43#[cfg_attr(feature = "serialize", derive(Deserialize, Serialize))]
47#[derive(Clone, Debug, PartialEq)]
48pub struct Resource {
49 attrs: BTreeMap<Key, Value>,
50}
51
52impl Default for Resource {
53 fn default() -> Self {
54 Self::from_detectors(
55 Duration::from_secs(0),
56 vec![Box::new(EnvResourceDetector::new())],
57 )
58 }
59}
60
61impl Resource {
62 pub fn empty() -> Self {
64 Self {
65 attrs: Default::default(),
66 }
67 }
68
69 pub fn new<T: IntoIterator<Item = KeyValue>>(kvs: T) -> Self {
74 let mut resource = Resource::empty();
75
76 for kv in kvs.into_iter() {
77 resource.attrs.insert(kv.key, kv.value);
78 }
79
80 resource
81 }
82
83 pub fn from_detectors(timeout: Duration, detectors: Vec<Box<dyn ResourceDetector>>) -> Self {
87 let mut resource = Resource::empty();
88 for detector in detectors {
89 let detected_res = detector.detect(timeout);
90 for (key, value) in detected_res.into_iter() {
91 resource.attrs.insert(key, value);
93 }
94 }
95
96 resource
97 }
98
99 pub fn merge<T: Deref<Target = Self>>(&self, other: T) -> Self {
104 if self.attrs.is_empty() {
105 return other.clone();
106 }
107 if other.attrs.is_empty() {
108 return self.clone();
109 }
110
111 let mut resource = Resource::empty();
112
113 for (k, v) in self.attrs.iter() {
115 resource.attrs.insert(k.clone(), v.clone());
116 }
117 for (k, v) in other.attrs.iter() {
118 resource.attrs.insert(k.clone(), v.clone());
119 }
120
121 resource
122 }
123
124 pub fn len(&self) -> usize {
126 self.attrs.len()
127 }
128
129 pub fn is_empty(&self) -> bool {
131 self.attrs.is_empty()
132 }
133
134 pub fn iter(&self) -> Iter<'_> {
136 self.into_iter()
137 }
138
139 pub fn get(&self, key: Key) -> Option<Value> {
141 self.attrs.get(&key).cloned()
142 }
143
144 #[cfg(feature = "metrics")]
146 #[cfg_attr(docsrs, doc(cfg(feature = "metrics")))]
147 pub fn encoded(&self, encoder: &dyn attributes::Encoder) -> String {
148 encoder.encode(&mut self.into_iter())
149 }
150}
151
152#[derive(Debug)]
154pub struct IntoIter(btree_map::IntoIter<Key, Value>);
155
156impl Iterator for IntoIter {
157 type Item = (Key, Value);
158
159 fn next(&mut self) -> Option<Self::Item> {
160 self.0.next()
161 }
162}
163
164impl IntoIterator for Resource {
165 type Item = (Key, Value);
166 type IntoIter = IntoIter;
167
168 fn into_iter(self) -> Self::IntoIter {
169 IntoIter(self.attrs.into_iter())
170 }
171}
172
173#[derive(Debug)]
175pub struct Iter<'a>(btree_map::Iter<'a, Key, Value>);
176
177impl<'a> Iterator for Iter<'a> {
178 type Item = (&'a Key, &'a Value);
179
180 fn next(&mut self) -> Option<Self::Item> {
181 self.0.next()
182 }
183}
184
185impl<'a> IntoIterator for &'a Resource {
186 type Item = (&'a Key, &'a Value);
187 type IntoIter = Iter<'a>;
188
189 fn into_iter(self) -> Self::IntoIter {
190 Iter(self.attrs.iter())
191 }
192}
193
194pub trait ResourceDetector {
199 fn detect(&self, timeout: Duration) -> Resource;
208}
209
210#[cfg(test)]
211mod tests {
212 use super::*;
213 use crate::sdk::resource::EnvResourceDetector;
214 use std::collections::BTreeMap;
215 use std::{env, time};
216
217 #[test]
218 fn new_resource() {
219 let args_with_dupe_keys = vec![KeyValue::new("a", ""), KeyValue::new("a", "final")];
220
221 let mut expected_attrs = BTreeMap::new();
222 expected_attrs.insert(Key::new("a"), Value::from("final"));
223
224 assert_eq!(
225 Resource::new(args_with_dupe_keys),
226 Resource {
227 attrs: expected_attrs
228 }
229 );
230 }
231
232 #[test]
233 fn merge_resource() {
234 let resource_a = Resource::new(vec![
235 KeyValue::new("a", ""),
236 KeyValue::new("b", "b-value"),
237 KeyValue::new("d", "d-value"),
238 ]);
239
240 let resource_b = Resource::new(vec![
241 KeyValue::new("a", "a-value"),
242 KeyValue::new("c", "c-value"),
243 KeyValue::new("d", ""),
244 ]);
245
246 let mut expected_attrs = BTreeMap::new();
247 expected_attrs.insert(Key::new("a"), Value::from("a-value"));
248 expected_attrs.insert(Key::new("b"), Value::from("b-value"));
249 expected_attrs.insert(Key::new("c"), Value::from("c-value"));
250 expected_attrs.insert(Key::new("d"), Value::from(""));
251
252 assert_eq!(
253 resource_a.merge(&resource_b),
254 Resource {
255 attrs: expected_attrs
256 }
257 );
258 }
259
260 #[test]
261 fn detect_resource() {
262 env::set_var("OTEL_RESOURCE_ATTRIBUTES", "key=value, k = v , a= x, a=z");
263 env::set_var("irrelevant".to_uppercase(), "20200810");
264
265 let detector = EnvResourceDetector::new();
266 let resource =
267 Resource::from_detectors(time::Duration::from_secs(5), vec![Box::new(detector)]);
268 assert_eq!(
269 resource,
270 Resource::new(vec![
271 KeyValue::new("key", "value"),
272 KeyValue::new("k", "v"),
273 KeyValue::new("a", "x"),
274 KeyValue::new("a", "z")
275 ])
276 )
277 }
278}