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 only support sending telemetry in OTLP
11//! via grpc and http (in binary format). Supports for other format and protocol
12//! will be added in the future. The details of what's currently offering in this
13//! crate can be found in this doc.
14//!
15//! # Quickstart
16//!
17//! First make sure you have a running version of the opentelemetry collector
18//! you want to send data to:
19//!
20//! ```shell
21//! $ docker run -p 4317:4317 otel/opentelemetry-collector:latest
22//! ```
23//!
24//! Then install a new pipeline with the recommended defaults to start exporting
25//! telemetry. You will have to build a OTLP exporter first.
26//!
27//! Exporting pipelines can be started with `new_pipeline().tracing()` and
28//! `new_pipeline().metrics()`, and `new_pipeline().logging()` respectively for
29//! traces, metrics and logs.
30//!
31//! ```no_run
32//! # #[cfg(all(feature = "trace", feature = "grpc-tonic"))]
33//! # {
34//! use opentelemetry::global;
35//! use opentelemetry::trace::Tracer;
36//!
37//! fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync + 'static>> {
38//!     // First, create a OTLP exporter builder. Configure it as you need.
39//!     let otlp_exporter = opentelemetry_otlp::new_exporter().tonic();
40//!     // Then pass it into pipeline builder
41//!     let _ = opentelemetry_otlp::new_pipeline()
42//!         .tracing()
43//!         .with_exporter(otlp_exporter)
44//!         .install_simple()?;
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 exporter is recommended as the simple
58//! exporter will export each span synchronously on dropping. You can enable the
59//! [`rt-tokio`], [`rt-tokio-current-thread`] or [`rt-async-std`] features and
60//! specify a runtime on the pipeline builder to have a batch exporter
61//! configured for you automatically.
62//!
63//! ```toml
64//! [dependencies]
65//! opentelemetry_sdk = { version = "*", features = ["async-std"] }
66//! opentelemetry-otlp = { version = "*", features = ["grpc-tonic"] }
67//! ```
68//!
69//! ```no_run
70//! # #[cfg(all(feature = "trace", feature = "grpc-tonic"))]
71//! # {
72//! # fn main() -> Result<(), opentelemetry::trace::TraceError> {
73//! let tracer = opentelemetry_otlp::new_pipeline()
74//!     .tracing()
75//!     .with_exporter(opentelemetry_otlp::new_exporter().tonic())
76//!     .install_batch(opentelemetry_sdk::runtime::AsyncStd)?;
77//! # Ok(())
78//! # }
79//! # }
80//! ```
81//!
82//! [`tokio`]: https://tokio.rs
83//! [`async-std`]: https://async.rs
84//!
85//! # Feature Flags
86//! The following feature flags can enable exporters for different telemetry signals:
87//!
88//! * `trace`: Includes the trace exporters (enabled by default).
89//! * `metrics`: Includes the metrics exporters.
90//! * `logs`: Includes the logs exporters.
91//!
92//! The following feature flags generate additional code and types:
93//! * `serialize`: Enables serialization support for type defined in this create via `serde`.
94//!
95//! The following feature flags offer additional configurations on gRPC:
96//!
97//! For users uses `tonic` as grpc layer:
98//! * `grpc-tonic`: Use `tonic` as grpc layer. This is enabled by default.
99//! * `gzip-tonic`: Use gzip compression for `tonic` grpc layer.
100//! * `tls-tonic`: Enable TLS.
101//! * `tls-roots`: Adds system trust roots to rustls-based gRPC clients using the rustls-native-certs crate
102//! * `tls-webkpi-roots`: Embeds Mozilla's trust roots to rustls-based gRPC clients using the webkpi-roots crate
103//!
104//! The following feature flags offer additional configurations on http:
105//!
106//! * `http-proto`: Use http as transport layer, protobuf as body format.
107//! * `reqwest-blocking-client`: Use reqwest blocking http client.
108//! * `reqwest-client`: Use reqwest http client.
109//! * `reqwest-rustls`: Use reqwest with TLS with system trust roots via `rustls-native-certs` crate.
110//! * `reqwest-rustls-webkpi-roots`: Use reqwest with TLS with Mozilla's trust roots via `webkpi-roots` crate.
111//!
112//! # Kitchen Sink Full Configuration
113//!
114//! Example showing how to override all configuration options.
115//!
116//! Generally there are two parts of configuration. One is metrics config
117//! or tracing config. Users can config it via [`OtlpTracePipeline`]
118//! or [`OtlpMetricPipeline`]. The other is exporting configuration.
119//! Users can set those configurations using [`OtlpExporterPipeline`] based
120//! on the choice of exporters.
121//!
122//! ```no_run
123//! use opentelemetry::{global, KeyValue, trace::Tracer};
124//! use opentelemetry_sdk::{trace::{self, RandomIdGenerator, Sampler}, Resource};
125//! # #[cfg(feature = "metrics")]
126//! use opentelemetry_sdk::metrics::reader::{DefaultAggregationSelector, DefaultTemporalitySelector};
127//! use opentelemetry_otlp::{Protocol, WithExportConfig, ExportConfig};
128//! use std::time::Duration;
129//! # #[cfg(feature = "grpc-tonic")]
130//! use tonic::metadata::*;
131//!
132//! fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync + 'static>> {
133//!     # #[cfg(all(feature = "trace", feature = "grpc-tonic"))]
134//!     # let tracer = {
135//!     let mut map = MetadataMap::with_capacity(3);
136//!
137//!     map.insert("x-host", "example.com".parse().unwrap());
138//!     map.insert("x-number", "123".parse().unwrap());
139//!     map.insert_bin("trace-proto-bin", MetadataValue::from_bytes(b"[binary data]"));
140//!
141//!     let tracer_provider = opentelemetry_otlp::new_pipeline()
142//!         .tracing()
143//!         .with_exporter(
144//!             opentelemetry_otlp::new_exporter()
145//!             .tonic()
146//!             .with_endpoint("http://localhost:4317")
147//!             .with_timeout(Duration::from_secs(3))
148//!             .with_metadata(map)
149//!          )
150//!         .with_trace_config(
151//!             trace::config()
152//!                 .with_sampler(Sampler::AlwaysOn)
153//!                 .with_id_generator(RandomIdGenerator::default())
154//!                 .with_max_events_per_span(64)
155//!                 .with_max_attributes_per_span(16)
156//!                 .with_max_events_per_span(16)
157//!                 .with_resource(Resource::new(vec![KeyValue::new("service.name", "example")])),
158//!         )
159//!         .install_batch(opentelemetry_sdk::runtime::Tokio)?;
160//!         global::set_tracer_provider(tracer_provider);
161//!         let tracer = global::tracer("tracer-name");
162//!         # tracer
163//!     # };
164//!
165//!     # #[cfg(all(feature = "metrics", feature = "grpc-tonic"))]
166//!     # {
167//!     let export_config = ExportConfig {
168//!         endpoint: "http://localhost:4317".to_string(),
169//!         timeout: Duration::from_secs(3),
170//!         protocol: Protocol::Grpc
171//!     };
172//!
173//!     let meter = opentelemetry_otlp::new_pipeline()
174//!         .metrics(opentelemetry_sdk::runtime::Tokio)
175//!         .with_exporter(
176//!             opentelemetry_otlp::new_exporter()
177//!                 .tonic()
178//!                 .with_export_config(export_config),
179//!                 // can also config it using with_* functions like the tracing part above.
180//!         )
181//!         .with_resource(Resource::new(vec![KeyValue::new("service.name", "example")]))
182//!         .with_period(Duration::from_secs(3))
183//!         .with_timeout(Duration::from_secs(10))
184//!         .with_aggregation_selector(DefaultAggregationSelector::new())
185//!         .with_temporality_selector(DefaultTemporalitySelector::new())
186//!         .build();
187//!     # }
188//!
189//! # #[cfg(all(feature = "trace", feature = "grpc-tonic"))]
190//! # {
191//!     tracer.in_span("doing_work", |cx| {
192//!         // Traced app logic here...
193//!     });
194//! # }
195//!
196//!     Ok(())
197//! }
198//! ```
199#![warn(
200    future_incompatible,
201    missing_debug_implementations,
202    missing_docs,
203    nonstandard_style,
204    rust_2018_idioms,
205    unreachable_pub,
206    unused
207)]
208#![allow(elided_lifetimes_in_paths)]
209#![cfg_attr(
210    docsrs,
211    feature(doc_cfg, doc_auto_cfg),
212    deny(rustdoc::broken_intra_doc_links)
213)]
214#![cfg_attr(test, deny(warnings))]
215
216mod exporter;
217#[cfg(feature = "logs")]
218mod logs;
219#[cfg(feature = "metrics")]
220mod metric;
221#[cfg(feature = "trace")]
222mod span;
223
224pub use crate::exporter::Compression;
225pub use crate::exporter::ExportConfig;
226#[cfg(feature = "trace")]
227pub use crate::span::{
228    OtlpTracePipeline, SpanExporter, SpanExporterBuilder, OTEL_EXPORTER_OTLP_TRACES_COMPRESSION,
229    OTEL_EXPORTER_OTLP_TRACES_ENDPOINT, OTEL_EXPORTER_OTLP_TRACES_HEADERS,
230    OTEL_EXPORTER_OTLP_TRACES_TIMEOUT,
231};
232
233#[cfg(feature = "metrics")]
234pub use crate::metric::{
235    MetricsExporter, MetricsExporterBuilder, OtlpMetricPipeline,
236    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")]
241pub use crate::logs::{
242    LogExporter, LogExporterBuilder, OtlpLogPipeline, OTEL_EXPORTER_OTLP_LOGS_COMPRESSION,
243    OTEL_EXPORTER_OTLP_LOGS_ENDPOINT, OTEL_EXPORTER_OTLP_LOGS_HEADERS,
244    OTEL_EXPORTER_OTLP_LOGS_TIMEOUT,
245};
246
247pub use crate::exporter::{
248    HasExportConfig, WithExportConfig, OTEL_EXPORTER_OTLP_COMPRESSION, OTEL_EXPORTER_OTLP_ENDPOINT,
249    OTEL_EXPORTER_OTLP_ENDPOINT_DEFAULT, OTEL_EXPORTER_OTLP_HEADERS, OTEL_EXPORTER_OTLP_PROTOCOL,
250    OTEL_EXPORTER_OTLP_PROTOCOL_DEFAULT, OTEL_EXPORTER_OTLP_TIMEOUT,
251    OTEL_EXPORTER_OTLP_TIMEOUT_DEFAULT,
252};
253
254use opentelemetry_sdk::export::ExportError;
255
256#[cfg(any(feature = "http-proto", feature = "http-json"))]
257pub use crate::exporter::http::HttpExporterBuilder;
258
259#[cfg(feature = "grpc-tonic")]
260pub use crate::exporter::tonic::{TonicConfig, TonicExporterBuilder};
261
262#[cfg(feature = "serialize")]
263use serde::{Deserialize, Serialize};
264
265/// General builder for both tracing and metrics.
266#[derive(Debug)]
267pub struct OtlpPipeline;
268
269/// Build a OTLP metrics or tracing exporter builder. See functions below to understand
270/// what's currently supported.
271#[derive(Debug)]
272pub struct OtlpExporterPipeline;
273
274impl OtlpExporterPipeline {
275    /// Use tonic as grpc layer, return a `TonicExporterBuilder` to config tonic and build the exporter.
276    ///
277    /// This exporter can be used in both `tracing` and `metrics` pipeline.
278    #[cfg(feature = "grpc-tonic")]
279    pub fn tonic(self) -> TonicExporterBuilder {
280        TonicExporterBuilder::default()
281    }
282
283    /// Use HTTP as transport layer, return a `HttpExporterBuilder` to config the http transport
284    /// and build the exporter.
285    ///
286    /// This exporter can be used in both `tracing` and `metrics` pipeline.
287    #[cfg(any(feature = "http-proto", feature = "http-json"))]
288    pub fn http(self) -> HttpExporterBuilder {
289        HttpExporterBuilder::default()
290    }
291}
292
293/// Create a new pipeline builder with the recommended configuration.
294///
295/// ## Examples
296///
297/// ```no_run
298/// fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync + 'static>> {
299///     # #[cfg(feature = "trace")]
300///     let tracing_builder = opentelemetry_otlp::new_pipeline().tracing();
301///
302///     Ok(())
303/// }
304/// ```
305pub fn new_pipeline() -> OtlpPipeline {
306    OtlpPipeline
307}
308
309/// Create a builder to build OTLP metrics exporter or tracing exporter.
310pub fn new_exporter() -> OtlpExporterPipeline {
311    OtlpExporterPipeline
312}
313
314/// Wrap type for errors from this crate.
315#[derive(thiserror::Error, Debug)]
316pub enum Error {
317    /// Wrap error from [`tonic::transport::Error`]
318    #[cfg(feature = "grpc-tonic")]
319    #[error("transport error {0}")]
320    Transport(#[from] tonic::transport::Error),
321
322    /// Wrap the [`tonic::codegen::http::uri::InvalidUri`] error
323    #[cfg(any(feature = "grpc-tonic", feature = "http-proto", feature = "http-json"))]
324    #[error("invalid URI {0}")]
325    InvalidUri(#[from] http::uri::InvalidUri),
326
327    /// Wrap type for [`tonic::Status`]
328    #[cfg(feature = "grpc-tonic")]
329    #[error("the grpc server returns error ({code}): {message}")]
330    Status {
331        /// grpc status code
332        code: tonic::Code,
333        /// error message
334        message: String,
335    },
336
337    /// Http requests failed because no http client is provided.
338    #[cfg(any(feature = "http-proto", feature = "http-json"))]
339    #[error(
340        "no http client, you must select one from features or provide your own implementation"
341    )]
342    NoHttpClient,
343
344    /// Http requests failed.
345    #[cfg(any(feature = "http-proto", feature = "http-json"))]
346    #[error("http request failed with {0}")]
347    RequestFailed(#[from] opentelemetry_http::HttpError),
348
349    /// The provided value is invalid in HTTP headers.
350    #[cfg(any(feature = "grpc-tonic", feature = "http-proto", feature = "http-json"))]
351    #[error("http header value error {0}")]
352    InvalidHeaderValue(#[from] http::header::InvalidHeaderValue),
353
354    /// The provided name is invalid in HTTP headers.
355    #[cfg(any(feature = "grpc-tonic", feature = "http-proto", feature = "http-json"))]
356    #[error("http header name error {0}")]
357    InvalidHeaderName(#[from] http::header::InvalidHeaderName),
358
359    /// Prost encode failed
360    #[cfg(any(
361        feature = "http-proto",
362        all(feature = "http-json", not(feature = "trace"))
363    ))]
364    #[error("prost encoding error {0}")]
365    EncodeError(#[from] prost::EncodeError),
366
367    /// The lock in exporters has been poisoned.
368    #[cfg(feature = "metrics")]
369    #[error("the lock of the {0} has been poisoned")]
370    PoisonedLock(&'static str),
371
372    /// Unsupported compression algorithm.
373    #[error("unsupported compression algorithm '{0}'")]
374    UnsupportedCompressionAlgorithm(String),
375}
376
377#[cfg(feature = "grpc-tonic")]
378impl From<tonic::Status> for Error {
379    fn from(status: tonic::Status) -> Error {
380        Error::Status {
381            code: status.code(),
382            message: {
383                if !status.message().is_empty() {
384                    let mut result = ", detailed error message: ".to_string() + status.message();
385                    if status.code() == tonic::Code::Unknown {
386                        let source = (&status as &dyn std::error::Error)
387                            .source()
388                            .map(|e| format!("{:?}", e));
389                        result.push(' ');
390                        result.push_str(source.unwrap_or_default().as_ref());
391                    }
392                    result
393                } else {
394                    String::new()
395                }
396            },
397        }
398    }
399}
400
401impl ExportError for Error {
402    fn exporter_name(&self) -> &'static str {
403        "otlp"
404    }
405}
406
407/// The communication protocol to use when exporting data.
408#[cfg_attr(feature = "serialize", derive(Deserialize, Serialize))]
409#[derive(Clone, Copy, Debug, Eq, PartialEq)]
410pub enum Protocol {
411    /// GRPC protocol
412    Grpc,
413    /// HTTP protocol with binary protobuf
414    HttpBinary,
415    /// HTTP protocol with JSON payload
416    HttpJson,
417}
418
419#[derive(Debug, Default)]
420#[doc(hidden)]
421/// Placeholder type when no exporter pipeline has been configured in telemetry pipeline.
422pub struct NoExporterConfig(());