opentelemetry_otlp/
span.rs

1//! # OTLP - Span Exporter
2//!
3//! Defines a [SpanExporter] to send trace data via the OpenTelemetry Protocol (OTLP)
4
5use std::fmt::Debug;
6
7use futures_core::future::BoxFuture;
8use opentelemetry::trace::TraceError;
9use opentelemetry_sdk::{
10    self as sdk,
11    export::trace::{ExportResult, SpanData},
12};
13use sdk::runtime::RuntimeChannel;
14
15#[cfg(feature = "grpc-tonic")]
16use crate::exporter::tonic::TonicExporterBuilder;
17
18#[cfg(any(feature = "http-proto", feature = "http-json"))]
19use crate::exporter::http::HttpExporterBuilder;
20
21use crate::{NoExporterConfig, OtlpPipeline};
22
23/// Target to which the exporter is going to send spans, defaults to https://localhost:4317/v1/traces.
24/// Learn about the relationship between this constant and default/metrics/logs at
25/// <https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/protocol/exporter.md#endpoint-urls-for-otlphttp>
26pub const OTEL_EXPORTER_OTLP_TRACES_ENDPOINT: &str = "OTEL_EXPORTER_OTLP_TRACES_ENDPOINT";
27/// Max waiting time for the backend to process each spans batch, defaults to 10s.
28pub const OTEL_EXPORTER_OTLP_TRACES_TIMEOUT: &str = "OTEL_EXPORTER_OTLP_TRACES_TIMEOUT";
29/// Compression algorithm to use, defaults to none.
30pub const OTEL_EXPORTER_OTLP_TRACES_COMPRESSION: &str = "OTEL_EXPORTER_OTLP_TRACES_COMPRESSION";
31/// Key-value pairs to be used as headers associated with gRPC or HTTP requests
32/// for sending spans.
33/// Example: `k1=v1,k2=v2`
34/// Note: this is only supported for HTTP.
35pub const OTEL_EXPORTER_OTLP_TRACES_HEADERS: &str = "OTEL_EXPORTER_OTLP_TRACES_HEADERS";
36
37impl OtlpPipeline {
38    /// Create a OTLP tracing pipeline.
39    pub fn tracing(self) -> OtlpTracePipeline<NoExporterConfig> {
40        OtlpTracePipeline {
41            exporter_builder: NoExporterConfig(()),
42            trace_config: None,
43            batch_config: None,
44        }
45    }
46}
47
48/// Recommended configuration for an OTLP exporter pipeline.
49///
50/// ## Examples
51///
52/// ```no_run
53/// let tracing_pipeline = opentelemetry_otlp::new_pipeline().tracing();
54/// ```
55#[derive(Debug)]
56pub struct OtlpTracePipeline<EB> {
57    exporter_builder: EB,
58    trace_config: Option<sdk::trace::Config>,
59    batch_config: Option<sdk::trace::BatchConfig>,
60}
61
62impl<EB> OtlpTracePipeline<EB> {
63    /// Set the trace provider configuration.
64    pub fn with_trace_config(mut self, trace_config: sdk::trace::Config) -> Self {
65        self.trace_config = Some(trace_config);
66        self
67    }
68
69    /// Set the batch span processor configuration, and it will override the env vars.
70    pub fn with_batch_config(mut self, batch_config: sdk::trace::BatchConfig) -> Self {
71        self.batch_config = Some(batch_config);
72        self
73    }
74}
75
76impl OtlpTracePipeline<NoExporterConfig> {
77    /// Set the OTLP span exporter builder.
78    ///
79    /// Note that the pipeline will not build the exporter until [`install_batch`] or [`install_simple`]
80    /// is called.
81    ///
82    /// [`install_batch`]: OtlpTracePipeline::install_batch
83    /// [`install_simple`]: OtlpTracePipeline::install_simple
84    pub fn with_exporter<B: Into<SpanExporterBuilder>>(
85        self,
86        pipeline: B,
87    ) -> OtlpTracePipeline<SpanExporterBuilder> {
88        OtlpTracePipeline {
89            exporter_builder: pipeline.into(),
90            trace_config: self.trace_config,
91            batch_config: self.batch_config,
92        }
93    }
94}
95
96impl OtlpTracePipeline<SpanExporterBuilder> {
97    /// Install the configured span exporter.
98    ///
99    /// Returns a [`TracerProvider`].
100    ///
101    /// [`TracerProvider`]: opentelemetry::trace::TracerProvider
102    pub fn install_simple(self) -> Result<sdk::trace::TracerProvider, TraceError> {
103        Ok(build_simple_with_exporter(
104            self.exporter_builder.build_span_exporter()?,
105            self.trace_config,
106        ))
107    }
108
109    /// Install the configured span exporter and a batch span processor using the
110    /// specified runtime.
111    ///
112    /// Returns a [`TracerProvider`].
113    ///
114    /// `install_batch` will panic if not called within a tokio runtime
115    ///
116    /// [`TracerProvider`]: opentelemetry::trace::TracerProvider
117    pub fn install_batch<R: RuntimeChannel>(
118        self,
119        runtime: R,
120    ) -> Result<sdk::trace::TracerProvider, TraceError> {
121        Ok(build_batch_with_exporter(
122            self.exporter_builder.build_span_exporter()?,
123            self.trace_config,
124            runtime,
125            self.batch_config,
126        ))
127    }
128}
129
130fn build_simple_with_exporter(
131    exporter: SpanExporter,
132    trace_config: Option<sdk::trace::Config>,
133) -> sdk::trace::TracerProvider {
134    let mut provider_builder = sdk::trace::TracerProvider::builder().with_simple_exporter(exporter);
135    if let Some(config) = trace_config {
136        provider_builder = provider_builder.with_config(config);
137    }
138
139    provider_builder.build()
140}
141
142fn build_batch_with_exporter<R: RuntimeChannel>(
143    exporter: SpanExporter,
144    trace_config: Option<sdk::trace::Config>,
145    runtime: R,
146    batch_config: Option<sdk::trace::BatchConfig>,
147) -> sdk::trace::TracerProvider {
148    let mut provider_builder = sdk::trace::TracerProvider::builder();
149    let batch_processor = sdk::trace::BatchSpanProcessor::builder(exporter, runtime)
150        .with_batch_config(batch_config.unwrap_or_default())
151        .build();
152    provider_builder = provider_builder.with_span_processor(batch_processor);
153
154    if let Some(config) = trace_config {
155        provider_builder = provider_builder.with_config(config);
156    }
157    provider_builder.build()
158}
159
160/// OTLP span exporter builder.
161#[derive(Debug)]
162// This enum only used during initialization stage of application. The overhead should be OK.
163// Users can also disable the unused features to make the overhead on object size smaller.
164#[allow(clippy::large_enum_variant)]
165#[non_exhaustive]
166pub enum SpanExporterBuilder {
167    /// Tonic span exporter builder
168    #[cfg(feature = "grpc-tonic")]
169    Tonic(TonicExporterBuilder),
170    /// Http span exporter builder
171    #[cfg(any(feature = "http-proto", feature = "http-json"))]
172    Http(HttpExporterBuilder),
173}
174
175impl SpanExporterBuilder {
176    /// Build a OTLP span exporter using the given tonic configuration and exporter configuration.
177    pub fn build_span_exporter(self) -> Result<SpanExporter, TraceError> {
178        match self {
179            #[cfg(feature = "grpc-tonic")]
180            SpanExporterBuilder::Tonic(builder) => builder.build_span_exporter(),
181            #[cfg(any(feature = "http-proto", feature = "http-json"))]
182            SpanExporterBuilder::Http(builder) => builder.build_span_exporter(),
183        }
184    }
185}
186
187#[cfg(feature = "grpc-tonic")]
188impl From<TonicExporterBuilder> for SpanExporterBuilder {
189    fn from(exporter: TonicExporterBuilder) -> Self {
190        SpanExporterBuilder::Tonic(exporter)
191    }
192}
193
194#[cfg(any(feature = "http-proto", feature = "http-json"))]
195impl From<HttpExporterBuilder> for SpanExporterBuilder {
196    fn from(exporter: HttpExporterBuilder) -> Self {
197        SpanExporterBuilder::Http(exporter)
198    }
199}
200
201/// OTLP exporter that sends tracing information
202#[derive(Debug)]
203pub struct SpanExporter(Box<dyn opentelemetry_sdk::export::trace::SpanExporter>);
204
205impl SpanExporter {
206    /// Build a new span exporter from a client
207    pub fn new(client: impl opentelemetry_sdk::export::trace::SpanExporter + 'static) -> Self {
208        SpanExporter(Box::new(client))
209    }
210}
211
212impl opentelemetry_sdk::export::trace::SpanExporter for SpanExporter {
213    fn export(&mut self, batch: Vec<SpanData>) -> BoxFuture<'static, ExportResult> {
214        self.0.export(batch)
215    }
216
217    fn set_resource(&mut self, resource: &opentelemetry_sdk::Resource) {
218        self.0.set_resource(resource);
219    }
220}