opentelemetry_spanprocessor_any/sdk/trace/
provider.rs

1//! # Trace Provider SDK
2//!
3//! ## Tracer Creation
4//!
5//! New `Tracer` instances are always created through a `TracerProvider`.
6//!
7//! All configuration objects and extension points (span processors,
8//! propagators) are provided by the `TracerProvider`. `Tracer` instances do
9//! not duplicate this data to avoid that different `Tracer` instances
10//! of the `TracerProvider` have different versions of these data.
11use 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
23/// Default tracer name if empty string is provided.
24const DEFAULT_COMPONENT_NAME: &str = "rust.opentelemetry.io/sdk/tracer";
25
26/// TracerProvider inner type
27#[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/// Creator and registry of named `Tracer` instances.
44#[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    /// Build a new tracer provider
57    pub(crate) fn new(inner: Arc<TracerProviderInner>) -> Self {
58        TracerProvider { inner }
59    }
60
61    /// Create a new `TracerProvider` builder.
62    pub fn builder() -> Builder {
63        Builder::default()
64    }
65
66    /// Span processors associated with this provider
67    pub fn span_processors(&self) -> &Vec<Box<dyn SpanProcessor>> {
68        &self.inner.processors
69    }
70
71    /// Config associated with this tracer
72    pub fn config(&self) -> &sdk::trace::Config {
73        &self.inner.config
74    }
75}
76
77impl crate::trace::TracerProvider for TracerProvider {
78    /// This implementation of `TracerProvider` produces `Tracer` instances.
79    type Tracer = sdk::trace::Tracer;
80
81    /// Create a new versioned `Tracer` instance.
82    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        // Use default value if name is invalid empty string
90        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    /// Force flush all remaining spans in span processors and return results.
102    fn force_flush(&self) -> Vec<TraceResult<()>> {
103        self.span_processors()
104            .iter()
105            .map(|processor| processor.force_flush())
106            .collect()
107    }
108}
109
110/// Builder for provider attributes.
111#[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    /// The `SpanExporter` that this provider should use.
136    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    /// The `SpanExporter` setup using a default `BatchSpanProcessor` that this provider should use.
146    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    /// The `SpanProcessor` that this provider should use.
156    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    /// The sdk `Config` that this provider will use.
164    pub fn with_config(self, config: sdk::trace::Config) -> Self {
165        Builder { config, ..self }
166    }
167
168    /// Return the clone of sdk provided resource.
169    ///
170    /// See <https://github.com/open-telemetry/opentelemetry-specification/blob/v1.8.0/specification/resource/sdk.md#sdk-provided-resource-attributes>
171    /// for details.
172    pub fn sdk_provided_resource(&self) -> Resource {
173        self.sdk_provided_resource.clone()
174    }
175
176    /// Create a new provider from this configuration.
177    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            // User provided resource information has higher priority.
182            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        // If users didn't provided a resource and there isn't a env var set. Use default one
254        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        // If user didn't provided a resource, try to get a default from env var
267        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        // If `OTEL_RESOURCE_ATTRIBUTES` is set, read them automatically
279        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        // When `OTEL_RESOURCE_ATTRIBUTES` is set and also user provided config
291        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        // If user provided a resource, it takes priority during collision.
315        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}