Skip to main content

opentelemetry_otlp/
lib.rs

1//! # OpenTelemetry OTLP Exporter
2//!
3//! The OTLP Exporter enables exporting telemetry data (logs, metrics, and traces) in the
4//! OpenTelemetry Protocol (OTLP) format to compatible backends. These backends include:
5//!
6//! - OpenTelemetry Collector
7//! - Open-source observability tools (Prometheus, Jaeger, etc.)
8//! - Vendor-specific monitoring platforms
9//!
10//! This crate supports sending OTLP data via:
11//! - gRPC
12//! - HTTP (binary protobuf or JSON)
13//!
14//! ## Quickstart with OpenTelemetry Collector
15//!
16//! The examples below show traces, but the same pattern applies to metrics
17//! ([`MetricExporter`]) and logs ([`LogExporter`]) — just swap the exporter
18//! builder and the corresponding SDK provider.
19//!
20//! ### HTTP Transport (Port 4318)
21//!
22//! Run the OpenTelemetry Collector:
23//!
24//! ```shell
25//! $ docker run -p 4318:4318 otel/opentelemetry-collector:latest
26//! ```
27//!
28//! Configure your application to export traces via HTTP:
29//!
30//! ```no_run
31//! # #[cfg(all(feature = "trace", feature = "http-proto"))]
32//! # {
33//! use opentelemetry::global;
34//! use opentelemetry::trace::Tracer;
35//! use opentelemetry_otlp::Protocol;
36//! use opentelemetry_otlp::WithExportConfig;
37//!
38//! fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync + 'static>> {
39//!     // Initialize OTLP exporter using HTTP binary protocol
40//!     let otlp_exporter = opentelemetry_otlp::SpanExporter::builder()
41//!         .with_http()
42//!         .with_protocol(Protocol::HttpBinary)
43//!         .build()?;
44//!
45//!     // Create a tracer provider with the exporter
46//!     let tracer_provider = opentelemetry_sdk::trace::SdkTracerProvider::builder()
47//!         .with_batch_exporter(otlp_exporter)
48//!         .build();
49//!
50//!     // Set it as the global provider
51//!     global::set_tracer_provider(tracer_provider);
52//!
53//!     // Get a tracer and create spans
54//!     let tracer = global::tracer("my_tracer");
55//!     tracer.in_span("doing_work", |_cx| {
56//!         // Your application logic here...
57//!     });
58//!
59//!     Ok(())
60//! # }
61//! }
62//! ```
63//!
64//! ### gRPC Transport (Port 4317)
65//!
66//! Run the OpenTelemetry Collector:
67//!
68//! ```shell
69//! $ docker run -p 4317:4317 otel/opentelemetry-collector:latest
70//! ```
71//!
72//! Configure your application to export traces via gRPC (the tonic client requires a Tokio runtime):
73//!
74//! - With `[tokio::main]`
75//!
76//! ```no_run
77//! # #[cfg(all(feature = "trace", feature = "grpc-tonic"))]
78//! # {
79//! use opentelemetry::{global, trace::Tracer};
80//!
81//! #[tokio::main]
82//! async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync + 'static>> {
83//!     // Initialize OTLP exporter using gRPC (Tonic)
84//!     let otlp_exporter = opentelemetry_otlp::SpanExporter::builder()
85//!         .with_tonic()
86//!         .build()?;
87//!
88//!     // Create a tracer provider with the exporter
89//!     let tracer_provider = opentelemetry_sdk::trace::SdkTracerProvider::builder()
90//!         .with_batch_exporter(otlp_exporter)
91//!         .build();
92//!
93//!     // Set it as the global provider
94//!     global::set_tracer_provider(tracer_provider);
95//!
96//!     // Get a tracer and create spans
97//!     let tracer = global::tracer("my_tracer");
98//!     tracer.in_span("doing_work", |_cx| {
99//!         // Your application logic here...
100//!     });
101//!
102//!     Ok(())
103//! # }
104//! }
105//! ```
106//!
107//! - Without `[tokio::main]`
108//!
109//! ```no_run
110//! # #[cfg(all(feature = "trace", feature = "grpc-tonic"))]
111//! # {
112//! use opentelemetry::{global, trace::Tracer};
113//!
114//! fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync + 'static>> {
115//!     // Initialize OTLP exporter using gRPC (Tonic)
116//!     let rt = tokio::runtime::Runtime::new()?;
117//!     let tracer_provider = rt.block_on(async {
118//!         let exporter = opentelemetry_otlp::SpanExporter::builder()
119//!             .with_tonic()
120//!             .build()
121//!             .expect("Failed to create span exporter");
122//!         opentelemetry_sdk::trace::SdkTracerProvider::builder()
123//!             .with_batch_exporter(exporter)
124//!             .build()
125//!     });
126//!
127//!     // Set it as the global provider
128//!     global::set_tracer_provider(tracer_provider);
129//!
130//!     // Get a tracer and create spans
131//!     let tracer = global::tracer("my_tracer");
132//!     tracer.in_span("doing_work", |_cx| {
133//!         // Your application logic here...
134//!     });
135//!
136//!     // Ensure the runtime (`rt`) remains active until the program ends
137//!     Ok(())
138//! # }
139//! }
140//! ```
141//!
142//! ## Using with Jaeger
143//!
144//! Jaeger natively supports the OTLP protocol, making it easy to send traces directly:
145//!
146//! ```shell
147//! $ docker run -p 16686:16686 -p 4317:4317 -e COLLECTOR_OTLP_ENABLED=true jaegertracing/all-in-one:latest
148//! ```
149//!
150//! After running your application configured with the OTLP exporter, view traces at:
151//! `http://localhost:16686`
152//!
153//! ## Using with Prometheus
154//!
155//! Prometheus natively supports accepting metrics via the OTLP protocol
156//! (HTTP/protobuf). You can [run
157//! Prometheus](https://prometheus.io/docs/prometheus/latest/installation/) with
158//! the following command:
159//!
160//! ```shell
161//! docker run -p 9090:9090 -v ./prometheus.yml:/etc/prometheus/prometheus.yml prom/prometheus --config.file=/etc/prometheus/prometheus.yml --web.enable-otlp-receiver
162//! ```
163//!
164//! (An empty prometheus.yml file is sufficient for this example.)
165//!
166//! Modify your application to export metrics via OTLP:
167//!
168//! ```no_run
169//! # #[cfg(all(feature = "metrics", feature = "http-proto"))]
170//! # {
171//! use opentelemetry::global;
172//! use opentelemetry::metrics::Meter;
173//! use opentelemetry::KeyValue;
174//! use opentelemetry_otlp::Protocol;
175//! use opentelemetry_otlp::WithExportConfig;
176//!
177//! fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync + 'static>> {
178//!     // Initialize OTLP exporter using HTTP binary protocol
179//!     let exporter = opentelemetry_otlp::MetricExporter::builder()
180//!         .with_http()
181//!         .with_protocol(Protocol::HttpBinary)
182//!         .with_endpoint("http://localhost:9090/api/v1/otlp/v1/metrics")
183//!         .build()?;
184//!
185//!     // Create a meter provider with the OTLP Metric exporter
186//!     let meter_provider = opentelemetry_sdk::metrics::SdkMeterProvider::builder()
187//!         .with_periodic_exporter(exporter)
188//!         .build();
189//!     global::set_meter_provider(meter_provider.clone());
190//!
191//!     // Get a meter
192//!     let meter = global::meter("my_meter");
193//!
194//!     // Create a metric
195//!     let counter = meter.u64_counter("my_counter").build();
196//!     counter.add(1, &[KeyValue::new("key", "value")]);
197//!
198//!     // Shutdown the meter provider. This will trigger an export of all metrics.
199//!     meter_provider.shutdown()?;
200//!
201//!     Ok(())
202//! # }
203//! }
204//! ```
205//!
206//! After running your application configured with the OTLP exporter, view metrics at:
207//! `http://localhost:9090`
208//!
209//! # Environment Variables
210//!
211//! The OTLP exporter respects the following environment variables, as defined by the
212//! [OpenTelemetry specification]. Programmatic configuration via builder methods
213//! takes precedence over environment variables. Signal-specific variables take
214//! precedence over the generic `OTEL_EXPORTER_OTLP_*` variables.
215//!
216//! [OpenTelemetry specification]: https://opentelemetry.io/docs/specs/otel/protocol/exporter/
217//!
218//! ## General (all signals)
219//!
220//! | Variable | Description | Default |
221//! |---|---|---|
222//! | `OTEL_EXPORTER_OTLP_ENDPOINT` | Target URL for the exporter. For HTTP, signal paths (`/v1/traces`, `/v1/metrics`, `/v1/logs`) are appended automatically. | `http://localhost:4318` (HTTP), `http://localhost:4317` (gRPC) |
223//! | `OTEL_EXPORTER_OTLP_PROTOCOL` | Transport protocol. Valid values: `grpc`, `http/protobuf`, `http/json`. Requires the corresponding crate feature. | Feature-dependent |
224//! | `OTEL_EXPORTER_OTLP_TIMEOUT` | Maximum wait time (in milliseconds) for the backend to process each batch. | `10000` |
225//! | `OTEL_EXPORTER_OTLP_HEADERS` | Key-value pairs for request headers. Format: `key1=value1,key2=value2`. Values are URL-decoded. | (none) |
226//! | `OTEL_EXPORTER_OTLP_COMPRESSION` | Compression algorithm. Valid values: `gzip`, `zstd`. | (none) |
227//!
228//! ## Traces
229//!
230//! | Variable | Description |
231//! |---|---|
232//! | `OTEL_EXPORTER_OTLP_TRACES_ENDPOINT` | Signal-specific endpoint for trace exports. |
233//! | `OTEL_EXPORTER_OTLP_TRACES_PROTOCOL` | Signal-specific protocol for trace exports. Valid values: `grpc`, `http/protobuf`, `http/json`. |
234//! | `OTEL_EXPORTER_OTLP_TRACES_TIMEOUT` | Signal-specific timeout (in milliseconds) for trace exports. |
235//! | `OTEL_EXPORTER_OTLP_TRACES_HEADERS` | Signal-specific headers for trace exports. |
236//! | `OTEL_EXPORTER_OTLP_TRACES_COMPRESSION` | Signal-specific compression for trace exports. |
237//!
238//! ## Metrics
239//!
240//! | Variable | Description |
241//! |---|---|
242//! | `OTEL_EXPORTER_OTLP_METRICS_ENDPOINT` | Signal-specific endpoint for metrics exports. |
243//! | `OTEL_EXPORTER_OTLP_METRICS_PROTOCOL` | Signal-specific protocol for metrics exports. Valid values: `grpc`, `http/protobuf`, `http/json`. |
244//! | `OTEL_EXPORTER_OTLP_METRICS_TIMEOUT` | Signal-specific timeout (in milliseconds) for metrics exports. |
245//! | `OTEL_EXPORTER_OTLP_METRICS_HEADERS` | Signal-specific headers for metrics exports. |
246//! | `OTEL_EXPORTER_OTLP_METRICS_COMPRESSION` | Signal-specific compression for metrics exports. |
247//! | `OTEL_EXPORTER_OTLP_METRICS_TEMPORALITY_PREFERENCE` | Temporality preference for metrics. Valid values: `cumulative`, `delta`, `lowmemory` (case-insensitive). | `cumulative` |
248//!
249//! ## Logs
250//!
251//! | Variable | Description |
252//! |---|---|
253//! | `OTEL_EXPORTER_OTLP_LOGS_ENDPOINT` | Signal-specific endpoint for log exports. |
254//! | `OTEL_EXPORTER_OTLP_LOGS_PROTOCOL` | Signal-specific protocol for log exports. Valid values: `grpc`, `http/protobuf`, `http/json`. |
255//! | `OTEL_EXPORTER_OTLP_LOGS_TIMEOUT` | Signal-specific timeout (in milliseconds) for log exports. |
256//! | `OTEL_EXPORTER_OTLP_LOGS_HEADERS` | Signal-specific headers for log exports. |
257//! | `OTEL_EXPORTER_OTLP_LOGS_COMPRESSION` | Signal-specific compression for log exports. |
258//!
259//! # Feature Flags
260//! The following feature flags can enable exporters for different telemetry signals:
261//!
262//! * `trace`: Includes the trace exporters.
263//! * `metrics`: Includes the metrics exporters.
264//! * `logs`: Includes the logs exporters.
265//!
266//! The following feature flags generate additional code and types:
267//! * `serialize`: Enables serialization support for type defined in this crate via `serde`.
268//!
269//! The following feature flags offer additional configurations on gRPC:
270//!
271//! For users using `tonic` as grpc layer:
272//! * `grpc-tonic`: Use `tonic` as grpc layer.
273//! * `gzip-tonic`: Use gzip compression for `tonic` grpc layer.
274//! * `zstd-tonic`: Use zstd compression for `tonic` grpc layer.
275//! * `tls-ring`: Enable rustls TLS support using ring for `tonic`.
276//! * `tls-aws-lc`: Enable rustls TLS support using aws-lc for `tonic`.
277//! * `tls-provider-agnostic`: Provider-agnostic TLS — enables TLS code paths without bundling a specific
278//!   crypto provider. Use this when you install a `CryptoProvider` globally
279//!   (e.g., via `rustls-openssl` for FIPS/OpenSSL environments).
280//! * `tls` (deprecated): Use `tls-ring` or `tls-aws-lc` instead.
281//! * `tls-roots`: Adds system trust roots to rustls-based gRPC clients using the rustls-native-certs crate (use with `tls-ring` or `tls-aws-lc`).
282//! * `tls-webpki-roots`: Embeds Mozilla's trust roots to rustls-based gRPC clients using the webpki-roots crate (use with `tls-ring` or `tls-aws-lc`).
283//!
284//! The following feature flags offer additional configurations on http:
285//!
286//! * `http-proto`: Use http as transport layer, protobuf as body format. This feature is enabled by default.
287//! * `gzip-http`: Use gzip compression for HTTP transport.
288//! * `zstd-http`: Use zstd compression for HTTP transport.
289//! * `reqwest-blocking-client`: Use reqwest blocking http client. This feature is enabled by default.
290//! * `reqwest-client`: Use reqwest http client.
291//! * `reqwest-rustls`: Use reqwest with TLS with system trust roots via `rustls-native-certs` crate.
292//! * `reqwest-rustls-webpki-roots`: Use reqwest with TLS with Mozilla's trust roots via `webpki-roots` crate.
293//!
294//! The following feature flags enable experimental retry support:
295//!
296//! * `experimental-grpc-retry`: Enable automatic retry with exponential backoff for gRPC exports.
297//!   Requires a Tokio runtime (`rt-tokio` SDK feature is enabled transitively).
298//! * `experimental-http-retry`: Enable automatic retry with exponential backoff for HTTP exports.
299//!   Requires a Tokio runtime (`rt-tokio` SDK feature is enabled transitively).
300//!
301//! # Full Configuration Reference
302//!
303//!
304//! There are two layers of configuration for the OTLP exporter:
305//!
306//! 1. **Exporter configuration** – controls how telemetry is sent (endpoint, transport, headers, TLS, compression, timeout).
307//!    Built via the signal-specific builder: [`SpanExporter::builder()`], [`MetricExporter::builder()`], [`LogExporter::builder()`].
308//! 2. **Provider/SDK configuration** – controls how telemetry is collected and batched (sampling, batch size, resource, etc.).
309//!    Built via [`opentelemetry_sdk::trace::SdkTracerProvider::builder()`],
310//!    [`opentelemetry_sdk::metrics::SdkMeterProvider::builder()`], or
311//!    [`opentelemetry_sdk::logs::SdkLoggerProvider::builder()`].
312//!
313//! **Configuration precedence** (highest wins):
314//! 1. Programmatic configuration via builder methods — always wins when set
315//! 2. Signal-specific environment variables (e.g. `OTEL_EXPORTER_OTLP_TRACES_ENDPOINT`) — used when no programmatic value is set
316//! 3. Generic environment variables (e.g. `OTEL_EXPORTER_OTLP_ENDPOINT`) — used when neither programmatic nor signal-specific env var is set
317//! 4. Built-in defaults — used when nothing else is configured
318//!
319//! ## gRPC (tonic) — all configuration options
320//!
321//! Requires the `grpc-tonic` feature. The methods below come from two traits:
322//! - [`WithExportConfig`]: `with_endpoint`, `with_timeout` (shared with HTTP)
323//! - [`WithTonicConfig`]: `with_metadata`, `with_compression`, `with_tls_config`, `with_channel`, `with_interceptor`
324//!
325//! The examples here use [`SpanExporter`], but the same builder methods are
326//! available on [`MetricExporter`] and [`LogExporter`].
327//!
328//! ```no_run
329//! # #[cfg(all(feature = "trace", feature = "grpc-tonic"))]
330//! # {
331//! use opentelemetry_otlp::{WithExportConfig, WithTonicConfig, Compression};
332//! use std::time::Duration;
333//! use tonic::metadata::MetadataMap;
334//!
335//! // ── gRPC metadata (custom request headers) ───────────────────────────────
336//! // MetadataMap carries per-call key/value pairs sent as HTTP/2 headers.
337//! // with_metadata() is additive: calling it multiple times merges entries.
338//! let mut metadata = MetadataMap::with_capacity(3);
339//! metadata.insert("x-host", "example.com".parse().unwrap());
340//! metadata.insert("x-api-key", "secret".parse().unwrap());
341//! metadata.insert_bin("trace-proto-bin", tonic::metadata::MetadataValue::from_bytes(b"[bin]"));
342//!
343//! let exporter = opentelemetry_otlp::SpanExporter::builder()
344//!     .with_tonic()
345//!     // Target gRPC endpoint. Defaults to http://localhost:4317.
346//!     // Env var: OTEL_EXPORTER_OTLP_TRACES_ENDPOINT (or OTEL_EXPORTER_OTLP_ENDPOINT).
347//!     .with_endpoint("http://my-collector:4317")
348//!     // Per-export timeout. Defaults to 10 s.
349//!     // Env var: OTEL_EXPORTER_OTLP_TRACES_TIMEOUT (or OTEL_EXPORTER_OTLP_TIMEOUT).
350//!     .with_timeout(Duration::from_secs(5))
351//!     // Custom gRPC metadata (auth tokens, routing headers, …).
352//!     // Env var: OTEL_EXPORTER_OTLP_TRACES_HEADERS (or OTEL_EXPORTER_OTLP_HEADERS).
353//!     .with_metadata(metadata)
354//!     // Compression. Requires the `gzip-tonic` or `zstd-tonic` feature.
355//!     // Env var: OTEL_EXPORTER_OTLP_TRACES_COMPRESSION (or OTEL_EXPORTER_OTLP_COMPRESSION).
356//!     .with_compression(Compression::Gzip)
357//!     .build()
358//!     .expect("Failed to build SpanExporter");
359//! # }
360//! ```
361//!
362//! ### TLS (grpc-tonic)
363//!
364//! Requires the `tls-ring` or `tls-aws-lc` feature (plus optionally `tls-roots` or `tls-webpki-roots`
365//! to load CA roots automatically).
366//!
367//! ```no_run
368//! # #[cfg(all(feature = "trace", feature = "grpc-tonic", any(feature = "tls-ring", feature = "tls-aws-lc")))]
369//! # {
370//! use opentelemetry_otlp::{WithExportConfig, WithTonicConfig};
371//! use opentelemetry_otlp::tonic_types::transport::ClientTlsConfig;
372//!
373//! let tls = ClientTlsConfig::new()
374//!     .domain_name("my-collector.example.com")
375//!     // Optionally verify the server with a CA certificate:
376//!     // .ca_certificate(tonic::transport::Certificate::from_pem(CA_PEM))
377//!     // Or present a client identity for mutual TLS (mTLS):
378//!     // .identity(tonic::transport::Identity::from_pem(CERT_PEM, KEY_PEM))
379//!     ;
380//!
381//! let exporter = opentelemetry_otlp::SpanExporter::builder()
382//!     .with_tonic()
383//!     .with_endpoint("https://my-collector.example.com:4317")
384//!     .with_tls_config(tls)
385//!     .build()
386//!     .expect("Failed to build SpanExporter");
387//! # }
388//! ```
389//!
390//! ### Pre-built tonic channel
391//!
392//! Use `with_channel` when you need full control over the transport (e.g. Unix sockets,
393//! custom load-balancing). **Note:** `with_channel` overrides any TLS config set via
394//! `with_tls_config`, and you are responsible for matching the channel timeout to
395//! the exporter timeout.
396//!
397//! ```no_run
398//! # #[cfg(all(feature = "trace", feature = "grpc-tonic"))]
399//! # {
400//! use opentelemetry_otlp::{WithExportConfig, WithTonicConfig};
401//! use std::time::Duration;
402//!
403//! let channel = tonic::transport::Channel::from_static("http://localhost:4317")
404//!     .timeout(Duration::from_secs(5))
405//!     .connect_lazy();
406//!
407//! let exporter = opentelemetry_otlp::SpanExporter::builder()
408//!     .with_tonic()
409//!     .with_channel(channel)
410//!     .with_timeout(Duration::from_secs(5)) // keep in sync with channel timeout above
411//!     .build()
412//!     .expect("Failed to build SpanExporter");
413//! # }
414//! ```
415//!
416//! ### gRPC interceptors
417//!
418//! Use `with_interceptor` to modify every outbound gRPC request — useful for injecting
419//! auth tokens or dynamic metadata. Only one interceptor can be set; chain multiple together
420//! before passing them in.
421//!
422//! ```no_run
423//! # #[cfg(all(feature = "trace", feature = "grpc-tonic"))]
424//! # {
425//! use opentelemetry_otlp::WithTonicConfig;
426//! use tonic::{Request, Status};
427//!
428//! fn auth_interceptor(mut req: Request<()>) -> Result<Request<()>, Status> {
429//!     req.metadata_mut().insert("authorization", "Bearer my-token".parse().unwrap());
430//!     Ok(req)
431//! }
432//!
433//! let exporter = opentelemetry_otlp::SpanExporter::builder()
434//!     .with_tonic()
435//!     .with_interceptor(auth_interceptor)
436//!     .build()
437//!     .expect("Failed to build SpanExporter");
438//! # }
439//! ```
440//!
441//! ### gRPC retry policy
442//!
443//! Requires the `experimental-grpc-retry` feature. When enabled, failed exports are retried
444//! with exponential backoff and jitter. Without this feature, failed exports are not retried.
445//!
446//! ```no_run
447//! # #[cfg(all(feature = "trace", feature = "experimental-grpc-retry"))]
448//! # {
449//! use opentelemetry_otlp::{WithTonicConfig, RetryPolicy};
450//!
451//! let exporter = opentelemetry_otlp::SpanExporter::builder()
452//!     .with_tonic()
453//!     .with_retry_policy(RetryPolicy {
454//!         max_retries: 5,        // number of attempts after the first failure
455//!         initial_delay_ms: 500, // delay before the first retry
456//!         max_delay_ms: 30_000,  // cap on the delay between retries
457//!         jitter_ms: 100,        // upper bound for random jitter added by the exporter
458//!     })
459//!     .build()
460//!     .expect("Failed to build SpanExporter");
461//! # }
462//! ```
463//!
464//! ## HTTP — all configuration options
465//!
466//! Requires the `http-proto` (default) or `http-json` feature. The methods below come from:
467//! - [`WithExportConfig`]: `with_endpoint`, `with_timeout`, `with_protocol`
468//! - [`WithHttpConfig`]: `with_headers`, `with_compression`, `with_http_client`
469//!
470//! The examples here use [`SpanExporter`], but the same builder methods are
471//! available on [`MetricExporter`] and [`LogExporter`].
472//!
473//! ```no_run
474//! # #[cfg(all(feature = "trace", feature = "http-proto"))]
475//! # {
476//! use opentelemetry_otlp::{WithExportConfig, WithHttpConfig, Protocol, Compression};
477//! use std::time::Duration;
478//! use std::collections::HashMap;
479//!
480//! let exporter = opentelemetry_otlp::SpanExporter::builder()
481//!     .with_http()
482//!     // Target base URL. Defaults to http://localhost:4318.
483//!     // The path /v1/traces (or /v1/metrics, /v1/logs) is appended automatically.
484//!     // Env var: OTEL_EXPORTER_OTLP_TRACES_ENDPOINT (or OTEL_EXPORTER_OTLP_ENDPOINT).
485//!     .with_endpoint("http://my-collector:4318")
486//!     // Per-export timeout. Defaults to 10 s.
487//!     // Env var: OTEL_EXPORTER_OTLP_TRACES_TIMEOUT (or OTEL_EXPORTER_OTLP_TIMEOUT).
488//!     .with_timeout(Duration::from_secs(5))
489//!     // Transport encoding. HttpBinary (protobuf) is the default.
490//!     // HttpJson requires the `http-json` feature.
491//!     // Env var: OTEL_EXPORTER_OTLP_PROTOCOL.
492//!     .with_protocol(Protocol::HttpBinary)
493//!     // Custom HTTP headers (auth tokens, routing headers, …).
494//!     // Values are URL-decoded when read from environment variables.
495//!     // Env var: OTEL_EXPORTER_OTLP_TRACES_HEADERS (or OTEL_EXPORTER_OTLP_HEADERS).
496//!     .with_headers(HashMap::from([
497//!         ("x-api-key".to_string(), "secret".to_string()),
498//!     ]))
499//!     // Compression. Requires the `gzip-http` or `zstd-http` feature.
500//!     // Env var: OTEL_EXPORTER_OTLP_TRACES_COMPRESSION (or OTEL_EXPORTER_OTLP_COMPRESSION).
501//!     .with_compression(Compression::Gzip)
502//!     .build()
503//!     .expect("Failed to build SpanExporter");
504//! # }
505//! ```
506//!
507//! ### Custom HTTP client
508//!
509//! By default the exporter uses a `reqwest` blocking client (`reqwest-blocking-client` feature,
510//! enabled by default). Supply your own client to control TLS, proxies, connection pooling, etc.
511//! The client must implement the [`opentelemetry_http::HttpClient`] trait.
512//!
513//! ```no_run
514//! # #[cfg(all(feature = "trace", feature = "http-proto", feature = "reqwest-client"))]
515//! # {
516//! use opentelemetry_otlp::WithHttpConfig;
517//!
518//! // reqwest async client (requires the `reqwest-client` feature)
519//! let http_client = reqwest::Client::builder()
520//!     .timeout(std::time::Duration::from_secs(5))
521//!     // .danger_accept_invalid_certs(true) // for testing only
522//!     .build()
523//!     .expect("Failed to build reqwest client");
524//!
525//! let exporter = opentelemetry_otlp::SpanExporter::builder()
526//!     .with_http()
527//!     .with_http_client(http_client)
528//!     .build()
529//!     .expect("Failed to build SpanExporter");
530//! # }
531//! ```
532//!
533//! ### HTTP retry policy
534//!
535//! Requires the `experimental-http-retry` feature. When enabled, failed exports are retried
536//! with exponential backoff and jitter. Without this feature, failed exports are not retried.
537//!
538//! ```no_run
539//! # #[cfg(all(feature = "trace", feature = "experimental-http-retry"))]
540//! # {
541//! use opentelemetry_otlp::{WithHttpConfig, RetryPolicy};
542//!
543//! let exporter = opentelemetry_otlp::SpanExporter::builder()
544//!     .with_http()
545//!     .with_retry_policy(RetryPolicy {
546//!         max_retries: 5,        // number of attempts after the first failure
547//!         initial_delay_ms: 500, // delay before the first retry
548//!         max_delay_ms: 30_000,  // cap on the delay between retries
549//!         jitter_ms: 100,        // upper bound for random jitter added by the exporter
550//!     })
551//!     .build()
552//!     .expect("Failed to build SpanExporter");
553//! # }
554//! ```
555//!
556//! ## All three signals (Traces, Metrics, Logs)
557//!
558//! The same exporter configuration options apply to all three signals. The only differences are:
559//! - The builder entry point: [`SpanExporter::builder()`], [`MetricExporter::builder()`], [`LogExporter::builder()`]
560//! - The signal-specific environment variables (e.g. `OTEL_EXPORTER_OTLP_TRACES_*` vs `OTEL_EXPORTER_OTLP_METRICS_*`)
561//! - Metrics has an additional `OTEL_EXPORTER_OTLP_METRICS_TEMPORALITY_PREFERENCE` variable
562//!
563//! ```no_run
564//! # #[cfg(all(feature = "trace", feature = "metrics", feature = "logs", feature = "grpc-tonic"))]
565//! # {
566//! use opentelemetry_otlp::{WithExportConfig, WithTonicConfig};
567//! use opentelemetry_sdk::{
568//!     trace::SdkTracerProvider,
569//!     metrics::SdkMeterProvider,
570//!     logs::SdkLoggerProvider,
571//!     Resource,
572//! };
573//! use std::time::Duration;
574//!
575//! let resource = Resource::builder()
576//!     .with_service_name("my-service")
577//!     .build();
578//!
579//! // Traces
580//! let span_exporter = opentelemetry_otlp::SpanExporter::builder()
581//!     .with_tonic()
582//!     .with_endpoint("http://my-collector:4317")
583//!     .with_timeout(Duration::from_secs(5))
584//!     .build()
585//!     .expect("Failed to build SpanExporter");
586//! let tracer_provider = SdkTracerProvider::builder()
587//!     .with_resource(resource.clone())
588//!     .with_batch_exporter(span_exporter)
589//!     .build();
590//!
591//! // Metrics
592//! let metric_exporter = opentelemetry_otlp::MetricExporter::builder()
593//!     .with_tonic()
594//!     .with_endpoint("http://my-collector:4317")
595//!     .with_timeout(Duration::from_secs(5))
596//!     .build()
597//!     .expect("Failed to build MetricExporter");
598//! let meter_provider = SdkMeterProvider::builder()
599//!     .with_resource(resource.clone())
600//!     .with_periodic_exporter(metric_exporter)
601//!     .build();
602//!
603//! // Logs
604//! let log_exporter = opentelemetry_otlp::LogExporter::builder()
605//!     .with_tonic()
606//!     .with_endpoint("http://my-collector:4317")
607//!     .with_timeout(Duration::from_secs(5))
608//!     .build()
609//!     .expect("Failed to build LogExporter");
610//! let logger_provider = SdkLoggerProvider::builder()
611//!     .with_resource(resource)
612//!     .with_batch_exporter(log_exporter)
613//!     .build();
614//! # }
615//! ```
616#![warn(
617    future_incompatible,
618    missing_debug_implementations,
619    missing_docs,
620    nonstandard_style,
621    rust_2018_idioms,
622    unreachable_pub,
623    unused
624)]
625#![allow(elided_lifetimes_in_paths)]
626#![cfg_attr(docsrs, feature(doc_cfg), deny(rustdoc::broken_intra_doc_links))]
627#![cfg_attr(test, deny(warnings))]
628
629mod exporter;
630#[cfg(feature = "logs")]
631#[cfg(any(feature = "http-proto", feature = "http-json", feature = "grpc-tonic"))]
632mod logs;
633#[cfg(feature = "metrics")]
634#[cfg(any(feature = "http-proto", feature = "http-json", feature = "grpc-tonic"))]
635mod metric;
636#[cfg(feature = "trace")]
637#[cfg(any(feature = "http-proto", feature = "http-json", feature = "grpc-tonic"))]
638mod span;
639
640#[cfg(any(feature = "grpc-tonic", feature = "experimental-http-retry"))]
641pub mod retry_classification;
642
643/// Retry logic for exporting telemetry data.
644#[cfg(any(feature = "grpc-tonic", feature = "experimental-http-retry"))]
645pub mod retry;
646
647pub use crate::exporter::Compression;
648pub use crate::exporter::ExporterBuildError;
649#[cfg(feature = "trace")]
650#[cfg(any(feature = "http-proto", feature = "http-json", feature = "grpc-tonic"))]
651pub use crate::span::{
652    SpanExporter, SpanExporterBuilder, OTEL_EXPORTER_OTLP_TRACES_COMPRESSION,
653    OTEL_EXPORTER_OTLP_TRACES_ENDPOINT, OTEL_EXPORTER_OTLP_TRACES_HEADERS,
654    OTEL_EXPORTER_OTLP_TRACES_PROTOCOL, OTEL_EXPORTER_OTLP_TRACES_TIMEOUT,
655};
656
657#[cfg(feature = "metrics")]
658#[cfg(any(feature = "http-proto", feature = "http-json", feature = "grpc-tonic"))]
659pub use crate::metric::{
660    MetricExporter, MetricExporterBuilder, OTEL_EXPORTER_OTLP_METRICS_COMPRESSION,
661    OTEL_EXPORTER_OTLP_METRICS_ENDPOINT, OTEL_EXPORTER_OTLP_METRICS_HEADERS,
662    OTEL_EXPORTER_OTLP_METRICS_PROTOCOL, OTEL_EXPORTER_OTLP_METRICS_TEMPORALITY_PREFERENCE,
663    OTEL_EXPORTER_OTLP_METRICS_TIMEOUT,
664};
665
666#[cfg(feature = "logs")]
667#[cfg(any(feature = "http-proto", feature = "http-json", feature = "grpc-tonic"))]
668pub use crate::logs::{
669    LogExporter, LogExporterBuilder, OTEL_EXPORTER_OTLP_LOGS_COMPRESSION,
670    OTEL_EXPORTER_OTLP_LOGS_ENDPOINT, OTEL_EXPORTER_OTLP_LOGS_HEADERS,
671    OTEL_EXPORTER_OTLP_LOGS_PROTOCOL, OTEL_EXPORTER_OTLP_LOGS_TIMEOUT,
672};
673
674#[cfg(any(feature = "http-proto", feature = "http-json"))]
675pub use crate::exporter::http::WithHttpConfig;
676
677#[cfg(feature = "grpc-tonic")]
678pub use crate::exporter::tonic::WithTonicConfig;
679
680pub use crate::exporter::{
681    WithExportConfig, OTEL_EXPORTER_OTLP_COMPRESSION, OTEL_EXPORTER_OTLP_ENDPOINT,
682    OTEL_EXPORTER_OTLP_ENDPOINT_DEFAULT, OTEL_EXPORTER_OTLP_HEADERS, OTEL_EXPORTER_OTLP_PROTOCOL,
683    OTEL_EXPORTER_OTLP_PROTOCOL_GRPC, OTEL_EXPORTER_OTLP_PROTOCOL_HTTP_JSON,
684    OTEL_EXPORTER_OTLP_PROTOCOL_HTTP_PROTOBUF, OTEL_EXPORTER_OTLP_TIMEOUT,
685    OTEL_EXPORTER_OTLP_TIMEOUT_DEFAULT,
686};
687
688#[cfg(any(
689    feature = "experimental-http-retry",
690    feature = "experimental-grpc-retry"
691))]
692pub use retry::RetryPolicy;
693
694/// Type to indicate the builder does not have a client set.
695#[derive(Debug, Default, Clone)]
696pub struct NoExporterBuilderSet;
697
698/// Type to hold the [TonicExporterBuilder] and indicate it has been set.
699///
700/// Allowing access to [TonicExporterBuilder] specific configuration methods.
701#[cfg(feature = "grpc-tonic")]
702// This is for clippy to work with only the grpc-tonic feature enabled
703#[allow(unused)]
704#[derive(Debug, Default)]
705pub struct TonicExporterBuilderSet(TonicExporterBuilder);
706
707/// Type to hold the [HttpExporterBuilder] and indicate it has been set.
708///
709/// Allowing access to [HttpExporterBuilder] specific configuration methods.
710#[cfg(any(feature = "http-proto", feature = "http-json"))]
711#[derive(Debug, Default)]
712pub struct HttpExporterBuilderSet(HttpExporterBuilder);
713
714#[cfg(any(feature = "http-proto", feature = "http-json"))]
715pub use crate::exporter::http::HttpExporterBuilder;
716
717#[cfg(feature = "grpc-tonic")]
718pub use crate::exporter::tonic::TonicExporterBuilder;
719
720#[cfg(feature = "serialize")]
721use serde::{Deserialize, Serialize};
722
723/// The communication protocol to use when exporting data.
724#[cfg_attr(feature = "serialize", derive(Deserialize, Serialize))]
725#[derive(Clone, Copy, Debug, Eq, PartialEq)]
726pub enum Protocol {
727    /// GRPC protocol
728    #[cfg(feature = "grpc-tonic")]
729    Grpc,
730    /// HTTP protocol with binary protobuf
731    #[cfg(feature = "http-proto")]
732    HttpBinary,
733    /// HTTP protocol with JSON payload
734    #[cfg(feature = "http-json")]
735    HttpJson,
736}
737
738#[cfg(any(feature = "grpc-tonic", feature = "http-proto", feature = "http-json"))]
739impl Protocol {
740    /// Attempts to parse a protocol from the `OTEL_EXPORTER_OTLP_PROTOCOL` environment variable.
741    ///
742    /// Returns `None` if:
743    /// - The environment variable is not set
744    /// - The value doesn't match a known protocol
745    /// - The specified protocol's feature is not enabled
746    pub fn from_env() -> Option<Self> {
747        Self::parse_from_env_var(OTEL_EXPORTER_OTLP_PROTOCOL)
748    }
749
750    /// Attempts to parse a protocol from the given environment variable.
751    ///
752    /// Returns `None` if:
753    /// - The environment variable is not set
754    /// - The value doesn't match a known protocol
755    /// - The specified protocol's feature is not enabled
756    pub(crate) fn parse_from_env_var(env_var: &str) -> Option<Self> {
757        use crate::exporter::{
758            OTEL_EXPORTER_OTLP_PROTOCOL_GRPC, OTEL_EXPORTER_OTLP_PROTOCOL_HTTP_JSON,
759            OTEL_EXPORTER_OTLP_PROTOCOL_HTTP_PROTOBUF,
760        };
761
762        let protocol = std::env::var(env_var).ok()?;
763
764        match protocol.as_str() {
765            OTEL_EXPORTER_OTLP_PROTOCOL_GRPC => {
766                #[cfg(feature = "grpc-tonic")]
767                {
768                    Some(Protocol::Grpc)
769                }
770                #[cfg(not(feature = "grpc-tonic"))]
771                {
772                    opentelemetry::otel_warn!(
773                        name: "Protocol.InvalidFeatureCombination",
774                        message = format!("Protocol '{}' requested but 'grpc-tonic' feature is not enabled", OTEL_EXPORTER_OTLP_PROTOCOL_GRPC)
775                    );
776                    None
777                }
778            }
779            OTEL_EXPORTER_OTLP_PROTOCOL_HTTP_PROTOBUF => {
780                #[cfg(feature = "http-proto")]
781                {
782                    Some(Protocol::HttpBinary)
783                }
784                #[cfg(not(feature = "http-proto"))]
785                {
786                    opentelemetry::otel_warn!(
787                        name: "Protocol.InvalidFeatureCombination",
788                        message = format!("Protocol '{}' requested but 'http-proto' feature is not enabled", OTEL_EXPORTER_OTLP_PROTOCOL_HTTP_PROTOBUF)
789                    );
790                    None
791                }
792            }
793            OTEL_EXPORTER_OTLP_PROTOCOL_HTTP_JSON => {
794                #[cfg(feature = "http-json")]
795                {
796                    Some(Protocol::HttpJson)
797                }
798                #[cfg(not(feature = "http-json"))]
799                {
800                    opentelemetry::otel_warn!(
801                        name: "Protocol.InvalidFeatureCombination",
802                        message = format!("Protocol '{}' requested but 'http-json' feature is not enabled", OTEL_EXPORTER_OTLP_PROTOCOL_HTTP_JSON)
803                    );
804                    None
805                }
806            }
807            _ => None,
808        }
809    }
810
811    /// Returns the default protocol based on enabled features, without
812    /// consulting environment variables.
813    ///
814    /// Priority order (first available wins):
815    /// 1. http-json (if enabled)
816    /// 2. http-proto (if enabled)
817    /// 3. grpc-tonic (if enabled)
818    pub(crate) fn feature_default() -> Self {
819        #[cfg(feature = "http-json")]
820        return Protocol::HttpJson;
821
822        #[cfg(all(feature = "http-proto", not(feature = "http-json")))]
823        return Protocol::HttpBinary;
824
825        #[cfg(all(
826            feature = "grpc-tonic",
827            not(any(feature = "http-proto", feature = "http-json"))
828        ))]
829        return Protocol::Grpc;
830    }
831}
832
833#[derive(Debug, Default)]
834#[doc(hidden)]
835/// Placeholder type when no exporter pipeline has been configured in telemetry pipeline.
836pub struct NoExporterConfig(());
837
838/// Re-exported types from the `tonic` crate.
839#[cfg(feature = "grpc-tonic")]
840pub mod tonic_types {
841    /// Re-exported types from `tonic::metadata`.
842    pub mod metadata {
843        #[doc(no_inline)]
844        pub use tonic::metadata::MetadataMap;
845    }
846
847    /// Re-exported types from `tonic::transport`.
848    #[cfg(any(
849        feature = "tls",
850        feature = "tls-ring",
851        feature = "tls-aws-lc",
852        feature = "tls-provider-agnostic"
853    ))]
854    pub mod transport {
855        #[doc(no_inline)]
856        pub use tonic::transport::{Certificate, ClientTlsConfig, Identity};
857    }
858}