opentelemetry_sdk/resource/
env.rs1use crate::resource::{Resource, ResourceDetector};
6use opentelemetry::{Key, KeyValue, Value};
7use std::env;
8
9const OTEL_RESOURCE_ATTRIBUTES: &str = "OTEL_RESOURCE_ATTRIBUTES";
10const OTEL_SERVICE_NAME: &str = "OTEL_SERVICE_NAME";
11
12#[derive(Debug)]
17pub struct EnvResourceDetector {
18 _private: (),
19}
20
21impl ResourceDetector for EnvResourceDetector {
22 fn detect(&self) -> Resource {
23 match env::var(OTEL_RESOURCE_ATTRIBUTES) {
24 Ok(s) if !s.is_empty() => construct_otel_resources(s),
25 Ok(_) | Err(_) => Resource::empty(), }
27 }
28}
29
30impl EnvResourceDetector {
31 pub fn new() -> Self {
33 EnvResourceDetector { _private: () }
34 }
35}
36
37impl Default for EnvResourceDetector {
38 fn default() -> Self {
39 EnvResourceDetector::new()
40 }
41}
42
43fn construct_otel_resources(s: String) -> Resource {
46 Resource::builder_empty()
47 .with_attributes(s.split_terminator(',').filter_map(|entry| {
48 let parts = match entry.split_once('=') {
49 Some(p) => p,
50 None => return None,
51 };
52 let key = parts.0.trim();
53 let value = parts.1.trim();
54
55 Some(KeyValue::new(key.to_owned(), value.to_owned()))
56 }))
57 .build()
58}
59
60#[derive(Debug)]
73pub struct SdkProvidedResourceDetector;
74
75fn default_service_name() -> Value {
76 if cfg!(miri) {
79 return "unknown_service".into();
80 }
81
82 env::current_exe()
83 .ok()
84 .and_then(|path| {
85 path.file_name()
86 .and_then(|name| name.to_str())
87 .map(|name| format!("unknown_service:{}", name))
88 })
89 .unwrap_or_else(|| "unknown_service".to_string())
90 .into()
91}
92
93impl ResourceDetector for SdkProvidedResourceDetector {
94 fn detect(&self) -> Resource {
95 Resource::builder_empty()
96 .with_attributes([KeyValue::new(
97 super::SERVICE_NAME,
98 env::var(OTEL_SERVICE_NAME)
99 .ok()
100 .filter(|s| !s.is_empty())
101 .map(Value::from)
102 .or_else(|| {
103 EnvResourceDetector::new()
104 .detect()
105 .get(&Key::new(super::SERVICE_NAME))
106 })
107 .unwrap_or_else(default_service_name),
108 )])
109 .build()
110 }
111}
112
113#[cfg(test)]
114mod tests {
115 use crate::resource::env::{
116 SdkProvidedResourceDetector, OTEL_RESOURCE_ATTRIBUTES, OTEL_SERVICE_NAME,
117 };
118 use crate::resource::{EnvResourceDetector, Resource, ResourceDetector};
119 use opentelemetry::{Key, KeyValue, Value};
120
121 #[test]
122 fn test_read_from_env() {
123 temp_env::with_vars(
124 [
125 (
126 "OTEL_RESOURCE_ATTRIBUTES",
127 Some("key=value, k = v , a= x, a=z,base64=SGVsbG8sIFdvcmxkIQ=="),
128 ),
129 ("IRRELEVANT", Some("20200810")),
130 ],
131 || {
132 let detector = EnvResourceDetector::new();
133 let resource = detector.detect();
134 assert_eq!(
135 resource,
136 Resource::builder_empty()
137 .with_attributes([
138 KeyValue::new("key", "value"),
139 KeyValue::new("k", "v"),
140 KeyValue::new("a", "x"),
141 KeyValue::new("a", "z"),
142 KeyValue::new("base64", "SGVsbG8sIFdvcmxkIQ=="), ])
144 .build()
145 );
146 },
147 );
148
149 let detector = EnvResourceDetector::new();
150 let resource = detector.detect();
151 assert!(resource.is_empty());
152 }
153
154 #[test]
155 fn test_sdk_provided_resource_detector() {
156 let no_env = SdkProvidedResourceDetector.detect();
159 let service_name = no_env
160 .get(&Key::from_static_str(crate::resource::SERVICE_NAME))
161 .map(|v| v.to_string())
162 .unwrap();
163
164 if cfg!(miri) {
165 assert_eq!(service_name, "unknown_service");
166 } else {
167 assert!(
168 service_name.starts_with("unknown_service:opentelemetry_sdk-"),
169 "Expected service name to start with 'unknown_service:opentelemetry_sdk-', got: {}",
170 service_name
171 );
172 }
173
174 temp_env::with_var(OTEL_SERVICE_NAME, Some("test service"), || {
175 let with_service = SdkProvidedResourceDetector.detect();
176 assert_eq!(
177 with_service.get(&Key::from_static_str(crate::resource::SERVICE_NAME)),
178 Some(Value::from("test service")),
179 )
180 });
181
182 temp_env::with_var(
183 OTEL_RESOURCE_ATTRIBUTES,
184 Some("service.name=test service1"),
185 || {
186 let with_service = SdkProvidedResourceDetector.detect();
187 assert_eq!(
188 with_service.get(&Key::from_static_str(crate::resource::SERVICE_NAME)),
189 Some(Value::from("test service1")),
190 )
191 },
192 );
193
194 temp_env::with_vars(
196 [
197 (OTEL_SERVICE_NAME, Some("test service")),
198 (OTEL_RESOURCE_ATTRIBUTES, Some("service.name=test service3")),
199 ],
200 || {
201 let with_service = SdkProvidedResourceDetector.detect();
202 assert_eq!(
203 with_service.get(&Key::from_static_str(crate::resource::SERVICE_NAME)),
204 Some(Value::from("test service"))
205 );
206 },
207 );
208 }
209}