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}