saluki_core/data_model/event/metric/
metadata.rs

1use std::{fmt, sync::Arc};
2
3use stringtheory::MetaString;
4
5const ORIGIN_PRODUCT_AGENT: u32 = 10;
6const ORIGIN_SUBPRODUCT_DOGSTATSD: u32 = 10;
7const ORIGIN_SUBPRODUCT_INTEGRATION: u32 = 11;
8const ORIGIN_PRODUCT_DETAIL_NONE: u32 = 0;
9
10/// Metric metadata.
11///
12/// Metadata includes all information that's not specifically related to the context or value of the metric itself,
13/// such as sample rate and timestamp.
14#[must_use]
15#[derive(Clone, Debug, Default, Eq, PartialEq)]
16pub struct MetricMetadata {
17    /// The metric origin.
18    // TODO: only optional so we can default? seems like we always have one
19    pub origin: Option<MetricOrigin>,
20
21    /// The unit of the metric values, if known.
22    ///
23    /// This is set for DogStatsD timing metrics (`ms` type), which carry an implicit unit of `"millisecond"`. For all
24    /// other metric types the unit is empty.
25    pub unit: MetaString,
26}
27
28impl MetricMetadata {
29    /// Returns the metric origin.
30    pub fn origin(&self) -> Option<&MetricOrigin> {
31        self.origin.as_ref()
32    }
33
34    /// Set the metric origin to the given source type.
35    ///
36    /// Indicates the source of the metric, such as the product or service that emitted it, or the source component
37    /// itself that emitted it.
38    ///
39    /// This variant is specifically for use in builder-style APIs.
40    pub fn with_source_type(mut self, source_type: impl Into<Option<Arc<str>>>) -> Self {
41        self.origin = source_type.into().map(MetricOrigin::SourceType);
42        self
43    }
44
45    /// Set the metric origin to the given source type.
46    ///
47    /// Indicates the source of the metric, such as the product or service that emitted it, or the source component
48    /// itself that emitted it.
49    pub fn set_source_type(&mut self, source_type: impl Into<Option<Arc<str>>>) {
50        self.origin = source_type.into().map(MetricOrigin::SourceType);
51    }
52
53    /// Set the metric origin to the given origin.
54    ///
55    /// Indicates the source of the metric, such as the product or service that emitted it, or the source component
56    /// itself that emitted it.
57    ///
58    /// This variant is specifically for use in builder-style APIs.
59    pub fn with_origin(mut self, origin: impl Into<Option<MetricOrigin>>) -> Self {
60        self.origin = origin.into();
61        self
62    }
63
64    /// Set the metric origin to the given origin.
65    ///
66    /// Indicates the source of the metric, such as the product or service that emitted it, or the source component
67    /// itself that emitted it.
68    pub fn set_origin(&mut self, origin: impl Into<Option<MetricOrigin>>) {
69        self.origin = origin.into();
70    }
71
72    /// Returns the unit of the metric values, if set.
73    pub fn unit(&self) -> Option<&str> {
74        if self.unit.is_empty() {
75            None
76        } else {
77            Some(&self.unit)
78        }
79    }
80
81    /// Set the unit of the metric values.
82    ///
83    /// This variant is specifically for use in builder-style APIs.
84    pub fn with_unit(mut self, unit: impl Into<MetaString>) -> Self {
85        self.unit = unit.into();
86        self
87    }
88
89    /// Set the unit of the metric values.
90    pub fn set_unit(&mut self, unit: impl Into<MetaString>) {
91        self.unit = unit.into();
92    }
93}
94
95impl fmt::Display for MetricMetadata {
96    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
97        if let Some(origin) = &self.origin {
98            write!(f, " origin={}", origin)?;
99        }
100
101        if !self.unit.is_empty() {
102            write!(f, " unit={}", self.unit)?;
103        }
104
105        Ok(())
106    }
107}
108
109// TODO: This is not technically right.
110//
111// In practice, the Datadog Agent _does_ ship metrics with both source type name and origin metadata, although perhaps
112// luckily, that is only the case for check metrics, which we don't deal with in ADP (yet).
113//
114// Eventually, we likely will have to consider exposing both of these fields.
115
116/// Categorical origin of a metric.
117///
118/// This is used to describe, in high-level terms, where a metric originated from, such as the specific software package
119/// or library that emitted. This is distinct from the `OriginEntity`, which describes the specific sender of the metric.
120#[derive(Clone, Debug, Eq, PartialEq)]
121pub enum MetricOrigin {
122    /// Originated from a generic source.
123    ///
124    /// This is used to set the origin of a metric as the source component type itself, such as `dogstatsd` or `otel`,
125    /// when richer origin metadata isn't available.
126    SourceType(Arc<str>),
127
128    /// Originated from a specific product/subproduct, with product-specific detail.
129    OriginMetadata {
130        /// Product.
131        product: u32,
132
133        /// Subproduct.
134        subproduct: u32,
135
136        /// Product detail.
137        product_detail: u32,
138    },
139}
140
141impl MetricOrigin {
142    /// Creates a `MetricsOrigin` for any metric ingested via DogStatsD.
143    pub fn dogstatsd() -> Self {
144        Self::OriginMetadata {
145            product: ORIGIN_PRODUCT_AGENT,
146            subproduct: ORIGIN_SUBPRODUCT_DOGSTATSD,
147            product_detail: ORIGIN_PRODUCT_DETAIL_NONE,
148        }
149    }
150
151    /// Creates a `MetricsOrigin` for any metric that originated via an JXM check integration.
152    pub fn jmx_check(check_name: &str) -> Self {
153        let product_detail = jmx_check_name_to_product_detail(check_name);
154
155        Self::OriginMetadata {
156            product: ORIGIN_PRODUCT_AGENT,
157            subproduct: ORIGIN_SUBPRODUCT_INTEGRATION,
158            product_detail,
159        }
160    }
161
162    /// Returns `true` if the origin of the metric is DogStatsD.
163    pub fn is_dogstatsd(&self) -> bool {
164        matches!(self, Self::OriginMetadata { subproduct, .. } if *subproduct == ORIGIN_SUBPRODUCT_DOGSTATSD)
165    }
166}
167
168impl fmt::Display for MetricOrigin {
169    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
170        match self {
171            Self::SourceType(source_type) => write!(f, "source_type={}", source_type),
172            Self::OriginMetadata {
173                product,
174                subproduct,
175                product_detail,
176            } => write!(
177                f,
178                "product={} subproduct={} product_detail={}",
179                product_id_to_str(*product),
180                subproduct_id_to_str(*subproduct),
181                product_detail_id_to_str(*product_detail),
182            ),
183        }
184    }
185}
186
187fn jmx_check_name_to_product_detail(check_name: &str) -> u32 {
188    // Taken from Datadog Agent mappings:
189    // https://github.com/DataDog/datadog-agent/blob/fd3a119bda125462d578e0004f1370ee019ce2d5/pkg/serializer/internal/metrics/origin_mapping.go#L41
190    match check_name {
191        "jmx-custom-check" => 9,
192        "activemq" => 12,
193        "cassandra" => 28,
194        "confluent_platform" => 40,
195        "hazelcast" => 70,
196        "hive" => 73,
197        "hivemq" => 74,
198        "hudi" => 76,
199        "ignite" => 83,
200        "jboss_wildfly" => 87,
201        "kafka" => 90,
202        "presto" => 130,
203        "solr" => 147,
204        "sonarqube" => 148,
205        "tomcat" => 163,
206        "weblogic" => 172,
207        _ => 0,
208    }
209}
210
211fn product_id_to_str(product_id: u32) -> &'static str {
212    match product_id {
213        ORIGIN_PRODUCT_AGENT => "agent",
214        _ => "unknown_product",
215    }
216}
217
218fn subproduct_id_to_str(subproduct_id: u32) -> &'static str {
219    match subproduct_id {
220        ORIGIN_SUBPRODUCT_DOGSTATSD => "dogstatsd",
221        ORIGIN_SUBPRODUCT_INTEGRATION => "integration",
222        _ => "unknown_subproduct",
223    }
224}
225
226fn product_detail_id_to_str(product_detail_id: u32) -> &'static str {
227    match product_detail_id {
228        // TODO: Map the JMX check integration product detail IDs to their respective names.
229        ORIGIN_PRODUCT_DETAIL_NONE => "none",
230        _ => "unknown_product_detail",
231    }
232}