opentelemetry_otlp/
metric.rs

1//! OTEL metric exporter
2//!
3//! Defines a [MetricExporter] to send metric data to backend via OTLP protocol.
4//!
5
6#[cfg(any(feature = "http-proto", feature = "http-json", feature = "grpc-tonic"))]
7use crate::HasExportConfig;
8
9#[cfg(any(feature = "http-proto", feature = "http-json"))]
10use crate::{exporter::http::HttpExporterBuilder, HasHttpConfig, HttpExporterBuilderSet};
11
12#[cfg(feature = "grpc-tonic")]
13use crate::{exporter::tonic::TonicExporterBuilder, HasTonicConfig, TonicExporterBuilderSet};
14
15use crate::NoExporterBuilderSet;
16
17use async_trait::async_trait;
18use core::fmt;
19use opentelemetry_sdk::error::OTelSdkResult;
20use opentelemetry_sdk::metrics::MetricResult;
21
22use opentelemetry_sdk::metrics::{
23    data::ResourceMetrics, exporter::PushMetricExporter, Temporality,
24};
25use std::fmt::{Debug, Formatter};
26
27/// Target to which the exporter is going to send metrics, defaults to https://localhost:4317/v1/metrics.
28/// Learn about the relationship between this constant and default/spans/logs at
29/// <https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/protocol/exporter.md#endpoint-urls-for-otlphttp>
30pub const OTEL_EXPORTER_OTLP_METRICS_ENDPOINT: &str = "OTEL_EXPORTER_OTLP_METRICS_ENDPOINT";
31/// Max waiting time for the backend to process each metrics batch, defaults to 10s.
32pub const OTEL_EXPORTER_OTLP_METRICS_TIMEOUT: &str = "OTEL_EXPORTER_OTLP_METRICS_TIMEOUT";
33/// Compression algorithm to use, defaults to none.
34pub const OTEL_EXPORTER_OTLP_METRICS_COMPRESSION: &str = "OTEL_EXPORTER_OTLP_METRICS_COMPRESSION";
35/// Key-value pairs to be used as headers associated with gRPC or HTTP requests
36/// for sending metrics.
37/// Example: `k1=v1,k2=v2`
38/// Note: this is only supported for HTTP.
39pub const OTEL_EXPORTER_OTLP_METRICS_HEADERS: &str = "OTEL_EXPORTER_OTLP_METRICS_HEADERS";
40
41#[derive(Debug, Default, Clone)]
42pub struct MetricExporterBuilder<C> {
43    client: C,
44    temporality: Temporality,
45}
46
47impl MetricExporterBuilder<NoExporterBuilderSet> {
48    pub fn new() -> Self {
49        MetricExporterBuilder::default()
50    }
51}
52
53impl<C> MetricExporterBuilder<C> {
54    #[cfg(feature = "grpc-tonic")]
55    pub fn with_tonic(self) -> MetricExporterBuilder<TonicExporterBuilderSet> {
56        MetricExporterBuilder {
57            client: TonicExporterBuilderSet(TonicExporterBuilder::default()),
58            temporality: self.temporality,
59        }
60    }
61
62    #[cfg(any(feature = "http-proto", feature = "http-json"))]
63    pub fn with_http(self) -> MetricExporterBuilder<HttpExporterBuilderSet> {
64        MetricExporterBuilder {
65            client: HttpExporterBuilderSet(HttpExporterBuilder::default()),
66            temporality: self.temporality,
67        }
68    }
69
70    pub fn with_temporality(self, temporality: Temporality) -> MetricExporterBuilder<C> {
71        MetricExporterBuilder {
72            client: self.client,
73            temporality,
74        }
75    }
76}
77
78#[cfg(feature = "grpc-tonic")]
79impl MetricExporterBuilder<TonicExporterBuilderSet> {
80    pub fn build(self) -> MetricResult<MetricExporter> {
81        let exporter = self.client.0.build_metrics_exporter(self.temporality)?;
82        opentelemetry::otel_debug!(name: "MetricExporterBuilt");
83        Ok(exporter)
84    }
85}
86
87#[cfg(any(feature = "http-proto", feature = "http-json"))]
88impl MetricExporterBuilder<HttpExporterBuilderSet> {
89    pub fn build(self) -> MetricResult<MetricExporter> {
90        let exporter = self.client.0.build_metrics_exporter(self.temporality)?;
91        Ok(exporter)
92    }
93}
94
95#[cfg(feature = "grpc-tonic")]
96impl HasExportConfig for MetricExporterBuilder<TonicExporterBuilderSet> {
97    fn export_config(&mut self) -> &mut crate::ExportConfig {
98        &mut self.client.0.exporter_config
99    }
100}
101
102#[cfg(any(feature = "http-proto", feature = "http-json"))]
103impl HasExportConfig for MetricExporterBuilder<HttpExporterBuilderSet> {
104    fn export_config(&mut self) -> &mut crate::ExportConfig {
105        &mut self.client.0.exporter_config
106    }
107}
108
109#[cfg(feature = "grpc-tonic")]
110impl HasTonicConfig for MetricExporterBuilder<TonicExporterBuilderSet> {
111    fn tonic_config(&mut self) -> &mut crate::TonicConfig {
112        &mut self.client.0.tonic_config
113    }
114}
115
116#[cfg(any(feature = "http-proto", feature = "http-json"))]
117impl HasHttpConfig for MetricExporterBuilder<HttpExporterBuilderSet> {
118    fn http_client_config(&mut self) -> &mut crate::exporter::http::HttpConfig {
119        &mut self.client.0.http_config
120    }
121}
122
123/// An interface for OTLP metrics clients
124#[async_trait]
125pub(crate) trait MetricsClient: fmt::Debug + Send + Sync + 'static {
126    async fn export(&self, metrics: &mut ResourceMetrics) -> OTelSdkResult;
127    fn shutdown(&self) -> OTelSdkResult;
128}
129
130/// Export metrics in OTEL format.
131pub struct MetricExporter {
132    client: Box<dyn MetricsClient>,
133    temporality: Temporality,
134}
135
136impl Debug for MetricExporter {
137    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
138        f.debug_struct("MetricExporter").finish()
139    }
140}
141
142#[async_trait]
143impl PushMetricExporter for MetricExporter {
144    async fn export(&self, metrics: &mut ResourceMetrics) -> OTelSdkResult {
145        self.client.export(metrics).await
146    }
147
148    async fn force_flush(&self) -> OTelSdkResult {
149        // this component is stateless
150        Ok(())
151    }
152
153    fn shutdown(&self) -> OTelSdkResult {
154        self.client.shutdown()
155    }
156
157    fn temporality(&self) -> Temporality {
158        self.temporality
159    }
160}
161
162impl MetricExporter {
163    /// Obtain a builder to configure a [MetricExporter].
164    pub fn builder() -> MetricExporterBuilder<NoExporterBuilderSet> {
165        MetricExporterBuilder::default()
166    }
167
168    /// Create a new metrics exporter
169    pub(crate) fn new(client: impl MetricsClient, temporality: Temporality) -> MetricExporter {
170        MetricExporter {
171            client: Box::new(client),
172            temporality,
173        }
174    }
175}