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//! ### HTTP Transport (Port 4318)
17//!
18//! Run the OpenTelemetry Collector:
19//!
20//! ```shell
21//! $ docker run -p 4318:4318 otel/opentelemetry-collector:latest
22//! ```
23//!
24//! Configure your application to export traces via HTTP:
25//!
26//! ```no_run
27//! # #[cfg(all(feature = "trace", feature = "http-proto"))]
28//! # {
29//! use opentelemetry::global;
30//! use opentelemetry::trace::Tracer;
31//! use opentelemetry_otlp::Protocol;
32//! use opentelemetry_otlp::WithExportConfig;
33//!
34//! fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync + 'static>> {
35//!     // Initialize OTLP exporter using HTTP binary protocol
36//!     let otlp_exporter = opentelemetry_otlp::SpanExporter::builder()
37//!         .with_http()
38//!         .with_protocol(Protocol::HttpBinary)
39//!         .build()?;
40//!
41//!     // Create a tracer provider with the exporter
42//!     let tracer_provider = opentelemetry_sdk::trace::SdkTracerProvider::builder()
43//!         .with_simple_exporter(otlp_exporter)
44//!         .build();
45//!
46//!     // Set it as the global provider
47//!     global::set_tracer_provider(tracer_provider);
48//!
49//!     // Get a tracer and create spans
50//!     let tracer = global::tracer("my_tracer");
51//!     tracer.in_span("doing_work", |_cx| {
52//!         // Your application logic here...
53//!     });
54//!
55//!     Ok(())
56//! # }
57//! }
58//! ```
59//!
60//! ### gRPC Transport (Port 4317)
61//!
62//! Run the OpenTelemetry Collector:
63//!
64//! ```shell
65//! $ docker run -p 4317:4317 otel/opentelemetry-collector:latest
66//! ```
67//!
68//! Configure your application to export traces via gRPC (the tonic client requires a Tokio runtime):
69//!
70//! - With `[tokio::main]`
71//!
72//! ```no_run
73//! # #[cfg(all(feature = "trace", feature = "grpc-tonic"))]
74//! # {
75//! use opentelemetry::{global, trace::Tracer};
76//!
77//! #[tokio::main]
78//! async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync + 'static>> {
79//!     // Initialize OTLP exporter using gRPC (Tonic)
80//!     let otlp_exporter = opentelemetry_otlp::SpanExporter::builder()
81//!         .with_tonic()
82//!         .build()?;
83//!
84//!     // Create a tracer provider with the exporter
85//!     let tracer_provider = opentelemetry_sdk::trace::SdkTracerProvider::builder()
86//!         .with_simple_exporter(otlp_exporter)
87//!         .build();
88//!
89//!     // Set it as the global provider
90//!     global::set_tracer_provider(tracer_provider);
91//!
92//!     // Get a tracer and create spans
93//!     let tracer = global::tracer("my_tracer");
94//!     tracer.in_span("doing_work", |_cx| {
95//!         // Your application logic here...
96//!     });
97//!
98//!     Ok(())
99//! # }
100//! }
101//! ```
102//!
103//! - Without `[tokio::main]`
104//!
105//!  ```no_run
106//! # #[cfg(all(feature = "trace", feature = "grpc-tonic"))]
107//! # {
108//! use opentelemetry::{global, trace::Tracer};
109//!
110//! fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync + 'static>> {
111//!     // Initialize OTLP exporter using gRPC (Tonic)
112//!     let rt = tokio::runtime::Runtime::new()?;
113//!     let tracer_provider = rt.block_on(async {
114//!         let exporter = opentelemetry_otlp::SpanExporter::builder()
115//!             .with_tonic()
116//!             .build()
117//!             .expect("Failed to create span exporter");
118//!         opentelemetry_sdk::trace::SdkTracerProvider::builder()
119//!             .with_simple_exporter(exporter)
120//!             .build()
121//!     });
122//!
123//!     // Set it as the global provider
124//!     global::set_tracer_provider(tracer_provider);
125//!
126//!     // Get a tracer and create spans
127//!     let tracer = global::tracer("my_tracer");
128//!     tracer.in_span("doing_work", |_cx| {
129//!         // Your application logic here...
130//!     });
131//!
132//!     // Ensure the runtime (`rt`) remains active until the program ends
133//!     Ok(())
134//! # }
135//! }
136//! ```
137//!
138//! ## Using with Jaeger
139//!
140//! Jaeger natively supports the OTLP protocol, making it easy to send traces directly:
141//!
142//! ```shell
143//! $ docker run -p 16686:16686 -p 4317:4317 -e COLLECTOR_OTLP_ENABLED=true jaegertracing/all-in-one:latest
144//! ```
145//!
146//! After running your application configured with the OTLP exporter, view traces at:
147//! `http://localhost:16686`
148//!
149//! ## Using with Prometheus
150//!
151//! Prometheus natively supports accepting metrics via the OTLP protocol
152//! (HTTP/protobuf). You can [run
153//! Prometheus](https://prometheus.io/docs/prometheus/latest/installation/) with
154//! the following command:
155//!
156//! ```shell
157//! docker run -p 9090:9090 -v ./prometheus.yml:/etc/prometheus/prometheus.yml prom/prometheus --config.file=/etc/prometheus/prometheus.yml --web.enable-otlp-receiver
158//! ```
159//!
160//! (An empty prometheus.yml file is sufficient for this example.)
161//!
162//! Modify your application to export metrics via OTLP:
163//!
164//! ```no_run
165//! # #[cfg(all(feature = "metrics", feature = "http-proto"))]
166//! # {
167//! use opentelemetry::global;
168//! use opentelemetry::metrics::Meter;
169//! use opentelemetry::KeyValue;
170//! use opentelemetry_otlp::Protocol;
171//! use opentelemetry_otlp::WithExportConfig;
172//!
173//! fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync + 'static>> {
174//!     // Initialize OTLP exporter using HTTP binary protocol
175//!     let exporter = opentelemetry_otlp::MetricExporter::builder()
176//!         .with_http()
177//!         .with_protocol(Protocol::HttpBinary)
178//!         .with_endpoint("http://localhost:9090/api/v1/otlp/v1/metrics")
179//!         .build()?;
180//!
181//!     // Create a meter provider with the OTLP Metric exporter
182//!     let meter_provider = opentelemetry_sdk::metrics::SdkMeterProvider::builder()
183//!         .with_periodic_exporter(exporter)
184//!         .build();
185//!     global::set_meter_provider(meter_provider.clone());
186//!
187//!     // Get a meter
188//!     let meter = global::meter("my_meter");
189//!
190//!     // Create a metric
191//!     let counter = meter.u64_counter("my_counter").build();
192//!     counter.add(1, &[KeyValue::new("key", "value")]);
193//!
194//!     // Shutdown the meter provider. This will trigger an export of all metrics.
195//!     meter_provider.shutdown()?;
196//!
197//!     Ok(())
198//! # }
199//! }
200//! ```
201//!
202//! After running your application configured with the OTLP exporter, view metrics at:
203//! `http://localhost:9090`
204//! ## Show Logs, Metrics too (TODO)
205//!
206//! ## Performance
207//!
208//! For optimal performance, a batch exporting processor is recommended as the simple
209//! processor will export each span synchronously on dropping, and is only good
210//! for test/debug purposes.
211//!
212//! ```toml
213//! [dependencies]
214//! opentelemetry-otlp = { version = "*", features = ["grpc-tonic"] }
215//! ```
216//!
217//! ```no_run
218//! # #[cfg(all(feature = "trace", feature = "grpc-tonic"))]
219//! # {
220//! use opentelemetry::global;
221//! use opentelemetry::trace::Tracer;
222//!
223//! fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync + 'static>> {
224//!     // First, create a OTLP exporter builder. Configure it as you need.
225//!     let otlp_exporter = opentelemetry_otlp::SpanExporter::builder().with_tonic().build()?;
226//!     // Then pass it into provider builder
227//!     let _ = opentelemetry_sdk::trace::SdkTracerProvider::builder()
228//!         .with_batch_exporter(otlp_exporter)
229//!         .build();
230//!     let tracer = global::tracer("my_tracer");
231//!     tracer.in_span("doing_work", |cx| {
232//!         // Traced app logic here...
233//!     });
234//!
235//!     Ok(())
236//!   # }
237//! }
238//! ```
239//!
240//! [`tokio`]: https://tokio.rs
241//!
242//! # Feature Flags
243//! The following feature flags can enable exporters for different telemetry signals:
244//!
245//! * `trace`: Includes the trace exporters.
246//! * `metrics`: Includes the metrics exporters.
247//! * `logs`: Includes the logs exporters.
248//!
249//! The following feature flags generate additional code and types:
250//! * `serialize`: Enables serialization support for type defined in this crate via `serde`.
251//!
252//! The following feature flags offer additional configurations on gRPC:
253//!
254//! For users using `tonic` as grpc layer:
255//! * `grpc-tonic`: Use `tonic` as grpc layer.
256//! * `gzip-tonic`: Use gzip compression for `tonic` grpc layer.
257//! * `zstd-tonic`: Use zstd compression for `tonic` grpc layer.
258//! * `tls-roots`: Adds system trust roots to rustls-based gRPC clients using the rustls-native-certs crate
259//! * `tls-webpki-roots`: Embeds Mozilla's trust roots to rustls-based gRPC clients using the webpki-roots crate
260//!
261//! The following feature flags offer additional configurations on http:
262//!
263//! * `http-proto`: Use http as transport layer, protobuf as body format. This feature is enabled by default.
264//! * `reqwest-blocking-client`: Use reqwest blocking http client. This feature is enabled by default.
265//! * `reqwest-client`: Use reqwest http client.
266//! * `reqwest-rustls`: Use reqwest with TLS with system trust roots via `rustls-native-certs` crate.
267//! * `reqwest-rustls-webpki-roots`: Use reqwest with TLS with Mozilla's trust roots via `webpki-roots` crate.
268//!
269//! # Kitchen Sink Full Configuration
270//!
271//! Example showing how to override all configuration options.
272//!
273//! Generally there are two parts of configuration. One is the exporter, the other is the provider.
274//! Users can configure the exporter using [SpanExporter::builder()] for traces,
275//! and [MetricExporter::builder()] + [opentelemetry_sdk::metrics::PeriodicReader::builder()] for metrics.
276//! Once you have an exporter, you can add it to either a [opentelemetry_sdk::trace::SdkTracerProvider::builder()] for traces,
277//! or [opentelemetry_sdk::metrics::SdkMeterProvider::builder()] for metrics.
278//!
279//! ```no_run
280//! use opentelemetry::{global, KeyValue, trace::Tracer};
281//! use opentelemetry_sdk::{trace::{self, RandomIdGenerator, Sampler}, Resource};
282//! # #[cfg(feature = "metrics")]
283//! use opentelemetry_sdk::metrics::Temporality;
284//! use opentelemetry_otlp::{Protocol, WithExportConfig, WithTonicConfig};
285//! use std::time::Duration;
286//! # #[cfg(feature = "grpc-tonic")]
287//! use tonic::metadata::*;
288//!
289//! fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync + 'static>> {
290//!     # #[cfg(all(feature = "trace", feature = "grpc-tonic"))]
291//!     # let tracer = {
292//!     let mut map = MetadataMap::with_capacity(3);
293//!
294//!     map.insert("x-host", "example.com".parse().unwrap());
295//!     map.insert("x-number", "123".parse().unwrap());
296//!     map.insert_bin("trace-proto-bin", MetadataValue::from_bytes(b"[binary data]"));
297//!     let exporter = opentelemetry_otlp::SpanExporter::builder()
298//!         .with_tonic()
299//!         .with_endpoint("http://localhost:4317")
300//!         .with_timeout(Duration::from_secs(3))
301//!         .with_metadata(map)
302//!         .build()?;
303//!
304//!     let tracer_provider = opentelemetry_sdk::trace::SdkTracerProvider::builder()
305//!         .with_batch_exporter(exporter)
306//!         .with_sampler(Sampler::AlwaysOn)
307//!         .with_id_generator(RandomIdGenerator::default())
308//!         .with_max_events_per_span(64)
309//!         .with_max_attributes_per_span(16)
310//!         .with_resource(Resource::builder_empty().with_attributes([KeyValue::new("service.name", "example")]).build())
311//!         .build();
312//!     global::set_tracer_provider(tracer_provider.clone());
313//!     let tracer = global::tracer("tracer-name");
314//!         # tracer
315//!     # };
316//!
317//!     # #[cfg(all(feature = "metrics", feature = "grpc-tonic"))]
318//!     # {
319//!     let exporter = opentelemetry_otlp::MetricExporter::builder()
320//!        .with_tonic()
321//!        .with_endpoint("http://localhost:4318/v1/metrics")
322//!        .with_protocol(Protocol::Grpc)
323//!        .with_timeout(Duration::from_secs(3))
324//!        .build()
325//!        .unwrap();
326//!
327//!    let provider = opentelemetry_sdk::metrics::SdkMeterProvider::builder()
328//!         .with_periodic_exporter(exporter)
329//!         .with_resource(Resource::builder_empty().with_attributes([KeyValue::new("service.name", "example")]).build())
330//!         .build();
331//!     # }
332//!
333//! # #[cfg(all(feature = "trace", feature = "grpc-tonic"))]
334//! # {
335//!     tracer.in_span("doing_work", |cx| {
336//!         // Traced app logic here...
337//!     });
338//! # }
339//!
340//!     Ok(())
341//! }
342//! ```
343#![warn(
344    future_incompatible,
345    missing_debug_implementations,
346    missing_docs,
347    nonstandard_style,
348    rust_2018_idioms,
349    unreachable_pub,
350    unused
351)]
352#![allow(elided_lifetimes_in_paths)]
353#![cfg_attr(
354    docsrs,
355    feature(doc_cfg, doc_auto_cfg),
356    deny(rustdoc::broken_intra_doc_links)
357)]
358#![cfg_attr(test, deny(warnings))]
359
360mod exporter;
361#[cfg(feature = "logs")]
362#[cfg(any(feature = "http-proto", feature = "http-json", feature = "grpc-tonic"))]
363mod logs;
364#[cfg(feature = "metrics")]
365#[cfg(any(feature = "http-proto", feature = "http-json", feature = "grpc-tonic"))]
366mod metric;
367#[cfg(feature = "trace")]
368#[cfg(any(feature = "http-proto", feature = "http-json", feature = "grpc-tonic"))]
369mod span;
370
371pub use crate::exporter::Compression;
372pub use crate::exporter::ExportConfig;
373pub use crate::exporter::ExporterBuildError;
374#[cfg(feature = "trace")]
375#[cfg(any(feature = "http-proto", feature = "http-json", feature = "grpc-tonic"))]
376pub use crate::span::{
377    SpanExporter, SpanExporterBuilder, OTEL_EXPORTER_OTLP_TRACES_COMPRESSION,
378    OTEL_EXPORTER_OTLP_TRACES_ENDPOINT, OTEL_EXPORTER_OTLP_TRACES_HEADERS,
379    OTEL_EXPORTER_OTLP_TRACES_TIMEOUT,
380};
381
382#[cfg(feature = "metrics")]
383#[cfg(any(feature = "http-proto", feature = "http-json", feature = "grpc-tonic"))]
384pub use crate::metric::{
385    MetricExporter, MetricExporterBuilder, OTEL_EXPORTER_OTLP_METRICS_COMPRESSION,
386    OTEL_EXPORTER_OTLP_METRICS_ENDPOINT, OTEL_EXPORTER_OTLP_METRICS_HEADERS,
387    OTEL_EXPORTER_OTLP_METRICS_TIMEOUT,
388};
389
390#[cfg(feature = "logs")]
391#[cfg(any(feature = "http-proto", feature = "http-json", feature = "grpc-tonic"))]
392pub use crate::logs::{
393    LogExporter, LogExporterBuilder, OTEL_EXPORTER_OTLP_LOGS_COMPRESSION,
394    OTEL_EXPORTER_OTLP_LOGS_ENDPOINT, OTEL_EXPORTER_OTLP_LOGS_HEADERS,
395    OTEL_EXPORTER_OTLP_LOGS_TIMEOUT,
396};
397
398#[cfg(any(feature = "http-proto", feature = "http-json"))]
399pub use crate::exporter::http::{HasHttpConfig, WithHttpConfig};
400
401#[cfg(feature = "grpc-tonic")]
402pub use crate::exporter::tonic::{HasTonicConfig, WithTonicConfig};
403
404pub use crate::exporter::{
405    HasExportConfig, WithExportConfig, OTEL_EXPORTER_OTLP_COMPRESSION, OTEL_EXPORTER_OTLP_ENDPOINT,
406    OTEL_EXPORTER_OTLP_ENDPOINT_DEFAULT, OTEL_EXPORTER_OTLP_HEADERS, OTEL_EXPORTER_OTLP_PROTOCOL,
407    OTEL_EXPORTER_OTLP_PROTOCOL_DEFAULT, OTEL_EXPORTER_OTLP_TIMEOUT,
408    OTEL_EXPORTER_OTLP_TIMEOUT_DEFAULT,
409};
410
411/// Type to indicate the builder does not have a client set.
412#[derive(Debug, Default, Clone)]
413pub struct NoExporterBuilderSet;
414
415/// Type to hold the [TonicExporterBuilder] and indicate it has been set.
416///
417/// Allowing access to [TonicExporterBuilder] specific configuration methods.
418#[cfg(feature = "grpc-tonic")]
419// This is for clippy to work with only the grpc-tonic feature enabled
420#[allow(unused)]
421#[derive(Debug, Default)]
422pub struct TonicExporterBuilderSet(TonicExporterBuilder);
423
424/// Type to hold the [HttpExporterBuilder] and indicate it has been set.
425///
426/// Allowing access to [HttpExporterBuilder] specific configuration methods.
427#[cfg(any(feature = "http-proto", feature = "http-json"))]
428#[derive(Debug, Default)]
429pub struct HttpExporterBuilderSet(HttpExporterBuilder);
430
431#[cfg(any(feature = "http-proto", feature = "http-json"))]
432pub use crate::exporter::http::HttpExporterBuilder;
433
434#[cfg(feature = "grpc-tonic")]
435pub use crate::exporter::tonic::{TonicConfig, TonicExporterBuilder};
436
437#[cfg(feature = "serialize")]
438use serde::{Deserialize, Serialize};
439
440/// The communication protocol to use when exporting data.
441#[cfg_attr(feature = "serialize", derive(Deserialize, Serialize))]
442#[derive(Clone, Copy, Debug, Eq, PartialEq)]
443pub enum Protocol {
444    /// GRPC protocol
445    Grpc,
446    /// HTTP protocol with binary protobuf
447    HttpBinary,
448    /// HTTP protocol with JSON payload
449    HttpJson,
450}
451
452#[derive(Debug, Default)]
453#[doc(hidden)]
454/// Placeholder type when no exporter pipeline has been configured in telemetry pipeline.
455pub struct NoExporterConfig(());
456
457/// Re-exported types from the `tonic` crate.
458#[cfg(feature = "grpc-tonic")]
459pub mod tonic_types {
460    /// Re-exported types from `tonic::metadata`.
461    pub mod metadata {
462        #[doc(no_inline)]
463        pub use tonic::metadata::MetadataMap;
464    }
465
466    /// Re-exported types from `tonic::transport`.
467    #[cfg(feature = "tls")]
468    pub mod transport {
469        #[doc(no_inline)]
470        pub use tonic::transport::{Certificate, ClientTlsConfig, Identity};
471    }
472}