opentelemetry_application_insights/
metrics.rs1use crate::{
2 convert::time_to_string,
3 models::{Data, DataPoint, DataPointType, Envelope, MetricData, Properties},
4 tags::get_tags_for_metric,
5 Exporter,
6};
7use async_trait::async_trait;
8use opentelemetry::KeyValue;
9use opentelemetry_http::HttpClient;
10use opentelemetry_sdk::{
11 error::OTelSdkResult,
12 metrics::{
13 data::{ExponentialHistogram, Gauge, Histogram, Metric, ResourceMetrics, Sum},
14 exporter::PushMetricExporter,
15 Temporality,
16 },
17};
18use std::{
19 convert::TryInto,
20 sync::Arc,
21 time::{Duration, SystemTime},
22};
23
24#[cfg_attr(docsrs, doc(cfg(feature = "metrics")))]
25#[async_trait]
26impl<C> PushMetricExporter for Exporter<C>
27where
28 C: HttpClient + 'static,
29{
30 fn export(
31 &self,
32 metrics: &ResourceMetrics,
33 ) -> impl std::future::Future<Output = OTelSdkResult> + Send {
34 let client = Arc::clone(&self.client);
35 let endpoint = Arc::clone(&self.track_endpoint);
36
37 let mut envelopes = Vec::new();
38 for scope_metrics in metrics.scope_metrics() {
39 for metric in scope_metrics.metrics() {
40 let data_points = map_metric(metric);
41 for data in data_points {
42 let tags =
43 get_tags_for_metric(metrics.resource(), scope_metrics.scope(), &data.attrs);
44 let properties: Properties = metrics
45 .resource()
46 .iter()
47 .chain(
48 scope_metrics
49 .scope()
50 .attributes()
51 .map(|kv| (&kv.key, &kv.value)),
52 )
53 .chain(data.attrs.iter().map(|kv| (&kv.key, &kv.value)))
54 .map(|(k, v)| (k.as_str().into(), v.into()))
55 .collect();
56 envelopes.push(Envelope {
57 name: "Microsoft.ApplicationInsights.Metric",
58 time: time_to_string(data.time).into(),
59 sample_rate: None,
60 i_key: Some(self.instrumentation_key.clone().into()),
61 tags: Some(tags).filter(|x| !x.is_empty()),
62 data: Some(Data::Metric(MetricData {
63 ver: 2,
64 metrics: vec![data.data],
65 properties: Some(properties).filter(|x| !x.is_empty()),
66 })),
67 });
68 }
69 }
70 }
71
72 async move {
73 crate::uploader::send(
74 client.as_ref(),
75 endpoint.as_ref(),
76 envelopes,
77 self.retry_notify.clone(),
78 )
79 .await
80 .map_err(Into::into)
81 }
82 }
83
84 fn force_flush(&self) -> OTelSdkResult {
85 Ok(())
86 }
87
88 fn shutdown_with_timeout(&self, _timeout: Duration) -> OTelSdkResult {
89 Ok(())
90 }
91
92 fn temporality(&self) -> Temporality {
93 Temporality::Delta
103 }
104}
105
106struct EnvelopeData {
107 time: SystemTime,
108 data: DataPoint,
109 attrs: Vec<KeyValue>,
110}
111
112trait ToF64Lossy {
113 fn to_f64_lossy(&self) -> f64;
114}
115
116impl ToF64Lossy for i64 {
117 fn to_f64_lossy(&self) -> f64 {
118 *self as f64
119 }
120}
121
122impl ToF64Lossy for u64 {
123 fn to_f64_lossy(&self) -> f64 {
124 *self as f64
125 }
126}
127
128impl ToF64Lossy for f64 {
129 fn to_f64_lossy(&self) -> f64 {
130 *self
131 }
132}
133
134fn map_metric(metric: &Metric) -> Vec<EnvelopeData> {
135 use opentelemetry_sdk::metrics::data::{AggregatedMetrics::*, MetricData};
136 match metric.data() {
137 F64(MetricData::Gauge(data)) => map_gauge(metric, data),
138 U64(MetricData::Gauge(data)) => map_gauge(metric, data),
139 I64(MetricData::Gauge(data)) => map_gauge(metric, data),
140 F64(MetricData::Sum(data)) => map_sum(metric, data),
141 U64(MetricData::Sum(data)) => map_sum(metric, data),
142 I64(MetricData::Sum(data)) => map_sum(metric, data),
143 F64(MetricData::Histogram(data)) => map_histogram(metric, data),
144 U64(MetricData::Histogram(data)) => map_histogram(metric, data),
145 I64(MetricData::Histogram(data)) => map_histogram(metric, data),
146 F64(MetricData::ExponentialHistogram(data)) => map_exponential_histogram(metric, data),
147 U64(MetricData::ExponentialHistogram(data)) => map_exponential_histogram(metric, data),
148 I64(MetricData::ExponentialHistogram(data)) => map_exponential_histogram(metric, data),
149 }
150}
151
152fn map_gauge<T: Copy + ToF64Lossy>(metric: &Metric, gauge: &Gauge<T>) -> Vec<EnvelopeData> {
153 gauge
154 .data_points()
155 .map(|data_point| {
156 let time = gauge.time();
157 let data = DataPoint {
158 ns: None,
159 name: metric.name().into(),
160 kind: Some(DataPointType::Measurement),
161 value: data_point.value().to_f64_lossy(),
162 };
163 let attrs = data_point.attributes().cloned().collect();
164 EnvelopeData { time, data, attrs }
165 })
166 .collect()
167}
168
169fn map_histogram<T: Copy + ToF64Lossy>(
170 metric: &Metric,
171 histogram: &Histogram<T>,
172) -> Vec<EnvelopeData> {
173 histogram
174 .data_points()
175 .map(|data_point| {
176 let time = histogram.time();
177 let data = DataPoint {
178 ns: None,
179 name: metric.name().into(),
180 kind: Some(DataPointType::Aggregation {
181 count: Some(data_point.count().try_into().unwrap_or_default()),
182 min: data_point.min().as_ref().map(ToF64Lossy::to_f64_lossy),
183 max: data_point.max().as_ref().map(ToF64Lossy::to_f64_lossy),
184 std_dev: None,
185 }),
186 value: data_point.sum().to_f64_lossy(),
187 };
188 let attrs = data_point.attributes().cloned().collect();
189 EnvelopeData { time, data, attrs }
190 })
191 .collect()
192}
193
194fn map_exponential_histogram<T: Copy + ToF64Lossy>(
195 metric: &Metric,
196 exp_histogram: &ExponentialHistogram<T>,
197) -> Vec<EnvelopeData> {
198 exp_histogram
199 .data_points()
200 .map(|data_point| {
201 let time = exp_histogram.time();
202 let data = DataPoint {
203 ns: None,
204 name: metric.name().into(),
205 kind: Some(DataPointType::Aggregation {
206 count: Some(data_point.count().try_into().unwrap_or_default()),
207 min: data_point.min().as_ref().map(ToF64Lossy::to_f64_lossy),
208 max: data_point.max().as_ref().map(ToF64Lossy::to_f64_lossy),
209 std_dev: None,
210 }),
211 value: data_point.sum().to_f64_lossy(),
212 };
213 let attrs = data_point.attributes().cloned().collect();
214 EnvelopeData { time, data, attrs }
215 })
216 .collect()
217}
218
219fn map_sum<T: Copy + ToF64Lossy>(metric: &Metric, sum: &Sum<T>) -> Vec<EnvelopeData> {
220 sum.data_points()
221 .map(|data_point| {
222 let time = sum.time();
223 let data = DataPoint {
224 ns: None,
225 name: metric.name().into(),
226 kind: Some(DataPointType::Aggregation {
227 count: None,
228 min: None,
229 max: None,
230 std_dev: None,
231 }),
232 value: data_point.value().to_f64_lossy(),
233 };
234 let attrs = data_point.attributes().cloned().collect();
235 EnvelopeData { time, data, attrs }
236 })
237 .collect()
238}