opentelemetry_sdk/metrics/data/mod.rs
1//! Types for delivery of pre-aggregated metric time series data.
2
3use std::{borrow::Cow, time::SystemTime};
4
5use opentelemetry::{InstrumentationScope, KeyValue};
6
7use crate::Resource;
8
9use super::Temporality;
10
11/// A collection of [ScopeMetrics] and the associated [Resource] that created them.
12#[derive(Debug)]
13pub struct ResourceMetrics {
14 /// The entity that collected the metrics.
15 pub(crate) resource: Resource,
16 /// The collection of metrics with unique [InstrumentationScope]s.
17 pub(crate) scope_metrics: Vec<ScopeMetrics>,
18}
19
20impl Default for ResourceMetrics {
21 fn default() -> Self {
22 Self {
23 resource: Resource::empty(),
24 scope_metrics: Vec::new(),
25 }
26 }
27}
28
29impl ResourceMetrics {
30 /// Returns a reference to the [Resource] in [ResourceMetrics].
31 pub fn resource(&self) -> &Resource {
32 &self.resource
33 }
34
35 /// Returns an iterator over the [ScopeMetrics] in [ResourceMetrics].
36 pub fn scope_metrics(&self) -> impl Iterator<Item = &ScopeMetrics> {
37 self.scope_metrics.iter()
38 }
39}
40
41/// A collection of metrics produced by a meter.
42#[derive(Default, Debug)]
43pub struct ScopeMetrics {
44 /// The [InstrumentationScope] that the meter was created with.
45 pub(crate) scope: InstrumentationScope,
46 /// The list of aggregations created by the meter.
47 pub(crate) metrics: Vec<Metric>,
48}
49
50impl ScopeMetrics {
51 /// Returns a reference to the [InstrumentationScope] in [ScopeMetrics].
52 pub fn scope(&self) -> &InstrumentationScope {
53 &self.scope
54 }
55
56 /// Returns an iterator over the [Metric]s in [ScopeMetrics].
57 pub fn metrics(&self) -> impl Iterator<Item = &Metric> {
58 self.metrics.iter()
59 }
60}
61
62/// A collection of one or more aggregated time series from an [Instrument].
63///
64/// [Instrument]: crate::metrics::Instrument
65#[derive(Debug)]
66pub struct Metric {
67 /// The name of the instrument that created this data.
68 pub(crate) name: Cow<'static, str>,
69 /// The description of the instrument, which can be used in documentation.
70 pub(crate) description: Cow<'static, str>,
71 /// The unit in which the instrument reports.
72 pub(crate) unit: Cow<'static, str>,
73 /// The aggregated data from an instrument.
74 pub(crate) data: AggregatedMetrics,
75}
76
77impl Metric {
78 /// Returns the name of the instrument that created this data.
79 pub fn name(&self) -> &str {
80 &self.name
81 }
82
83 /// Returns the description of the instrument.
84 pub fn description(&self) -> &str {
85 &self.description
86 }
87
88 /// Returns the unit in which the instrument reports.
89 pub fn unit(&self) -> &str {
90 &self.unit
91 }
92
93 /// Returns the aggregated data from the instrument.
94 pub fn data(&self) -> &AggregatedMetrics {
95 &self.data
96 }
97}
98
99/// Aggregated metrics data from an instrument
100#[derive(Debug)]
101pub enum AggregatedMetrics {
102 /// All metric data with `f64` value type
103 F64(MetricData<f64>),
104 /// All metric data with `u64` value type
105 U64(MetricData<u64>),
106 /// All metric data with `i64` value type
107 I64(MetricData<i64>),
108}
109
110/// Metric data for all types
111#[derive(Debug)]
112pub enum MetricData<T> {
113 /// Metric data for Gauge
114 Gauge(Gauge<T>),
115 /// Metric data for Sum
116 Sum(Sum<T>),
117 /// Metric data for Histogram
118 Histogram(Histogram<T>),
119 /// Metric data for ExponentialHistogram
120 ExponentialHistogram(ExponentialHistogram<T>),
121}
122
123impl From<MetricData<f64>> for AggregatedMetrics {
124 fn from(value: MetricData<f64>) -> Self {
125 AggregatedMetrics::F64(value)
126 }
127}
128
129impl From<MetricData<i64>> for AggregatedMetrics {
130 fn from(value: MetricData<i64>) -> Self {
131 AggregatedMetrics::I64(value)
132 }
133}
134
135impl From<MetricData<u64>> for AggregatedMetrics {
136 fn from(value: MetricData<u64>) -> Self {
137 AggregatedMetrics::U64(value)
138 }
139}
140
141impl<T> From<Gauge<T>> for MetricData<T> {
142 fn from(value: Gauge<T>) -> Self {
143 MetricData::Gauge(value)
144 }
145}
146
147impl<T> From<Sum<T>> for MetricData<T> {
148 fn from(value: Sum<T>) -> Self {
149 MetricData::Sum(value)
150 }
151}
152
153impl<T> From<Histogram<T>> for MetricData<T> {
154 fn from(value: Histogram<T>) -> Self {
155 MetricData::Histogram(value)
156 }
157}
158
159impl<T> From<ExponentialHistogram<T>> for MetricData<T> {
160 fn from(value: ExponentialHistogram<T>) -> Self {
161 MetricData::ExponentialHistogram(value)
162 }
163}
164
165/// DataPoint is a single data point in a time series.
166#[derive(Debug, Clone, PartialEq)]
167pub struct GaugeDataPoint<T> {
168 /// Attributes is the set of key value pairs that uniquely identify the
169 /// time series.
170 pub(crate) attributes: Vec<KeyValue>,
171 /// The value of this data point.
172 pub(crate) value: T,
173 /// The sampled [Exemplar]s collected during the time series.
174 pub(crate) exemplars: Vec<Exemplar<T>>,
175}
176
177impl<T> GaugeDataPoint<T> {
178 /// Returns an iterator over the attributes in [GaugeDataPoint].
179 pub fn attributes(&self) -> impl Iterator<Item = &KeyValue> {
180 self.attributes.iter()
181 }
182
183 /// Returns an iterator over the [Exemplar]s in [GaugeDataPoint].
184 pub fn exemplars(&self) -> impl Iterator<Item = &Exemplar<T>> {
185 self.exemplars.iter()
186 }
187}
188
189impl<T: Copy> GaugeDataPoint<T> {
190 /// Returns the value of this data point.
191 pub fn value(&self) -> T {
192 self.value
193 }
194}
195
196/// A measurement of the current value of an instrument.
197#[derive(Debug, Clone)]
198pub struct Gauge<T> {
199 /// Represents individual aggregated measurements with unique attributes.
200 pub(crate) data_points: Vec<GaugeDataPoint<T>>,
201 /// The time when the time series was started.
202 pub(crate) start_time: Option<SystemTime>,
203 /// The time when the time series was recorded.
204 pub(crate) time: SystemTime,
205}
206
207impl<T> Gauge<T> {
208 /// Returns an iterator over the [GaugeDataPoint]s in [Gauge].
209 pub fn data_points(&self) -> impl Iterator<Item = &GaugeDataPoint<T>> {
210 self.data_points.iter()
211 }
212
213 /// Returns the time when the time series was started.
214 pub fn start_time(&self) -> Option<SystemTime> {
215 self.start_time
216 }
217
218 /// Returns the time when the time series was recorded.
219 pub fn time(&self) -> SystemTime {
220 self.time
221 }
222}
223
224/// DataPoint is a single data point in a time series.
225#[derive(Debug, Clone, PartialEq)]
226pub struct SumDataPoint<T> {
227 /// Attributes is the set of key value pairs that uniquely identify the
228 /// time series.
229 pub(crate) attributes: Vec<KeyValue>,
230 /// The value of this data point.
231 pub(crate) value: T,
232 /// The sampled [Exemplar]s collected during the time series.
233 pub(crate) exemplars: Vec<Exemplar<T>>,
234}
235
236impl<T> SumDataPoint<T> {
237 /// Returns an iterator over the attributes in [SumDataPoint].
238 pub fn attributes(&self) -> impl Iterator<Item = &KeyValue> {
239 self.attributes.iter()
240 }
241
242 /// Returns an iterator over the [Exemplar]s in [SumDataPoint].
243 pub fn exemplars(&self) -> impl Iterator<Item = &Exemplar<T>> {
244 self.exemplars.iter()
245 }
246}
247
248impl<T: Copy> SumDataPoint<T> {
249 /// Returns the value of this data point.
250 pub fn value(&self) -> T {
251 self.value
252 }
253}
254
255/// Represents the sum of all measurements of values from an instrument.
256#[derive(Debug, Clone)]
257pub struct Sum<T> {
258 /// Represents individual aggregated measurements with unique attributes.
259 pub(crate) data_points: Vec<SumDataPoint<T>>,
260 /// The time when the time series was started.
261 pub(crate) start_time: SystemTime,
262 /// The time when the time series was recorded.
263 pub(crate) time: SystemTime,
264 /// Describes if the aggregation is reported as the change from the last report
265 /// time, or the cumulative changes since a fixed start time.
266 pub(crate) temporality: Temporality,
267 /// Whether this aggregation only increases or decreases.
268 pub(crate) is_monotonic: bool,
269}
270
271impl<T> Sum<T> {
272 /// Returns an iterator over the [SumDataPoint]s in [Sum].
273 pub fn data_points(&self) -> impl Iterator<Item = &SumDataPoint<T>> {
274 self.data_points.iter()
275 }
276
277 /// Returns the time when the time series was started.
278 pub fn start_time(&self) -> SystemTime {
279 self.start_time
280 }
281
282 /// Returns the time when the time series was recorded.
283 pub fn time(&self) -> SystemTime {
284 self.time
285 }
286
287 /// Returns the temporality describing if the aggregation is reported as the change
288 /// from the last report time, or the cumulative changes since a fixed start time.
289 pub fn temporality(&self) -> Temporality {
290 self.temporality
291 }
292
293 /// Returns whether this aggregation only increases or decreases.
294 pub fn is_monotonic(&self) -> bool {
295 self.is_monotonic
296 }
297}
298
299/// Represents the histogram of all measurements of values from an instrument.
300#[derive(Debug, Clone)]
301pub struct Histogram<T> {
302 /// Individual aggregated measurements with unique attributes.
303 pub(crate) data_points: Vec<HistogramDataPoint<T>>,
304 /// The time when the time series was started.
305 pub(crate) start_time: SystemTime,
306 /// The time when the time series was recorded.
307 pub(crate) time: SystemTime,
308 /// Describes if the aggregation is reported as the change from the last report
309 /// time, or the cumulative changes since a fixed start time.
310 pub(crate) temporality: Temporality,
311}
312
313impl<T> Histogram<T> {
314 /// Returns an iterator over the [HistogramDataPoint]s in [Histogram].
315 pub fn data_points(&self) -> impl Iterator<Item = &HistogramDataPoint<T>> {
316 self.data_points.iter()
317 }
318
319 /// Returns the time when the time series was started.
320 pub fn start_time(&self) -> SystemTime {
321 self.start_time
322 }
323
324 /// Returns the time when the time series was recorded.
325 pub fn time(&self) -> SystemTime {
326 self.time
327 }
328
329 /// Returns the temporality describing if the aggregation is reported as the change
330 /// from the last report time, or the cumulative changes since a fixed start time.
331 pub fn temporality(&self) -> Temporality {
332 self.temporality
333 }
334}
335
336/// A single histogram data point in a time series.
337#[derive(Debug, Clone, PartialEq)]
338pub struct HistogramDataPoint<T> {
339 /// The set of key value pairs that uniquely identify the time series.
340 pub(crate) attributes: Vec<KeyValue>,
341 /// The number of updates this histogram has been calculated with.
342 pub(crate) count: u64,
343 /// The upper bounds of the buckets of the histogram.
344 ///
345 /// Because the last boundary is +infinity this one is implied.
346 pub(crate) bounds: Vec<f64>,
347 /// The count of each of the buckets.
348 pub(crate) bucket_counts: Vec<u64>,
349
350 /// The minimum value recorded.
351 pub(crate) min: Option<T>,
352 /// The maximum value recorded.
353 pub(crate) max: Option<T>,
354 /// The sum of the values recorded.
355 pub(crate) sum: T,
356
357 /// The sampled [Exemplar]s collected during the time series.
358 pub(crate) exemplars: Vec<Exemplar<T>>,
359}
360
361impl<T> HistogramDataPoint<T> {
362 /// Returns an iterator over the attributes in [HistogramDataPoint].
363 pub fn attributes(&self) -> impl Iterator<Item = &KeyValue> {
364 self.attributes.iter()
365 }
366
367 /// Returns an iterator over the exemplars in [HistogramDataPoint].
368 pub fn exemplars(&self) -> impl Iterator<Item = &Exemplar<T>> {
369 self.exemplars.iter()
370 }
371
372 /// Returns an iterator over the bucket boundaries in [HistogramDataPoint].
373 pub fn bounds(&self) -> impl Iterator<Item = f64> + '_ {
374 self.bounds.iter().copied()
375 }
376
377 /// Returns an iterator over the bucket counts in [HistogramDataPoint].
378 pub fn bucket_counts(&self) -> impl Iterator<Item = u64> + '_ {
379 self.bucket_counts.iter().copied()
380 }
381
382 /// Returns the number of updates this histogram has been calculated with.
383 pub fn count(&self) -> u64 {
384 self.count
385 }
386}
387
388impl<T: Copy> HistogramDataPoint<T> {
389 /// Returns the minimum value recorded.
390 pub fn min(&self) -> Option<T> {
391 self.min
392 }
393
394 /// Returns the maximum value recorded.
395 pub fn max(&self) -> Option<T> {
396 self.max
397 }
398
399 /// Returns the sum of the values recorded.
400 pub fn sum(&self) -> T {
401 self.sum
402 }
403}
404
405/// The histogram of all measurements of values from an instrument.
406#[derive(Debug, Clone)]
407pub struct ExponentialHistogram<T> {
408 /// The individual aggregated measurements with unique attributes.
409 pub(crate) data_points: Vec<ExponentialHistogramDataPoint<T>>,
410 /// When the time series was started.
411 pub(crate) start_time: SystemTime,
412 /// The time when the time series was recorded.
413 pub(crate) time: SystemTime,
414 /// Describes if the aggregation is reported as the change from the last report
415 /// time, or the cumulative changes since a fixed start time.
416 pub(crate) temporality: Temporality,
417}
418
419impl<T> ExponentialHistogram<T> {
420 /// Returns an iterator over the [ExponentialHistogramDataPoint]s in [ExponentialHistogram].
421 pub fn data_points(&self) -> impl Iterator<Item = &ExponentialHistogramDataPoint<T>> {
422 self.data_points.iter()
423 }
424
425 /// Returns the time when the time series was started.
426 pub fn start_time(&self) -> SystemTime {
427 self.start_time
428 }
429
430 /// Returns the time when the time series was recorded.
431 pub fn time(&self) -> SystemTime {
432 self.time
433 }
434
435 /// Returns the temporality describing if the aggregation is reported as the change
436 /// from the last report time, or the cumulative changes since a fixed start time.
437 pub fn temporality(&self) -> Temporality {
438 self.temporality
439 }
440}
441
442/// A single exponential histogram data point in a time series.
443#[derive(Debug, Clone, PartialEq)]
444pub struct ExponentialHistogramDataPoint<T> {
445 /// The set of key value pairs that uniquely identify the time series.
446 pub(crate) attributes: Vec<KeyValue>,
447
448 /// The number of updates this histogram has been calculated with.
449 pub(crate) count: usize,
450 /// The minimum value recorded.
451 pub(crate) min: Option<T>,
452 /// The maximum value recorded.
453 pub(crate) max: Option<T>,
454 /// The sum of the values recorded.
455 pub(crate) sum: T,
456
457 /// Describes the resolution of the histogram.
458 ///
459 /// Boundaries are located at powers of the base, where:
460 ///
461 /// base = 2 ^ (2 ^ -scale)
462 pub(crate) scale: i8,
463
464 /// The number of values whose absolute value is less than or equal to
465 /// `zero_threshold`.
466 ///
467 /// When `zero_threshold` is `0`, this is the number of values that cannot be
468 /// expressed using the standard exponential formula as well as values that have
469 /// been rounded to zero.
470 pub(crate) zero_count: u64,
471
472 /// The range of positive value bucket counts.
473 pub(crate) positive_bucket: ExponentialBucket,
474 /// The range of negative value bucket counts.
475 pub(crate) negative_bucket: ExponentialBucket,
476
477 /// The width of the zero region.
478 ///
479 /// Where the zero region is defined as the closed interval
480 /// [-zero_threshold, zero_threshold].
481 pub(crate) zero_threshold: f64,
482
483 /// The sampled exemplars collected during the time series.
484 pub(crate) exemplars: Vec<Exemplar<T>>,
485}
486
487impl<T> ExponentialHistogramDataPoint<T> {
488 /// Returns an iterator over the attributes in [ExponentialHistogramDataPoint].
489 pub fn attributes(&self) -> impl Iterator<Item = &KeyValue> {
490 self.attributes.iter()
491 }
492
493 /// Returns an iterator over the exemplars in [ExponentialHistogramDataPoint].
494 pub fn exemplars(&self) -> impl Iterator<Item = &Exemplar<T>> {
495 self.exemplars.iter()
496 }
497
498 /// Returns the number of updates this histogram has been calculated with.
499 pub fn count(&self) -> usize {
500 self.count
501 }
502
503 /// Returns the resolution of the histogram.
504 pub fn scale(&self) -> i8 {
505 self.scale
506 }
507
508 /// Returns the number of values whose absolute value is less than or equal to zero_threshold.
509 pub fn zero_count(&self) -> u64 {
510 self.zero_count
511 }
512
513 /// Returns the range of positive value bucket counts.
514 pub fn positive_bucket(&self) -> &ExponentialBucket {
515 &self.positive_bucket
516 }
517
518 /// Returns the range of negative value bucket counts.
519 pub fn negative_bucket(&self) -> &ExponentialBucket {
520 &self.negative_bucket
521 }
522
523 /// Returns the width of the zero region.
524 pub fn zero_threshold(&self) -> f64 {
525 self.zero_threshold
526 }
527}
528
529impl<T: Copy> ExponentialHistogramDataPoint<T> {
530 /// Returns the minimum value recorded.
531 pub fn min(&self) -> Option<T> {
532 self.min
533 }
534
535 /// Returns the maximum value recorded.
536 pub fn max(&self) -> Option<T> {
537 self.max
538 }
539
540 /// Returns the sum of the values recorded.
541 pub fn sum(&self) -> T {
542 self.sum
543 }
544}
545
546/// A set of bucket counts, encoded in a contiguous array of counts.
547#[derive(Debug, Clone, PartialEq)]
548pub struct ExponentialBucket {
549 /// The bucket index of the first entry in the `counts` vec.
550 pub(crate) offset: i32,
551
552 /// A vec where `counts[i]` carries the count of the bucket at index `offset + i`.
553 ///
554 /// `counts[i]` is the count of values greater than base^(offset+i) and less than
555 /// or equal to base^(offset+i+1).
556 pub(crate) counts: Vec<u64>,
557}
558
559impl ExponentialBucket {
560 /// Returns the bucket index of the first entry in the counts vec.
561 pub fn offset(&self) -> i32 {
562 self.offset
563 }
564
565 /// Returns an iterator over the counts.
566 pub fn counts(&self) -> impl Iterator<Item = u64> + '_ {
567 self.counts.iter().copied()
568 }
569}
570
571/// A measurement sampled from a time series providing a typical example.
572#[derive(Debug, Clone, PartialEq)]
573pub struct Exemplar<T> {
574 /// The attributes recorded with the measurement but filtered out of the
575 /// time series' aggregated data.
576 pub(crate) filtered_attributes: Vec<KeyValue>,
577 /// The time when the measurement was recorded.
578 pub(crate) time: SystemTime,
579 /// The measured value.
580 pub value: T,
581 /// The ID of the span that was active during the measurement.
582 ///
583 /// If no span was active or the span was not sampled this will be empty.
584 pub(crate) span_id: [u8; 8],
585 /// The ID of the trace the active span belonged to during the measurement.
586 ///
587 /// If no span was active or the span was not sampled this will be empty.
588 pub(crate) trace_id: [u8; 16],
589}
590
591impl<T> Exemplar<T> {
592 /// Returns an iterator over the filtered attributes in [Exemplar].
593 pub fn filtered_attributes(&self) -> impl Iterator<Item = &KeyValue> {
594 self.filtered_attributes.iter()
595 }
596
597 /// Returns the time when the measurement was recorded.
598 pub fn time(&self) -> SystemTime {
599 self.time
600 }
601
602 /// Returns the ID of the span that was active during the measurement.
603 pub fn span_id(&self) -> &[u8; 8] {
604 &self.span_id
605 }
606
607 /// Returns the ID of the trace the active span belonged to during the measurement.
608 pub fn trace_id(&self) -> &[u8; 16] {
609 &self.trace_id
610 }
611}
612
613#[cfg(test)]
614mod tests {
615
616 use super::{Exemplar, ExponentialHistogramDataPoint, HistogramDataPoint, SumDataPoint};
617
618 use opentelemetry::time::now;
619 use opentelemetry::KeyValue;
620
621 #[test]
622 fn validate_cloning_data_points() {
623 let data_type = SumDataPoint {
624 attributes: vec![KeyValue::new("key", "value")],
625 value: 0u32,
626 exemplars: vec![Exemplar {
627 filtered_attributes: vec![],
628 time: now(),
629 value: 0u32,
630 span_id: [0; 8],
631 trace_id: [0; 16],
632 }],
633 };
634 assert_eq!(data_type.clone(), data_type);
635
636 let histogram_data_point = HistogramDataPoint {
637 attributes: vec![KeyValue::new("key", "value")],
638 count: 0,
639 bounds: vec![],
640 bucket_counts: vec![],
641 min: None,
642 max: None,
643 sum: 0u32,
644 exemplars: vec![Exemplar {
645 filtered_attributes: vec![],
646 time: now(),
647 value: 0u32,
648 span_id: [0; 8],
649 trace_id: [0; 16],
650 }],
651 };
652 assert_eq!(histogram_data_point.clone(), histogram_data_point);
653
654 let exponential_histogram_data_point = ExponentialHistogramDataPoint {
655 attributes: vec![KeyValue::new("key", "value")],
656 count: 0,
657 min: None,
658 max: None,
659 sum: 0u32,
660 scale: 0,
661 zero_count: 0,
662 positive_bucket: super::ExponentialBucket {
663 offset: 0,
664 counts: vec![],
665 },
666 negative_bucket: super::ExponentialBucket {
667 offset: 0,
668 counts: vec![],
669 },
670 zero_threshold: 0.0,
671 exemplars: vec![Exemplar {
672 filtered_attributes: vec![],
673 time: now(),
674 value: 0u32,
675 span_id: [0; 8],
676 trace_id: [0; 16],
677 }],
678 };
679 assert_eq!(
680 exponential_histogram_data_point.clone(),
681 exponential_histogram_data_point
682 );
683 }
684}