metrics_datadog_exporter/
data.rs

1//! Data model
2//!
3use std::sync::atomic::Ordering;
4use std::sync::Arc;
5
6use chrono::Utc;
7use itertools::Itertools;
8use metrics::atomics::AtomicU64;
9use metrics::{Key, Label};
10use metrics_util::AtomicBucket;
11
12use serde::{Deserialize, Serialize};
13use serde_with::skip_serializing_none;
14
15/// Metric type
16#[derive(Debug, Serialize, Deserialize, Clone, Eq, PartialEq, PartialOrd, Ord)]
17pub enum DataDogMetricType {
18    /// Counter
19    #[serde(rename = "count")]
20    Count,
21    /// Gauge
22    #[serde(rename = "gauge")]
23    Gauge,
24    /// Histogram
25    #[serde(rename = "histogram")]
26    Histogram,
27}
28
29#[derive(Debug, Serialize, Deserialize, Clone, PartialOrd, PartialEq)]
30#[serde(untagged)]
31/// Metric value
32pub enum DataDogMetricValue {
33    /// Float
34    Float(f64),
35    /// Unsigned
36    Unsigned(u64),
37}
38
39#[derive(Debug, Serialize, Deserialize, Clone, PartialOrd, PartialEq)]
40/// DataDog formatted metric
41pub struct DataDogMetric {
42    /// Metric name
43    pub metric: String,
44    /// Metric type
45    pub metric_type: DataDogMetricType,
46    /// Metric values
47    pub points: Vec<DataDogMetricValue>,
48    /// Timestamp
49    pub timestamp: i64,
50    /// Tags
51    pub tags: Vec<String>,
52}
53
54impl DataDogMetric {
55    pub(crate) fn from_counter(
56        key: Key,
57        values: Vec<Arc<AtomicU64>>,
58        global_tags: &[Label],
59    ) -> Self {
60        let values = values
61            .into_iter()
62            .map(|value| {
63                let u = value.load(Ordering::Acquire);
64                DataDogMetricValue::Unsigned(u)
65            })
66            .collect_vec();
67        DataDogMetric::from_metric_value(DataDogMetricType::Count, key, values, global_tags)
68    }
69
70    pub(crate) fn from_gauge(key: Key, values: Vec<Arc<AtomicU64>>, global_tags: &[Label]) -> Self {
71        let values = values
72            .into_iter()
73            .map(|value| {
74                let u = f64::from_bits(value.load(Ordering::Acquire));
75                DataDogMetricValue::Float(u)
76            })
77            .collect_vec();
78        DataDogMetric::from_metric_value(DataDogMetricType::Gauge, key, values, global_tags)
79    }
80
81    pub(crate) fn from_histogram(
82        key: Key,
83        values: Vec<Arc<AtomicBucket<f64>>>,
84        global_tags: &[Label],
85    ) -> Self {
86        let values = values
87            .into_iter()
88            .flat_map(|value| value.data().into_iter().map(DataDogMetricValue::Float))
89            .collect_vec();
90        DataDogMetric::from_metric_value(DataDogMetricType::Histogram, key, values, global_tags)
91    }
92
93    fn from_metric_value(
94        metric_type: DataDogMetricType,
95        key: Key,
96        values: Vec<DataDogMetricValue>,
97        global_tags: &[Label],
98    ) -> Self {
99        DataDogMetric {
100            metric: key.name().to_string(),
101            metric_type,
102            points: values,
103            timestamp: Utc::now().timestamp(),
104            tags: global_tags
105                .iter()
106                .chain(key.labels())
107                .map(|l| format!("{}:{}", l.key(), l.value()))
108                .collect(),
109        }
110    }
111
112    pub(crate) fn to_metric_lines(&self) -> Vec<DataDogMetricLine> {
113        self.points
114            .iter()
115            .map(|v| DataDogMetricLine {
116                name: self.metric.to_string(),
117                value: v.clone(),
118                timestamp: self.timestamp,
119                tags: self.tags.clone(),
120            })
121            .collect()
122    }
123}
124
125/// StdOut representation of a metric
126#[derive(Debug, Serialize, Deserialize, Clone)]
127pub struct DataDogMetricLine {
128    /// Metric name
129    #[serde(rename = "m")]
130    pub name: String,
131    /// Metric value
132    #[serde(rename = "v")]
133    pub value: DataDogMetricValue,
134    /// Metric timestamp
135    #[serde(rename = "e")]
136    pub timestamp: i64,
137    /// Metric tags
138    #[serde(rename = "t")]
139    pub tags: Vec<String>,
140}
141
142/// DataDog API Post Body
143#[derive(Debug, Serialize, Clone)]
144pub struct DataDogApiPost<'a> {
145    /// Metric series
146    pub series: &'a [DataDogSeries],
147}
148
149/// DataDog Metric Series
150#[skip_serializing_none]
151#[derive(Debug, Serialize, Deserialize, Clone)]
152pub struct DataDogSeries {
153    /// Metric interval
154    pub interval: Option<i64>,
155    /// Metric name
156    pub metric: String,
157    /// Metric time series
158    pub points: Vec<(i64, DataDogMetricValue)>,
159    /// Metric tags
160    pub tags: Vec<String>,
161    /// Metric type
162    #[serde(rename = "type")]
163    pub metric_type: DataDogMetricType,
164}
165
166impl DataDogSeries {
167    /// Create metric series from metric
168    pub fn new(m: DataDogMetric) -> Vec<DataDogSeries> {
169        m.points
170            .chunks(3)
171            .map(|points| DataDogSeries {
172                interval: None,
173                metric: m.metric.to_owned(),
174                points: points.iter().map(|v| (m.timestamp, v.to_owned())).collect(),
175                tags: m.tags.to_owned(),
176                metric_type: m.metric_type.to_owned(),
177            })
178            .collect_vec()
179    }
180}