opentelemetry_spanprocessor_any/sdk/trace/
provider.rs1use crate::sdk::resource::{EnvResourceDetector, SdkProvidedResourceDetector};
12use crate::sdk::trace::runtime::TraceRuntime;
13use crate::sdk::Resource;
14use crate::trace::TraceResult;
15use crate::{
16 global,
17 sdk::{self, export::trace::SpanExporter, trace::SpanProcessor},
18};
19use std::borrow::Cow;
20use std::sync::Arc;
21use std::time::Duration;
22
23const DEFAULT_COMPONENT_NAME: &str = "rust.opentelemetry.io/sdk/tracer";
25
26#[derive(Debug)]
28pub(crate) struct TracerProviderInner {
29 processors: Vec<Box<dyn SpanProcessor>>,
30 config: sdk::trace::Config,
31}
32
33impl Drop for TracerProviderInner {
34 fn drop(&mut self) {
35 for processor in &mut self.processors {
36 if let Err(err) = processor.shutdown() {
37 global::handle_error(err);
38 }
39 }
40 }
41}
42
43#[derive(Clone, Debug)]
45pub struct TracerProvider {
46 inner: Arc<TracerProviderInner>,
47}
48
49impl Default for TracerProvider {
50 fn default() -> Self {
51 TracerProvider::builder().build()
52 }
53}
54
55impl TracerProvider {
56 pub(crate) fn new(inner: Arc<TracerProviderInner>) -> Self {
58 TracerProvider { inner }
59 }
60
61 pub fn builder() -> Builder {
63 Builder::default()
64 }
65
66 pub fn span_processors(&self) -> &Vec<Box<dyn SpanProcessor>> {
68 &self.inner.processors
69 }
70
71 pub fn config(&self) -> &sdk::trace::Config {
73 &self.inner.config
74 }
75}
76
77impl crate::trace::TracerProvider for TracerProvider {
78 type Tracer = sdk::trace::Tracer;
80
81 fn versioned_tracer(
83 &self,
84 name: impl Into<Cow<'static, str>>,
85 version: Option<&'static str>,
86 _schema_url: Option<&'static str>,
87 ) -> Self::Tracer {
88 let name = name.into();
89 let component_name = if name.is_empty() {
91 Cow::Borrowed(DEFAULT_COMPONENT_NAME)
92 } else {
93 name
94 };
95 let instrumentation_lib =
96 sdk::InstrumentationLibrary::new(component_name, version.map(Into::into));
97
98 sdk::trace::Tracer::new(instrumentation_lib, Arc::downgrade(&self.inner))
99 }
100
101 fn force_flush(&self) -> Vec<TraceResult<()>> {
103 self.span_processors()
104 .iter()
105 .map(|processor| processor.force_flush())
106 .collect()
107 }
108}
109
110#[derive(Debug)]
112pub struct Builder {
113 processors: Vec<Box<dyn SpanProcessor>>,
114 config: sdk::trace::Config,
115 sdk_provided_resource: Resource,
116}
117
118impl Default for Builder {
119 fn default() -> Self {
120 Builder {
121 processors: Default::default(),
122 config: Default::default(),
123 sdk_provided_resource: Resource::from_detectors(
124 Duration::from_secs(0),
125 vec![
126 Box::new(SdkProvidedResourceDetector),
127 Box::new(EnvResourceDetector::new()),
128 ],
129 ),
130 }
131 }
132}
133
134impl Builder {
135 pub fn with_simple_exporter<T: SpanExporter + 'static>(self, exporter: T) -> Self {
137 let mut processors = self.processors;
138 processors.push(Box::new(sdk::trace::SimpleSpanProcessor::new(Box::new(
139 exporter,
140 ))));
141
142 Builder { processors, ..self }
143 }
144
145 pub fn with_batch_exporter<T: SpanExporter + 'static, R: TraceRuntime>(
147 self,
148 exporter: T,
149 runtime: R,
150 ) -> Self {
151 let batch = sdk::trace::BatchSpanProcessor::builder(exporter, runtime).build();
152 self.with_span_processor(batch)
153 }
154
155 pub fn with_span_processor<T: SpanProcessor + 'static>(self, processor: T) -> Self {
157 let mut processors = self.processors;
158 processors.push(Box::new(processor));
159
160 Builder { processors, ..self }
161 }
162
163 pub fn with_config(self, config: sdk::trace::Config) -> Self {
165 Builder { config, ..self }
166 }
167
168 pub fn sdk_provided_resource(&self) -> Resource {
173 self.sdk_provided_resource.clone()
174 }
175
176 pub fn build(self) -> TracerProvider {
178 let mut config = self.config;
179 config.resource = match config.resource {
180 None => Some(Arc::new(self.sdk_provided_resource)),
181 Some(resource) => {
183 if resource.is_empty() {
184 None
185 } else {
186 Some(Arc::new(self.sdk_provided_resource.merge(resource)))
187 }
188 }
189 };
190 TracerProvider {
191 inner: Arc::new(TracerProviderInner {
192 processors: self.processors,
193 config,
194 }),
195 }
196 }
197}
198
199#[cfg(test)]
200mod tests {
201 use crate::sdk::export::trace::SpanData;
202 use crate::sdk::trace::provider::TracerProviderInner;
203 use crate::sdk::trace::{Config, Span, SpanProcessor};
204 use crate::sdk::Resource;
205 use crate::trace::{TraceError, TraceResult, TracerProvider};
206 use crate::{Context, Key, KeyValue};
207 use std::env;
208 use std::sync::Arc;
209
210 #[derive(Debug)]
211 struct TestSpanProcessor {
212 success: bool,
213 }
214
215 impl SpanProcessor for TestSpanProcessor {
216 fn on_start(&self, _span: &mut Span, _cx: &Context) {
217 unimplemented!()
218 }
219
220 fn on_end(&self, _span: SpanData) {
221 unimplemented!()
222 }
223
224 fn force_flush(&self) -> TraceResult<()> {
225 if self.success {
226 Ok(())
227 } else {
228 Err(TraceError::from("cannot export"))
229 }
230 }
231
232 fn shutdown(&mut self) -> TraceResult<()> {
233 self.force_flush()
234 }
235 }
236
237 #[test]
238 fn test_force_flush() {
239 let tracer_provider = super::TracerProvider::new(Arc::from(TracerProviderInner {
240 processors: vec![
241 Box::from(TestSpanProcessor { success: true }),
242 Box::from(TestSpanProcessor { success: false }),
243 ],
244 config: Default::default(),
245 }));
246
247 let results = tracer_provider.force_flush();
248 assert_eq!(results.len(), 2);
249 }
250
251 #[test]
252 fn test_tracer_provider_default_resource() {
253 let assert_service_name = |provider: super::TracerProvider,
255 expect: Option<&'static str>| {
256 assert_eq!(
257 provider.config().resource.as_ref().and_then(|r| r
258 .get(Key::from_static_str("service.name"))
259 .map(|v| v.to_string())),
260 expect.map(|s| s.to_string())
261 );
262 };
263 let default_config_provider = super::TracerProvider::builder().build();
264 assert_service_name(default_config_provider, Some("unknown_service"));
265
266 let custom_config_provider = super::TracerProvider::builder()
268 .with_config(Config {
269 resource: Some(Arc::new(Resource::new(vec![KeyValue::new(
270 "service.name",
271 "test_service",
272 )]))),
273 ..Default::default()
274 })
275 .build();
276 assert_service_name(custom_config_provider, Some("test_service"));
277
278 env::set_var("OTEL_RESOURCE_ATTRIBUTES", "key1=value1, k2, k3=value2");
280 let env_resource_provider = super::TracerProvider::builder().build();
281 assert_eq!(
282 env_resource_provider.config().resource,
283 Some(Arc::new(Resource::new(vec![
284 KeyValue::new("key1", "value1"),
285 KeyValue::new("k3", "value2"),
286 KeyValue::new("service.name", "unknown_service"),
287 ])))
288 );
289
290 env::set_var(
292 "OTEL_RESOURCE_ATTRIBUTES",
293 "my-custom-key=env-val,k2=value2",
294 );
295 let user_provided_resource_config_provider = super::TracerProvider::builder()
296 .with_config(Config {
297 resource: Some(Arc::new(Resource::new(vec![KeyValue::new(
298 "my-custom-key",
299 "my-custom-value",
300 )]))),
301 ..Default::default()
302 })
303 .build();
304 assert_eq!(
305 user_provided_resource_config_provider.config().resource,
306 Some(Arc::new(Resource::new(vec![
307 KeyValue::new("my-custom-key", "my-custom-value"),
308 KeyValue::new("k2", "value2"),
309 KeyValue::new("service.name", "unknown_service"),
310 ])))
311 );
312 env::remove_var("OTEL_RESOURCE_ATTRIBUTES");
313
314 let no_service_name = super::TracerProvider::builder()
316 .with_config(Config {
317 resource: Some(Arc::new(Resource::empty())),
318 ..Default::default()
319 })
320 .build();
321
322 assert_service_name(no_service_name, None);
323 }
324}