agent_data_plane_config/domains/dogstatsd.rs
1//! DogStatsD domain: source listeners, parsing, origin detection, aggregation, mapping, filters
2//! (some dynamic-capable), and debug logging.
3
4use std::collections::HashMap;
5use std::fmt;
6use std::num::{NonZeroU64, NonZeroUsize};
7use std::path::PathBuf;
8use std::str::FromStr;
9use std::time::Duration;
10
11use serde::{Deserialize, Serialize};
12
13use crate::defaults::{
14 DEFAULT_AGGREGATE_CONTEXT_LIMIT, DEFAULT_AGGREGATE_FLUSH_INTERVAL,
15 DEFAULT_AGGREGATE_PASSTHROUGH_IDLE_FLUSH_TIMEOUT, DEFAULT_AGGREGATE_WINDOW_DURATION_SECONDS,
16 DEFAULT_DOGSTATSD_ALLOW_CONTEXT_HEAP_ALLOCS, DEFAULT_DOGSTATSD_AUTOSCALE_UDP_LISTENERS,
17 DEFAULT_DOGSTATSD_BUFFER_COUNT, DEFAULT_DOGSTATSD_BUFFER_COUNT_MAX, DEFAULT_DOGSTATSD_CACHED_CONTEXTS_LIMIT,
18 DEFAULT_DOGSTATSD_CACHED_TAGSETS_LIMIT, DEFAULT_DOGSTATSD_MAPPER_STRING_INTERNER_SIZE_BYTES,
19 DEFAULT_DOGSTATSD_MINIMUM_SAMPLE_RATE, DEFAULT_DOGSTATSD_PERMISSIVE_DECODING, DEFAULT_DOGSTATSD_TCP_PORT,
20};
21use crate::Error;
22
23// TODO: better name than Domain? Pipeline? Topology? BlueprintConfig?
24/// Resolved DogStatsD configuration.
25#[derive(Clone, Debug, Default, PartialEq, Serialize)]
26pub struct Domain {
27 /// Source listeners and packet-decoding options.
28 pub listeners: Listeners,
29
30 /// Origin detection and tag cardinality.
31 pub origin: OriginDetection,
32
33 /// Context cache sizing and the sample-rate floor.
34 pub contexts: Contexts,
35
36 /// Metric aggregation window and flush behavior.
37 pub aggregation: Aggregation,
38
39 /// Metric-name mapper.
40 pub mapper: Mapper,
41
42 /// Which payload types are emitted.
43 pub enable_payloads: EnablePayloads,
44
45 /// Metric-name filtering.
46 pub metric_filter: MetricFilter,
47
48 /// Metric namespace prefixing.
49 pub prefix_filter: PrefixFilter,
50
51 /// Per-metric tag include/exclude rules.
52 pub tag_filterlist: Vec<MetricTagFilterEntry>,
53
54 /// Per-metric tag value allow-list rules.
55 pub tag_value_allowlist: Vec<MetricTagValueAllowlistEntry>,
56
57 /// Extra tags added to every metric.
58 pub tags: Vec<String>,
59
60 /// Telemetry emitted by the DogStatsD source.
61 pub telemetry: Telemetry,
62
63 /// Debug-log and verbose-log settings for the DogStatsD source.
64 pub debug_log: DebugLog,
65}
66
67/// Source listeners and packet-decoding options.
68#[derive(Clone, Debug, PartialEq, Serialize)]
69pub struct Listeners {
70 /// UDP port DogStatsD listens on.
71 pub port: u16,
72
73 /// TCP port DogStatsD listens on. (not in Datadog Agent config schema)
74 ///
75 /// Defaults to `0`, which disables TCP.
76 pub tcp_port: u16,
77
78 /// Path of the Unix datagram socket DogStatsD listens on.
79 pub socket: Option<String>,
80
81 /// Path of the Unix stream socket DogStatsD listens on.
82 pub stream_socket: Option<String>,
83
84 /// Windows named pipe name DogStatsD listens on. Unset when no named pipe is configured.
85 pub pipe_name: Option<String>,
86
87 /// SDDL security descriptor applied to the Windows named pipe listener.
88 pub windows_pipe_security_descriptor: String,
89
90 /// Whether the UDP listener accepts traffic from non-local addresses.
91 pub non_local_traffic: bool,
92
93 /// Size, in bytes, requested for the socket receive buffer.
94 pub so_rcvbuf: usize,
95
96 /// Size, in bytes, of each packet receive buffer.
97 pub buffer_size: usize,
98
99 /// Number of receive buffers allocated. (not in Datadog Agent config schema)
100 pub buffer_count: usize,
101
102 /// Maximum number of receive buffers. (not in Datadog Agent config schema)
103 pub buffer_count_max: usize,
104
105 /// Number of connectionless packet decoder workers.
106 pub workers_count: usize,
107
108 /// Whether to bind multiple UDP sockets via `SO_REUSEPORT`. (not in Datadog Agent config
109 /// schema)
110 pub autoscale_udp_listeners: bool,
111
112 /// Path a traffic capture is written to or replayed from.
113 pub capture_path: PathBuf,
114
115 /// Maximum number of captured packets queued for persistence.
116 ///
117 /// The capture writer raises a depth below its own minimum, so the default, 0, selects that minimum.
118 pub capture_depth: usize,
119
120 /// End-of-line markers required to terminate a stream-socket message.
121 pub eol_required: Vec<String>,
122
123 /// Whether to log stream messages that exceed the buffer size.
124 pub stream_log_too_big: bool,
125
126 /// Whether startup requires at least one active DogStatsD listener.
127 ///
128 /// Defaults to `false`. A listener that can't be created is logged and skipped either way. When `true`, startup
129 /// fails if no configured listener can be created. When `false`, the source keeps running without listeners.
130 ///
131 /// Enable this when a process supervisor should detect and restart a process that can't receive DogStatsD
132 /// traffic.
133 pub require_listener: bool,
134
135 /// Whether to relax decoder strictness on malformed packets. (not in Datadog Agent config
136 /// schema)
137 pub permissive_decoding: bool,
138
139 /// Host that received metrics are additionally forwarded to.
140 pub forward_host: Option<String>,
141
142 /// Port that received metrics are additionally forwarded to.
143 pub forward_port: u16,
144}
145
146impl Default for Listeners {
147 fn default() -> Self {
148 Self {
149 // Saluki-only settings retain these defaults when unset.
150 buffer_count: DEFAULT_DOGSTATSD_BUFFER_COUNT,
151 buffer_count_max: DEFAULT_DOGSTATSD_BUFFER_COUNT_MAX,
152 permissive_decoding: DEFAULT_DOGSTATSD_PERMISSIVE_DECODING,
153 tcp_port: DEFAULT_DOGSTATSD_TCP_PORT,
154 autoscale_udp_listeners: DEFAULT_DOGSTATSD_AUTOSCALE_UDP_LISTENERS,
155 // Witnessed settings are overwritten during translation.
156 port: 0,
157 socket: None,
158 stream_socket: None,
159 pipe_name: None,
160 windows_pipe_security_descriptor: String::new(),
161 non_local_traffic: false,
162 so_rcvbuf: 0,
163 buffer_size: 0,
164 workers_count: 0,
165 capture_path: PathBuf::new(),
166 capture_depth: 0,
167 eol_required: Vec::new(),
168 stream_log_too_big: false,
169 require_listener: false,
170 forward_host: None,
171 forward_port: 0,
172 }
173 }
174}
175
176/// Origin detection and tag cardinality.
177#[derive(Clone, Debug, Default, PartialEq, Serialize)]
178pub struct OriginDetection {
179 /// Whether origin detection tags metrics with their source workload.
180 pub detection: bool,
181
182 /// Whether client-supplied origin information is honored.
183 pub detection_client: bool,
184
185 /// Whether the unified origin-detection scheme is used.
186 pub unified: bool,
187
188 /// Whether a client may opt out of origin detection per metric.
189 pub optout_enabled: bool,
190
191 /// Whether a client-supplied entity ID takes precedence over the detected origin.
192 pub entity_id_precedence: bool,
193
194 /// Tag cardinality applied to origin-detected tags, when a payload doesn't specify one itself.
195 pub tag_cardinality: OriginTagCardinality,
196}
197
198/// Tag cardinality applied during origin detection.
199#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Serialize)]
200pub enum OriginTagCardinality {
201 #[default]
202 Low,
203 Orchestrator,
204 High,
205 None,
206}
207
208impl FromStr for OriginTagCardinality {
209 type Err = Error;
210
211 fn from_str(value: &str) -> Result<Self, Self::Err> {
212 // The Datadog Agent lower-cases the value before matching, and accepts `orch` as a shorthand
213 // for `orchestrator`.
214 match value.to_ascii_lowercase().as_str() {
215 "low" => Ok(Self::Low),
216 "orch" | "orchestrator" => Ok(Self::Orchestrator),
217 "high" => Ok(Self::High),
218 "none" => Ok(Self::None),
219 other => Err(Error::new_without_source(format!(
220 "unknown tag cardinality `{other}`; expected low, orch, orchestrator, high, or none"
221 ))),
222 }
223 }
224}
225
226/// Telemetry emitted by the DogStatsD source.
227#[derive(Clone, Debug, Default, PartialEq, Serialize)]
228pub struct Telemetry {
229 /// Whether processed-metric telemetry is broken down by detected origin.
230 pub origin_breakdown: bool,
231}
232
233/// Context cache sizing and sample-rate floor.
234#[derive(Clone, Debug, PartialEq, Serialize)]
235pub struct Contexts {
236 /// Maximum number of metric contexts held in the cache. (not in Datadog Agent config schema)
237 pub cached_contexts_limit: usize,
238
239 /// Maximum number of tagsets held in the cache. (not in Datadog Agent config schema)
240 pub cached_tagsets_limit: usize,
241
242 /// Number of entries the context string interner holds.
243 pub string_interner_size: u64,
244
245 /// Byte budget for the context string interner, overriding the entry count when set. (not in
246 /// Datadog Agent config schema)
247 pub string_interner_size_bytes: Option<u64>,
248
249 /// Whether contexts may be heap-allocated when the interner is full. (not in Datadog Agent
250 /// config schema)
251 pub allow_context_heap_allocs: bool,
252
253 /// Lowest sample rate honored; the decoder warns and clamps anything below it. (not in Datadog
254 /// Agent config schema)
255 pub minimum_sample_rate: f64,
256}
257
258impl Default for Contexts {
259 fn default() -> Self {
260 Self {
261 // Saluki-only settings retain these defaults when unset.
262 cached_contexts_limit: DEFAULT_DOGSTATSD_CACHED_CONTEXTS_LIMIT,
263 cached_tagsets_limit: DEFAULT_DOGSTATSD_CACHED_TAGSETS_LIMIT,
264 string_interner_size_bytes: None,
265 allow_context_heap_allocs: DEFAULT_DOGSTATSD_ALLOW_CONTEXT_HEAP_ALLOCS,
266 minimum_sample_rate: DEFAULT_DOGSTATSD_MINIMUM_SAMPLE_RATE,
267 // Witnessed settings are overwritten during translation.
268 string_interner_size: 0,
269 }
270 }
271}
272
273/// Metric aggregation window and flush behavior.
274#[derive(Clone, Debug, PartialEq, Serialize)]
275pub struct Aggregation {
276 /// Length, in seconds, of each aggregation window. (not in Datadog Agent config schema)
277 pub window_duration_seconds: NonZeroU64,
278
279 /// Maximum number of contexts held per aggregation window. (not in Datadog Agent config schema)
280 pub context_limit: usize,
281
282 /// How often aggregated metrics are flushed. (not in Datadog Agent config schema)
283 pub flush_interval: Duration,
284
285 /// Whether windows that are still open are flushed on shutdown.
286 ///
287 /// Set by the Datadog `dogstatsd_flush_incomplete_buckets` key.
288 pub flush_open_windows: bool,
289
290 /// How long the no-aggregation passthrough waits before flushing while idle. (not in Datadog
291 /// Agent config schema)
292 pub passthrough_idle_flush_timeout: Duration,
293
294 /// How long, in seconds, a counter value is retained after its last update before expiring.
295 ///
296 /// Set by the Datadog `dogstatsd_expiry_seconds` key. A value of `0` disables zero-value counter
297 /// emission.
298 pub counter_expiry_seconds: Option<u64>,
299
300 /// How long, in seconds, a context is retained after its last update before expiring.
301 pub context_expiry_seconds: u64,
302
303 /// Whether metrics bypass aggregation and are forwarded directly.
304 pub no_aggregation_pipeline: bool,
305
306 /// Capacity of the aggregator's tag-filter result cache.
307 pub aggregator_tag_filter_cache_capacity: usize,
308}
309
310impl Default for Aggregation {
311 fn default() -> Self {
312 Self {
313 // Saluki-schema-only knobs: the Datadog Agent schema does not publish these, so they are
314 // seeded only when set; absent that, these defaults stand.
315 window_duration_seconds: DEFAULT_AGGREGATE_WINDOW_DURATION_SECONDS,
316 context_limit: DEFAULT_AGGREGATE_CONTEXT_LIMIT,
317 flush_interval: DEFAULT_AGGREGATE_FLUSH_INTERVAL,
318 passthrough_idle_flush_timeout: DEFAULT_AGGREGATE_PASSTHROUGH_IDLE_FLUSH_TIMEOUT,
319 // Datadog-schema knobs: always written by the witness driver, so these values are
320 // placeholders that never survive translation.
321 flush_open_windows: false,
322 counter_expiry_seconds: None,
323 context_expiry_seconds: 0,
324 no_aggregation_pipeline: false,
325 aggregator_tag_filter_cache_capacity: 0,
326 }
327 }
328}
329
330/// DogStatsD metric mapper.
331#[derive(Clone, Debug, PartialEq, Serialize)]
332pub struct Mapper {
333 /// Mapper profiles that rewrite matching metric names and tags.
334 pub profiles: Vec<MapperProfile>,
335
336 /// Number of mapper match results cached.
337 pub cache_size: usize,
338
339 /// Byte capacity of the mapper's string interner. (not in Datadog Agent config schema)
340 pub string_interner_size_bytes: NonZeroUsize,
341}
342
343impl Default for Mapper {
344 fn default() -> Self {
345 Self {
346 profiles: Vec::new(),
347 // Written by the Datadog witness driver.
348 cache_size: 0,
349 string_interner_size_bytes: DEFAULT_DOGSTATSD_MAPPER_STRING_INTERNER_SIZE_BYTES,
350 }
351 }
352}
353
354/// One mapper profile: a name, a metric prefix, and the mappings under it.
355#[derive(Clone, Debug, Default, PartialEq, Serialize)]
356pub struct MapperProfile {
357 /// Profile name, for diagnostics.
358 pub name: String,
359
360 /// Metric-name prefix the profile's mappings apply to.
361 pub prefix: String,
362
363 /// The name/tag mappings under this profile.
364 pub mappings: Vec<MetricMapping>,
365}
366
367/// A single metric-name mapping within a [`MapperProfile`].
368#[derive(Clone, Debug, Default, PartialEq, Serialize)]
369pub struct MetricMapping {
370 /// Pattern a metric name must match.
371 pub metric_match: String,
372
373 /// How `metric_match` is interpreted (for example, `wildcard` or `regex`).
374 pub match_type: String,
375
376 /// Replacement name emitted for a matching metric.
377 pub name: String,
378
379 /// Tags added to a matching metric, with values captured from the match.
380 pub tags: HashMap<String, String>,
381}
382
383/// Which payload types are emitted.
384#[derive(Clone, Debug, Default, PartialEq, Serialize)]
385pub struct EnablePayloads {
386 /// Whether event payloads are emitted.
387 pub events: bool,
388
389 /// Whether series (metric) payloads are emitted.
390 pub series: bool,
391
392 /// Whether service-check payloads are emitted.
393 pub service_checks: bool,
394
395 /// Whether sketch (distribution) payloads are emitted.
396 pub sketches: bool,
397}
398
399/// One `metric_filterlist_prefix` rule.
400#[derive(Clone, Debug, Default, PartialEq, Serialize)]
401pub struct MetricPrefixRule {
402 /// Prefix of metric names to drop.
403 ///
404 /// The empty string matches every stored metric name.
405 pub prefix: String,
406
407 /// Prefix exceptions that are kept.
408 ///
409 /// Exceptions apply across all prefix rules.
410 pub except_prefix: Vec<String>,
411
412 /// Exact-name exceptions that are kept.
413 ///
414 /// Exceptions apply across all prefix rules.
415 pub except_exact: Vec<String>,
416}
417
418/// Metric-name filtering (dynamic-capable).
419#[derive(Clone, Debug, Default, PartialEq, Serialize)]
420pub struct MetricFilter {
421 /// Metric names or prefixes that are dropped.
422 ///
423 /// Defaults to an empty list, which disables metric-name filtering.
424 pub values: Vec<String>,
425
426 /// Whether entries match by prefix rather than exact name.
427 ///
428 /// Defaults to `false`, which requires exact matches.
429 pub match_prefix: bool,
430
431 /// Additional per-entry prefix rules with optional exceptions.
432 ///
433 /// Defaults to an empty list.
434 pub prefix_rules: Vec<MetricPrefixRule>,
435}
436
437/// Metric namespace prefixing.
438#[derive(Clone, Debug, Default, PartialEq, Serialize)]
439pub struct PrefixFilter {
440 /// Namespace prepended to every metric name.
441 pub metric_namespace: String,
442
443 /// Namespaces excluded from the metric-namespace prefixing.
444 pub metric_namespace_blocklist: Vec<String>,
445}
446
447/// One tag-filterlist entry (dynamic-capable).
448#[derive(Clone, Debug, Default, PartialEq, Serialize)]
449pub struct MetricTagFilterEntry {
450 /// Metric name the entry applies to.
451 pub metric_name: String,
452
453 /// Whether the listed tags are included or excluded.
454 pub action: FilterAction,
455
456 /// Tags the action applies to.
457 pub tags: Vec<String>,
458}
459
460/// One tag value allow-list entry.
461///
462/// Rules apply to counters and sketch-backed metrics after mapper rewrites and metric namespace prefixing, and only
463/// to metrics that are aggregated: metrics carrying an explicit client timestamp bypass tag filtering entirely.
464/// Distinct prefixes must not overlap. Multiple rules may use the same prefix when they target different tags.
465#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
466pub struct MetricTagValueAllowlistEntry {
467 /// Non-empty metric-name prefix the entry applies to.
468 ///
469 /// Matching is exact and case-sensitive, including any whitespace. Empty prefixes and overlapping distinct
470 /// prefixes are invalid. Multiple rules may use the same prefix when they target different tags.
471 pub metric_prefix: String,
472
473 /// Non-empty tag key whose values are constrained.
474 ///
475 /// Bare tags have no value and are not changed. Key/value tags with an empty value are processed normally. Empty
476 /// names and names containing `:` are invalid. Matching is exact and preserves whitespace.
477 pub tag_name: String,
478
479 /// Tag values retained unchanged.
480 ///
481 /// The default is an empty list, which treats every key/value tag as a mismatch. The empty string is a valid list
482 /// member and retains tags with an empty value. Matching is exact and preserves whitespace.
483 #[serde(default)]
484 pub values: Vec<String>,
485
486 /// Action applied when a tag value is absent from [`values`][Self::values].
487 ///
488 /// The default is [`Remove`][TagValueMismatchAction::Remove].
489 #[serde(default)]
490 pub on_miss: TagValueMismatchAction,
491
492 /// Replacement value used when `on_miss` is [`Replace`][TagValueMismatchAction::Replace].
493 ///
494 /// The default is `other`. This field has no effect when `on_miss` is
495 /// [`Remove`][TagValueMismatchAction::Remove]. The replacement is emitted exactly as configured, including
496 /// whitespace.
497 #[serde(default = "default_tag_value_replacement")]
498 pub replacement: String,
499}
500
501fn default_tag_value_replacement() -> String {
502 "other".to_string()
503}
504
505impl Default for MetricTagValueAllowlistEntry {
506 fn default() -> Self {
507 Self {
508 metric_prefix: String::new(),
509 tag_name: String::new(),
510 values: Vec::new(),
511 on_miss: TagValueMismatchAction::Remove,
512 replacement: default_tag_value_replacement(),
513 }
514 }
515}
516
517/// Reports why a metric tag value allow-list cannot be represented.
518#[derive(Clone, Debug, PartialEq, Eq)]
519pub struct InvalidMetricTagValueAllowlist(String);
520
521impl fmt::Display for InvalidMetricTagValueAllowlist {
522 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
523 f.write_str(&self.0)
524 }
525}
526
527impl std::error::Error for InvalidMetricTagValueAllowlist {}
528
529/// Validates metric tag value allow-list entries.
530///
531/// # Errors
532///
533/// Returns an error for empty prefixes or tag names, tag names containing `:`, overlapping distinct prefixes, or a
534/// duplicate prefix and tag pair.
535pub fn validate_metric_tag_value_allowlists(
536 entries: &[MetricTagValueAllowlistEntry],
537) -> Result<(), InvalidMetricTagValueAllowlist> {
538 for (index, entry) in entries.iter().enumerate() {
539 let rule = index + 1;
540 if entry.metric_prefix.is_empty() {
541 return Err(InvalidMetricTagValueAllowlist(format!(
542 "metric tag value allow-list rule {rule} has an empty `metric_prefix`; configure a non-empty metric-name prefix"
543 )));
544 }
545 if entry.tag_name.is_empty() {
546 return Err(InvalidMetricTagValueAllowlist(format!(
547 "metric tag value allow-list rule {rule} for prefix '{}' has an empty `tag_name`; configure a non-empty tag name",
548 entry.metric_prefix
549 )));
550 }
551 if entry.tag_name.contains(':') {
552 return Err(InvalidMetricTagValueAllowlist(format!(
553 "metric tag value allow-list tag name '{}' contains ':'; configure only the tag key, without a colon or value",
554 entry.tag_name
555 )));
556 }
557 }
558
559 let mut sorted_entries = entries.iter().collect::<Vec<_>>();
560 sorted_entries.sort_unstable_by(|left, right| {
561 left.metric_prefix
562 .cmp(&right.metric_prefix)
563 .then_with(|| left.tag_name.cmp(&right.tag_name))
564 });
565
566 // After sorting by prefix and then tag, duplicate prefix/tag pairs are adjacent. Any distinct prefix that extends
567 // another prefix follows the complete group for the shorter prefix, so one adjacent pair also exposes that overlap.
568 for pair in sorted_entries.windows(2) {
569 let [left, right] = pair else {
570 unreachable!("a two-entry window must contain two entries");
571 };
572 if left.metric_prefix == right.metric_prefix {
573 if left.tag_name == right.tag_name {
574 return Err(InvalidMetricTagValueAllowlist(format!(
575 "metric prefix '{}' is configured more than once for tag '{}'; configure each prefix and tag pair only once",
576 left.metric_prefix, left.tag_name
577 )));
578 }
579 } else if right.metric_prefix.starts_with(&left.metric_prefix) {
580 return Err(InvalidMetricTagValueAllowlist(format!(
581 "overlapping metric prefixes '{}' and '{}' are configured; configure distinct prefixes that do not overlap",
582 left.metric_prefix, right.metric_prefix
583 )));
584 }
585 }
586
587 Ok(())
588}
589
590/// Action applied when a tag value is absent from its allow-list.
591#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Deserialize, Serialize)]
592#[serde(rename_all = "snake_case")]
593pub enum TagValueMismatchAction {
594 /// Removes the tag.
595 #[default]
596 Remove,
597 /// Replaces the tag value with the configured sentinel.
598 Replace,
599}
600
601/// Whether a tag-filterlist entry includes or excludes the listed tags.
602#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Serialize)]
603pub enum FilterAction {
604 Include,
605 #[default]
606 Exclude,
607}
608
609/// DogStatsD debug-log and verbose-log settings.
610#[derive(Clone, Debug, Default, PartialEq, Serialize)]
611pub struct DebugLog {
612 /// Whether the metric debug-log destination is added at startup.
613 ///
614 /// Defaults to `true`. Set this to `false` to remove the destination from the topology.
615 pub logging_enabled: bool,
616
617 /// Path of the DogStatsD metric debug log.
618 ///
619 /// Defaults to `None`, which selects the platform-specific Agent log path at startup.
620 /// Set a path to write the diagnostic log elsewhere.
621 pub log_file: Option<PathBuf>,
622
623 /// Number of rotated debug log files retained.
624 ///
625 /// Defaults to `3`. The file writer retains one rotated file when this is `0`.
626 pub log_file_max_rolls: usize,
627
628 /// Maximum size, in bytes, of the active debug log file before rotation.
629 ///
630 /// Defaults to `10,485,760` bytes (`10Mb`). A value of `0` is accepted as the rotation threshold.
631 /// Set this based on the diagnostic data and disk space to retain.
632 pub log_file_max_size: u64,
633
634 /// Whether per-metric processing statistics are written to the debug log.
635 ///
636 /// Defaults to `false` and can change at runtime. Enable this when collecting metric-level diagnostics.
637 pub metrics_stats_enable: bool,
638
639 /// Whether per-packet parse errors are logged at debug rather than error level.
640 ///
641 /// Defaults to `false`. Enable this to reduce error-level log volume from malformed packets.
642 pub disable_verbose_logs: bool,
643}