1#![allow(dead_code)]
5#![allow(unreachable_pub)]
6
7use std::sync::LazyLock;
8
9use regex::Regex;
10use serde::{Deserialize, Serialize};
11use vector_lib::{
12 event::DatadogMetricOriginMetadata, schema::meaning, sensitive_string::SensitiveString,
13};
14
15pub(crate) const DD_US_SITE: &str = "datadoghq.com";
16pub(crate) const DD_EU_SITE: &str = "datadoghq.eu";
17
18pub const DDTAGS: &str = "ddtags";
20pub const MESSAGE: &str = "message";
22
23pub const DD_RESERVED_SEMANTIC_ATTRS: [(&str, &str); 6] = [
27 (meaning::SEVERITY, "status"), (meaning::TIMESTAMP, "timestamp"),
29 (meaning::HOST, "hostname"),
30 (meaning::SERVICE, "service"),
31 (meaning::SOURCE, "ddsource"),
32 (meaning::TAGS, DDTAGS),
33];
34
35pub fn is_reserved_attribute(attr: &str) -> bool {
37 DD_RESERVED_SEMANTIC_ATTRS
38 .iter()
39 .any(|(_, attr_str)| &attr == attr_str)
40}
41
42#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
44pub struct DatadogSeriesMetric {
45 pub metric: String,
47 pub r#type: DatadogMetricType,
49 pub interval: Option<u32>,
51 pub points: Vec<DatadogPoint<f64>>,
53 pub tags: Option<Vec<String>>,
55 #[serde(skip_serializing_if = "Option::is_none")]
57 pub host: Option<String>,
58 #[serde(skip_serializing_if = "Option::is_none")]
60 pub source_type_name: Option<String>,
61 #[serde(skip_serializing_if = "Option::is_none")]
63 pub device: Option<String>,
64 #[serde(skip_serializing_if = "Option::is_none")]
66 pub metadata: Option<DatadogSeriesMetricMetadata>,
67}
68
69#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
71pub struct DatadogSeriesMetricMetadata {
72 #[serde(skip_serializing_if = "Option::is_none")]
73 pub(crate) origin: Option<DatadogMetricOriginMetadata>,
74}
75
76#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
78#[serde(rename_all = "snake_case")]
79pub enum DatadogMetricType {
80 Gauge,
82 Count,
84 Rate,
86}
87
88#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
90pub struct DatadogPoint<T>(pub i64, pub T);
91
92pub(crate) fn get_api_base_endpoint(endpoint: Option<&str>, site: &str) -> String {
96 endpoint.map_or_else(|| format!("https://api.{site}"), compute_api_endpoint)
97}
98
99fn compute_api_endpoint(endpoint: &str) -> String {
104 static DOMAIN_REGEX: LazyLock<Regex> = LazyLock::new(|| {
108 Regex::new(r"(?:[a-z]{2}\d\.)?(datadoghq\.[a-z]+|ddog-gov\.com)/*$")
109 .expect("Could not build Datadog domain regex")
110 });
111
112 if let Some(caps) = DOMAIN_REGEX.captures(endpoint) {
113 format!("https://api.{}", &caps[1])
114 } else {
115 endpoint.into()
116 }
117}
118
119#[derive(Clone, Debug, Derivative)]
121#[derivative(Default)]
122pub struct Options {
123 #[derivative(Default(value = "default_api_key()"))]
127 pub api_key: Option<SensitiveString>,
128
129 #[derivative(Default(value = "default_site()"))]
133 pub site: String,
134}
135
136fn default_api_key() -> Option<SensitiveString> {
137 std::env::var("DD_API_KEY").ok().map(Into::into)
138}
139
140pub(crate) fn default_site() -> String {
141 std::env::var("DD_SITE").unwrap_or(DD_US_SITE.to_string())
142}
143
144#[cfg(test)]
145mod tests {
146 use similar_asserts::assert_eq;
147
148 use super::*;
149
150 #[test]
151 fn computes_correct_api_endpoint() {
152 assert_eq!(
153 compute_api_endpoint("https://http-intake.logs.datadoghq.com"),
154 "https://api.datadoghq.com"
155 );
156 assert_eq!(
157 compute_api_endpoint("https://http-intake.logs.datadoghq.com/"),
158 "https://api.datadoghq.com"
159 );
160 assert_eq!(
161 compute_api_endpoint("http://http-intake.logs.datadoghq.com/"),
162 "https://api.datadoghq.com"
163 );
164 assert_eq!(
165 compute_api_endpoint("https://anythingelse.datadoghq.com/"),
166 "https://api.datadoghq.com"
167 );
168 assert_eq!(
169 compute_api_endpoint("https://this.datadoghq.eu/"),
170 "https://api.datadoghq.eu"
171 );
172 assert_eq!(
173 compute_api_endpoint("http://datadog.com/"),
174 "http://datadog.com/"
175 );
176 }
177
178 #[test]
179 fn gets_correct_api_base_endpoint() {
180 assert_eq!(
181 get_api_base_endpoint(None, DD_US_SITE),
182 "https://api.datadoghq.com"
183 );
184 assert_eq!(
185 get_api_base_endpoint(None, "datadog.net"),
186 "https://api.datadog.net"
187 );
188 assert_eq!(
189 get_api_base_endpoint(Some("https://logs.datadoghq.eu"), DD_US_SITE),
190 "https://api.datadoghq.eu"
191 );
192 }
193}