1use std::time::Duration;
5use std::{num::NonZeroUsize, str::FromStr};
6
7use serde::{Deserialize, Serialize};
8
9use crate::defaults::DEFAULT_STRING_INTERNER_SIZE_BYTES;
10use crate::{domains::dogstatsd::OriginTagCardinality, Error};
11
12#[derive(Clone, Debug, Default, PartialEq, Serialize)]
14pub struct Domain {
15 pub receiver: Receiver,
17
18 pub metrics: Metrics,
20
21 pub traces: Traces,
23
24 pub proxy: Proxy,
26
27 pub contexts: Contexts,
29}
30
31pub const DEFAULT_DELTA_TTL: Duration = Duration::from_secs(3600);
33
34#[derive(Clone, Debug, PartialEq, Serialize)]
36pub struct Metrics {
37 pub tag_cardinality: OriginTagCardinality,
42
43 pub histogram_mode: HistogramMode,
45
46 pub send_histogram_aggregations: bool,
50
51 pub resource_attributes_as_tags: bool,
54
55 pub instrumentation_scope_metadata_as_tags: bool,
60
61 pub sums: Sums,
63
64 pub tags: String,
69
70 pub summaries: Summaries,
72
73 pub delta_ttl: Duration,
76}
77
78impl Default for Metrics {
79 fn default() -> Self {
80 Self {
81 tag_cardinality: OriginTagCardinality::default(),
82 histogram_mode: HistogramMode::default(),
83 send_histogram_aggregations: false,
84 resource_attributes_as_tags: false,
85 instrumentation_scope_metadata_as_tags: true,
86 sums: Sums::default(),
87 tags: String::new(),
88 summaries: Summaries::default(),
89 delta_ttl: DEFAULT_DELTA_TTL,
90 }
91 }
92}
93
94#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Serialize)]
96pub enum HistogramMode {
97 NoBuckets,
99
100 Counters,
102
103 #[default]
105 Distributions,
106}
107
108impl FromStr for HistogramMode {
109 type Err = Error;
110
111 fn from_str(value: &str) -> Result<Self, Self::Err> {
112 match value {
113 "nobuckets" => Ok(Self::NoBuckets),
114 "counters" => Ok(Self::Counters),
115 "distributions" => Ok(Self::Distributions),
116 other => Err(Error::new_without_source(format!(
117 "unknown histogram mode `{other}`; expected `nobuckets`, `counters`, or `distributions`"
118 ))),
119 }
120 }
121}
122
123#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Serialize)]
125pub enum CumulativeMonotonicMode {
126 #[default]
128 ToDelta,
129
130 RawValue,
132}
133
134impl FromStr for CumulativeMonotonicMode {
135 type Err = Error;
136
137 fn from_str(value: &str) -> Result<Self, Self::Err> {
138 match value {
139 "to_delta" => Ok(Self::ToDelta),
140 "raw_value" => Ok(Self::RawValue),
141 other => Err(Error::new_without_source(format!(
142 "unknown cumulative monotonic sum mode `{other}`; expected `to_delta` or `raw_value`"
143 ))),
144 }
145 }
146}
147
148#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Serialize)]
150pub enum InitialCumulativeMonotonicValue {
151 #[default]
153 Auto,
154
155 Drop,
157
158 Keep,
160}
161
162impl FromStr for InitialCumulativeMonotonicValue {
163 type Err = Error;
164
165 fn from_str(value: &str) -> Result<Self, Self::Err> {
166 match value {
167 "auto" => Ok(Self::Auto),
168 "drop" => Ok(Self::Drop),
169 "keep" => Ok(Self::Keep),
170 other => Err(Error::new_without_source(format!(
171 "unknown initial cumulative monotonic value `{other}`; expected `auto`, `drop`, or `keep`"
172 ))),
173 }
174 }
175}
176
177#[derive(Clone, Debug, Default, PartialEq, Serialize)]
179pub struct Sums {
180 pub cumulative_monotonic_mode: CumulativeMonotonicMode,
185
186 pub initial_cumulative_monotonic_value: InitialCumulativeMonotonicValue,
191}
192
193#[derive(Clone, Debug, Default, PartialEq, Serialize)]
195pub struct Summaries {
196 pub mode: SummaryMode,
201}
202
203#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Serialize)]
205pub enum SummaryMode {
206 #[default]
208 Gauges,
209
210 NoQuantiles,
212}
213
214impl FromStr for SummaryMode {
215 type Err = Error;
216
217 fn from_str(value: &str) -> Result<Self, Self::Err> {
218 match value {
219 "gauges" => Ok(Self::Gauges),
220 "noquantiles" => Ok(Self::NoQuantiles),
221 other => Err(Error::new_without_source(format!(
222 "unknown summary mode `{other}`; expected `gauges` or `noquantiles`"
223 ))),
224 }
225 }
226}
227
228#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Serialize)]
230#[serde(rename_all = "lowercase")]
231pub enum GrpcTransport {
232 #[default]
234 Tcp,
235 Unix,
237}
238
239impl GrpcTransport {
240 pub const fn as_str(self) -> &'static str {
242 match self {
243 Self::Tcp => "tcp",
244 Self::Unix => "unix",
245 }
246 }
247}
248
249impl FromStr for GrpcTransport {
250 type Err = Error;
251
252 fn from_str(value: &str) -> Result<Self, Self::Err> {
253 match value {
254 "tcp" => Ok(Self::Tcp),
255 "unix" => Ok(Self::Unix),
256 other => Err(Error::new_without_source(format!(
257 "unknown gRPC transport `{other}`; expected `tcp` or `unix`"
258 ))),
259 }
260 }
261}
262
263#[derive(Clone, Debug, Default, PartialEq, Serialize)]
265pub struct Receiver {
266 pub logs_enabled: bool,
268
269 pub metrics_enabled: bool,
271
272 pub grpc: GrpcReceiver,
274
275 pub http: HttpReceiver,
277}
278
279pub const DEFAULT_GRPC_MAX_RECV_MSG_SIZE_MIB: u64 = 4;
285
286#[derive(Clone, Debug, Default, PartialEq, Serialize)]
288pub struct GrpcReceiver {
289 pub endpoint: String,
291
292 pub max_recv_msg_size_mib: u64,
294
295 pub transport: GrpcTransport,
297}
298
299#[derive(Clone, Debug, PartialEq, Serialize)]
301pub struct HttpReceiver {
302 pub endpoint: String,
304
305 pub transport: String,
308
309 pub cors: Cors,
311}
312
313impl Default for HttpReceiver {
314 fn default() -> Self {
315 Self {
316 endpoint: String::new(),
318 transport: "tcp".to_string(),
319 cors: Cors::default(),
320 }
321 }
322}
323
324#[derive(Clone, Debug, Default, PartialEq, Serialize)]
326pub struct Cors {
327 pub allowed_origins: Vec<String>,
331
332 pub allowed_headers: Vec<String>,
336
337 pub exposed_headers: Vec<String>,
340
341 pub max_age: u64,
344}
345
346#[derive(Clone, Debug, PartialEq, Serialize)]
348pub struct Traces {
349 pub enabled: bool,
351
352 pub internal_port: u16,
354
355 pub probabilistic_sampler_sampling_percentage: f64,
357
358 pub string_interner_size: NonZeroUsize,
362
363 pub enable_compute_top_level_by_span_kind: bool,
366
367 pub ignore_missing_datadog_fields: bool,
370}
371
372impl Default for Traces {
373 fn default() -> Self {
374 Self {
375 enabled: false,
376 internal_port: 0,
377 probabilistic_sampler_sampling_percentage: 0.0,
378 string_interner_size: DEFAULT_STRING_INTERNER_SIZE_BYTES,
379 enable_compute_top_level_by_span_kind: true,
380 ignore_missing_datadog_fields: false,
381 }
382 }
383}
384
385#[derive(Clone, Debug, Default, PartialEq, Serialize)]
387pub struct Proxy {
388 pub enabled: bool,
390
391 pub logs_enabled: bool,
393
394 pub metrics_enabled: bool,
396
397 pub traces_enabled: bool,
399
400 pub grpc_endpoint: String,
402}
403
404#[derive(Clone, Debug, PartialEq, Serialize)]
406pub struct Contexts {
407 pub allow_context_heap_allocs: bool,
410
411 pub cached_contexts_limit: usize,
413
414 pub cached_tagsets_limit: usize,
416
417 pub string_interner_size: u64,
419}
420
421impl Default for Contexts {
422 fn default() -> Self {
423 Self {
424 allow_context_heap_allocs: true,
425 cached_contexts_limit: 500_000,
426 cached_tagsets_limit: 500_000,
427 string_interner_size: 2 * 1024 * 1024,
428 }
429 }
430}
431
432#[cfg(test)]
433mod tests {
434 use super::{CumulativeMonotonicMode, GrpcTransport, InitialCumulativeMonotonicValue};
435
436 #[test]
437 fn grpc_transport_parses_known_values() {
438 assert_eq!("tcp".parse::<GrpcTransport>().unwrap(), GrpcTransport::Tcp);
439 assert_eq!("unix".parse::<GrpcTransport>().unwrap(), GrpcTransport::Unix);
440 }
441
442 #[test]
443 fn grpc_transport_rejects_unknown_values() {
444 assert!("tcp4".parse::<GrpcTransport>().is_err());
445 assert!("udp".parse::<GrpcTransport>().is_err());
446 }
447
448 #[test]
449 fn cumulative_monotonic_mode_parses_known_values() {
450 assert_eq!(
451 "to_delta"
452 .parse::<CumulativeMonotonicMode>()
453 .expect("to_delta should parse"),
454 CumulativeMonotonicMode::ToDelta
455 );
456 assert_eq!(
457 "raw_value"
458 .parse::<CumulativeMonotonicMode>()
459 .expect("raw_value should parse"),
460 CumulativeMonotonicMode::RawValue
461 );
462 }
463
464 #[test]
465 fn cumulative_monotonic_mode_rejects_unknown_values() {
466 let error = "unsupported"
467 .parse::<CumulativeMonotonicMode>()
468 .expect_err("unsupported mode should be rejected");
469
470 assert_eq!(
471 error.to_string(),
472 "unknown cumulative monotonic sum mode `unsupported`; expected `to_delta` or `raw_value`"
473 );
474 }
475
476 #[test]
477 fn initial_cumulative_monotonic_value_parses_known_values() {
478 for (value, expected) in [
479 ("auto", InitialCumulativeMonotonicValue::Auto),
480 ("drop", InitialCumulativeMonotonicValue::Drop),
481 ("keep", InitialCumulativeMonotonicValue::Keep),
482 ] {
483 assert_eq!(
484 value
485 .parse::<InitialCumulativeMonotonicValue>()
486 .expect("known value should parse"),
487 expected
488 );
489 }
490 }
491
492 #[test]
493 fn initial_cumulative_monotonic_value_rejects_unknown_values() {
494 let error = "unsupported"
495 .parse::<InitialCumulativeMonotonicValue>()
496 .expect_err("unsupported value should be rejected");
497
498 assert_eq!(
499 error.to_string(),
500 "unknown initial cumulative monotonic value `unsupported`; expected `auto`, `drop`, or `keep`"
501 );
502 }
503}