opentelemetry_otlp/
lib.rs

1//! The OTLP Exporter supports exporting logs, metrics and traces in the OTLP
2//! format to the OpenTelemetry collector or other compatible backend.
3//!
4//! The OpenTelemetry Collector offers a vendor-agnostic implementation on how
5//! to receive, process, and export telemetry data. In addition, it removes
6//! the need to run, operate, and maintain multiple agents/collectors in
7//! order to support open-source telemetry data formats (e.g. Jaeger,
8//! Prometheus, etc.) sending to multiple open-source or commercial back-ends.
9//!
10//! Currently, this crate supports sending telemetry in OTLP
11//! via gRPC and http (binary and json).
12//!
13//! # Quickstart
14//!
15//! First make sure you have a running version of the opentelemetry collector
16//! you want to send data to:
17//!
18//! ```shell
19//! $ docker run -p 4317:4317 otel/opentelemetry-collector:latest
20//! ```
21//!
22//! Then create a new `Exporter`, and `Provider` with the recommended defaults to start exporting
23//! telemetry.
24//!
25//! You will have to build a OTLP exporter first. Create the correct exporter based on the signal
26//! you are looking to export `SpanExporter::builder()`, `MetricExporter::builder()`,
27//! `LogExporter::builder()` respectively for traces, metrics, and logs.
28//!
29//! Once you have the exporter, you can create your `Provider` by starting with `TracerProvider::builder()`,
30//! `SdkMeterProvider::builder()`, and `LoggerProvider::builder()` respectively for traces, metrics, and logs.
31//!
32//! ```no_run
33//! # #[cfg(all(feature = "trace", feature = "grpc-tonic"))]
34//! # {
35//! use opentelemetry::global;
36//! use opentelemetry::trace::Tracer;
37//!
38//! fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync + 'static>> {
39//!     // First, create a OTLP exporter builder. Configure it as you need.
40//!     let otlp_exporter = opentelemetry_otlp::SpanExporter::builder().with_tonic().build()?;
41//!     // Then pass it into provider builder
42//!     let _ = opentelemetry_sdk::trace::SdkTracerProvider::builder()
43//!         .with_simple_exporter(otlp_exporter)
44//!         .build();
45//!     let tracer = global::tracer("my_tracer");
46//!     tracer.in_span("doing_work", |cx| {
47//!         // Traced app logic here...
48//!     });
49//!
50//!     Ok(())
51//!   # }
52//! }
53//! ```
54//!
55//! ## Performance
56//!
57//! For optimal performance, a batch exporting processor is recommended as the simple
58//! processor will export each span synchronously on dropping, and is only good
59//! for test/debug purposes.
60//!
61//! ```toml
62//! [dependencies]
63//! opentelemetry-otlp = { version = "*", features = ["grpc-tonic"] }
64//! ```
65//!
66//! ```no_run
67//! # #[cfg(all(feature = "trace", feature = "grpc-tonic"))]
68//! # {
69//! use opentelemetry::global;
70//! use opentelemetry::trace::Tracer;
71//!
72//! fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync + 'static>> {
73//!     // First, create a OTLP exporter builder. Configure it as you need.
74//!     let otlp_exporter = opentelemetry_otlp::SpanExporter::builder().with_tonic().build()?;
75//!     // Then pass it into provider builder
76//!     let _ = opentelemetry_sdk::trace::SdkTracerProvider::builder()
77//!         .with_batch_exporter(otlp_exporter)
78//!         .build();
79//!     let tracer = global::tracer("my_tracer");
80//!     tracer.in_span("doing_work", |cx| {
81//!         // Traced app logic here...
82//!     });
83//!
84//!     Ok(())
85//!   # }
86//! }
87//! ```
88//!
89//! [`tokio`]: https://tokio.rs
90//! [`async-std`]: https://async.rs
91//!
92//! # Feature Flags
93//! The following feature flags can enable exporters for different telemetry signals:
94//!
95//! * `trace`: Includes the trace exporters.
96//! * `metrics`: Includes the metrics exporters.
97//! * `logs`: Includes the logs exporters.
98//!
99//! The following feature flags generate additional code and types:
100//! * `serialize`: Enables serialization support for type defined in this create via `serde`.
101//!
102//! The following feature flags offer additional configurations on gRPC:
103//!
104//! For users using `tonic` as grpc layer:
105//! * `grpc-tonic`: Use `tonic` as grpc layer.
106//! * `gzip-tonic`: Use gzip compression for `tonic` grpc layer.
107//! * `zstd-tonic`: Use zstd compression for `tonic` grpc layer.
108//! * `tls-roots`: Adds system trust roots to rustls-based gRPC clients using the rustls-native-certs crate
109//! * `tls-webpki-roots`: Embeds Mozilla's trust roots to rustls-based gRPC clients using the webpki-roots crate
110//!
111//! The following feature flags offer additional configurations on http:
112//!
113//! * `http-proto`: Use http as transport layer, protobuf as body format. This feature is enabled by default.
114//! * `reqwest-blocking-client`: Use reqwest blocking http client. This feature is enabled by default.
115//! * `reqwest-client`: Use reqwest http client.
116//! * `reqwest-rustls`: Use reqwest with TLS with system trust roots via `rustls-native-certs` crate.
117//! * `reqwest-rustls-webpki-roots`: Use reqwest with TLS with Mozilla's trust roots via `webpki-roots` crate.
118//!
119//! # Kitchen Sink Full Configuration
120//!
121//! Example showing how to override all configuration options.
122//!
123//! Generally there are two parts of configuration. One is the exporter, the other is the provider.
124//! Users can configure the exporter using [SpanExporter::builder()] for traces,
125//! and [MetricExporter::builder()] + [opentelemetry_sdk::metrics::PeriodicReader::builder()] for metrics.
126//! Once you have an exporter, you can add it to either a [opentelemetry_sdk::trace::SdkTracerProvider::builder()] for traces,
127//! or [opentelemetry_sdk::metrics::SdkMeterProvider::builder()] for metrics.
128//!
129//! ```no_run
130//! use opentelemetry::{global, KeyValue, trace::Tracer};
131//! use opentelemetry_sdk::{trace::{self, RandomIdGenerator, Sampler}, Resource};
132//! # #[cfg(feature = "metrics")]
133//! use opentelemetry_sdk::metrics::Temporality;
134//! use opentelemetry_otlp::{Protocol, WithExportConfig, WithTonicConfig};
135//! use std::time::Duration;
136//! # #[cfg(feature = "grpc-tonic")]
137//! use tonic::metadata::*;
138//!
139//! fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync + 'static>> {
140//!     # #[cfg(all(feature = "trace", feature = "grpc-tonic"))]
141//!     # let tracer = {
142//!     let mut map = MetadataMap::with_capacity(3);
143//!
144//!     map.insert("x-host", "example.com".parse().unwrap());
145//!     map.insert("x-number", "123".parse().unwrap());
146//!     map.insert_bin("trace-proto-bin", MetadataValue::from_bytes(b"[binary data]"));
147//!     let exporter = opentelemetry_otlp::SpanExporter::builder()
148//!         .with_tonic()
149//!         .with_endpoint("http://localhost:4317")
150//!         .with_timeout(Duration::from_secs(3))
151//!         .with_metadata(map)
152//!         .build()?;
153//!
154//!     let tracer_provider = opentelemetry_sdk::trace::SdkTracerProvider::builder()
155//!         .with_batch_exporter(exporter)
156//!         .with_config(
157//!             trace::Config::default()
158//!                 .with_sampler(Sampler::AlwaysOn)
159//!                 .with_id_generator(RandomIdGenerator::default())
160//!                 .with_max_events_per_span(64)
161//!                 .with_max_attributes_per_span(16)
162//!                 .with_max_events_per_span(16)
163//!                 .with_resource(Resource::builder_empty().with_attributes([KeyValue::new("service.name", "example")]).build()),
164//!         ).build();
165//!     global::set_tracer_provider(tracer_provider.clone());
166//!     let tracer = global::tracer("tracer-name");
167//!         # tracer
168//!     # };
169//!
170//!     # #[cfg(all(feature = "metrics", feature = "grpc-tonic"))]
171//!     # {
172//!     let exporter = opentelemetry_otlp::MetricExporter::builder()
173//!        .with_tonic()
174//!        .with_endpoint("http://localhost:4318/v1/metrics")
175//!        .with_protocol(Protocol::Grpc)
176//!        .with_timeout(Duration::from_secs(3))
177//!        .build()
178//!        .unwrap();
179//!
180//!    let provider = opentelemetry_sdk::metrics::SdkMeterProvider::builder()
181//!         .with_periodic_exporter(exporter)
182//!         .with_resource(Resource::builder_empty().with_attributes([KeyValue::new("service.name", "example")]).build())
183//!         .build();
184//!     # }
185//!
186//! # #[cfg(all(feature = "trace", feature = "grpc-tonic"))]
187//! # {
188//!     tracer.in_span("doing_work", |cx| {
189//!         // Traced app logic here...
190//!     });
191//! # }
192//!
193//!     Ok(())
194//! }
195//! ```
196#![warn(
197    future_incompatible,
198    missing_debug_implementations,
199    missing_docs,
200    nonstandard_style,
201    rust_2018_idioms,
202    unreachable_pub,
203    unused
204)]
205#![allow(elided_lifetimes_in_paths)]
206#![cfg_attr(
207    docsrs,
208    feature(doc_cfg, doc_auto_cfg),
209    deny(rustdoc::broken_intra_doc_links)
210)]
211#![cfg_attr(test, deny(warnings))]
212
213mod exporter;
214#[cfg(feature = "logs")]
215#[cfg(any(feature = "http-proto", feature = "http-json", feature = "grpc-tonic"))]
216mod logs;
217#[cfg(feature = "metrics")]
218#[cfg(any(feature = "http-proto", feature = "http-json", feature = "grpc-tonic"))]
219mod metric;
220#[cfg(feature = "trace")]
221#[cfg(any(feature = "http-proto", feature = "http-json", feature = "grpc-tonic"))]
222mod span;
223
224pub use crate::exporter::Compression;
225pub use crate::exporter::ExportConfig;
226#[cfg(feature = "trace")]
227#[cfg(any(feature = "http-proto", feature = "http-json", feature = "grpc-tonic"))]
228pub use crate::span::{
229    SpanExporter, OTEL_EXPORTER_OTLP_TRACES_COMPRESSION, OTEL_EXPORTER_OTLP_TRACES_ENDPOINT,
230    OTEL_EXPORTER_OTLP_TRACES_HEADERS, OTEL_EXPORTER_OTLP_TRACES_TIMEOUT,
231};
232
233#[cfg(feature = "metrics")]
234#[cfg(any(feature = "http-proto", feature = "http-json", feature = "grpc-tonic"))]
235pub use crate::metric::{
236    MetricExporter, OTEL_EXPORTER_OTLP_METRICS_COMPRESSION, OTEL_EXPORTER_OTLP_METRICS_ENDPOINT,
237    OTEL_EXPORTER_OTLP_METRICS_HEADERS, OTEL_EXPORTER_OTLP_METRICS_TIMEOUT,
238};
239
240#[cfg(feature = "logs")]
241#[cfg(any(feature = "http-proto", feature = "http-json", feature = "grpc-tonic"))]
242pub use crate::logs::{
243    LogExporter, OTEL_EXPORTER_OTLP_LOGS_COMPRESSION, OTEL_EXPORTER_OTLP_LOGS_ENDPOINT,
244    OTEL_EXPORTER_OTLP_LOGS_HEADERS, OTEL_EXPORTER_OTLP_LOGS_TIMEOUT,
245};
246
247#[cfg(any(feature = "http-proto", feature = "http-json"))]
248pub use crate::exporter::http::{HasHttpConfig, WithHttpConfig};
249
250#[cfg(feature = "grpc-tonic")]
251pub use crate::exporter::tonic::{HasTonicConfig, WithTonicConfig};
252
253pub use crate::exporter::{
254    HasExportConfig, WithExportConfig, OTEL_EXPORTER_OTLP_COMPRESSION, OTEL_EXPORTER_OTLP_ENDPOINT,
255    OTEL_EXPORTER_OTLP_ENDPOINT_DEFAULT, OTEL_EXPORTER_OTLP_HEADERS, OTEL_EXPORTER_OTLP_PROTOCOL,
256    OTEL_EXPORTER_OTLP_PROTOCOL_DEFAULT, OTEL_EXPORTER_OTLP_TIMEOUT,
257    OTEL_EXPORTER_OTLP_TIMEOUT_DEFAULT,
258};
259
260use opentelemetry_sdk::ExportError;
261
262/// Type to indicate the builder does not have a client set.
263#[derive(Debug, Default, Clone)]
264pub struct NoExporterBuilderSet;
265
266/// Type to hold the [TonicExporterBuilder] and indicate it has been set.
267///
268/// Allowing access to [TonicExporterBuilder] specific configuration methods.
269#[cfg(feature = "grpc-tonic")]
270#[derive(Debug, Default)]
271pub struct TonicExporterBuilderSet(TonicExporterBuilder);
272
273/// Type to hold the [HttpExporterBuilder] and indicate it has been set.
274///
275/// Allowing access to [HttpExporterBuilder] specific configuration methods.
276#[cfg(any(feature = "http-proto", feature = "http-json"))]
277#[derive(Debug, Default)]
278pub struct HttpExporterBuilderSet(HttpExporterBuilder);
279
280#[cfg(any(feature = "http-proto", feature = "http-json"))]
281pub use crate::exporter::http::HttpExporterBuilder;
282
283#[cfg(feature = "grpc-tonic")]
284pub use crate::exporter::tonic::{TonicConfig, TonicExporterBuilder};
285
286#[cfg(feature = "serialize")]
287use serde::{Deserialize, Serialize};
288
289/// Wrap type for errors from this crate.
290#[derive(thiserror::Error, Debug)]
291pub enum Error {
292    /// Wrap error from [`tonic::transport::Error`]
293    #[cfg(feature = "grpc-tonic")]
294    #[error("transport error {0}")]
295    Transport(#[from] tonic::transport::Error),
296
297    /// Wrap the [`tonic::codegen::http::uri::InvalidUri`] error
298    #[cfg(any(feature = "grpc-tonic", feature = "http-proto", feature = "http-json"))]
299    #[error("invalid URI {0}")]
300    InvalidUri(#[from] http::uri::InvalidUri),
301
302    /// Wrap type for [`tonic::Status`]
303    #[cfg(feature = "grpc-tonic")]
304    #[error("the grpc server returns error ({code}): {message}")]
305    Status {
306        /// grpc status code
307        code: tonic::Code,
308        /// error message
309        message: String,
310    },
311
312    /// Http requests failed because no http client is provided.
313    #[cfg(any(feature = "http-proto", feature = "http-json"))]
314    #[error(
315        "no http client, you must select one from features or provide your own implementation"
316    )]
317    NoHttpClient,
318
319    /// Http requests failed.
320    #[cfg(any(feature = "http-proto", feature = "http-json"))]
321    #[error("http request failed with {0}")]
322    RequestFailed(#[from] opentelemetry_http::HttpError),
323
324    /// The provided value is invalid in HTTP headers.
325    #[cfg(any(feature = "grpc-tonic", feature = "http-proto", feature = "http-json"))]
326    #[error("http header value error {0}")]
327    InvalidHeaderValue(#[from] http::header::InvalidHeaderValue),
328
329    /// The provided name is invalid in HTTP headers.
330    #[cfg(any(feature = "grpc-tonic", feature = "http-proto", feature = "http-json"))]
331    #[error("http header name error {0}")]
332    InvalidHeaderName(#[from] http::header::InvalidHeaderName),
333
334    /// Prost encode failed
335    #[cfg(any(
336        feature = "http-proto",
337        all(feature = "http-json", not(feature = "trace"))
338    ))]
339    #[error("prost encoding error {0}")]
340    EncodeError(#[from] prost::EncodeError),
341
342    /// The lock in exporters has been poisoned.
343    #[cfg(feature = "metrics")]
344    #[error("the lock of the {0} has been poisoned")]
345    PoisonedLock(&'static str),
346
347    /// Unsupported compression algorithm.
348    #[error("unsupported compression algorithm '{0}'")]
349    UnsupportedCompressionAlgorithm(String),
350
351    /// Feature required to use the specified compression algorithm.
352    #[cfg(any(not(feature = "gzip-tonic"), not(feature = "zstd-tonic")))]
353    #[error("feature '{0}' is required to use the compression algorithm '{1}'")]
354    FeatureRequiredForCompressionAlgorithm(&'static str, Compression),
355}
356
357#[cfg(feature = "grpc-tonic")]
358impl From<tonic::Status> for Error {
359    fn from(status: tonic::Status) -> Error {
360        Error::Status {
361            code: status.code(),
362            message: {
363                if !status.message().is_empty() {
364                    let mut result = ", detailed error message: ".to_string() + status.message();
365                    if status.code() == tonic::Code::Unknown {
366                        let source = (&status as &dyn std::error::Error)
367                            .source()
368                            .map(|e| format!("{:?}", e));
369                        result.push(' ');
370                        result.push_str(source.unwrap_or_default().as_ref());
371                    }
372                    result
373                } else {
374                    String::new()
375                }
376            },
377        }
378    }
379}
380
381impl ExportError for Error {
382    fn exporter_name(&self) -> &'static str {
383        "otlp"
384    }
385}
386
387impl opentelemetry::trace::ExportError for Error {
388    fn exporter_name(&self) -> &'static str {
389        "otlp"
390    }
391}
392
393/// The communication protocol to use when exporting data.
394#[cfg_attr(feature = "serialize", derive(Deserialize, Serialize))]
395#[derive(Clone, Copy, Debug, Eq, PartialEq)]
396pub enum Protocol {
397    /// GRPC protocol
398    Grpc,
399    /// HTTP protocol with binary protobuf
400    HttpBinary,
401    /// HTTP protocol with JSON payload
402    HttpJson,
403}
404
405#[derive(Debug, Default)]
406#[doc(hidden)]
407/// Placeholder type when no exporter pipeline has been configured in telemetry pipeline.
408pub struct NoExporterConfig(());