agent_data_plane_config/domains/
traces.rs

1//! Traces domain: APM trace processing, including environment, sampling, and obfuscation.
2
3use serde::{Deserialize, Serialize, Serializer};
4
5use crate::defaults::{
6    DEFAULT_ERROR_SAMPLING_ENABLED, DEFAULT_MAX_RESOURCE_LEN, DEFAULT_RARE_SAMPLER_CARDINALITY,
7    DEFAULT_RARE_SAMPLER_COOLDOWN_SECS, DEFAULT_RARE_SAMPLER_TPS, DEFAULT_TRACE_ENV,
8};
9
10/// A beta APM feature flag.
11///
12/// The feature inventory is not stable across agent versions: a flag may be promoted to a
13/// dedicated setting or dropped. Unrecognized values are carried as `ApmFeature::Other` rather
14/// than rejected, so configurations for newer or removed flags load unchanged.
15#[derive(Clone, Debug, Eq, Hash, PartialEq)]
16pub enum ApmFeature {
17    /// Switches the probabilistic sampler from hashing the low 64 bits of the trace ID to hashing
18    /// the full 128-bit ID.
19    ProbabilisticSamplerFullTraceId,
20
21    /// An unrecognized feature ID, carried verbatim.
22    Other(String),
23}
24
25impl ApmFeature {
26    /// Returns the feature's configuration ID.
27    pub fn as_str(&self) -> &str {
28        match self {
29            ApmFeature::ProbabilisticSamplerFullTraceId => "probabilistic_sampler_full_trace_id",
30            ApmFeature::Other(feature) => feature,
31        }
32    }
33}
34
35impl From<&str> for ApmFeature {
36    fn from(feature: &str) -> Self {
37        match feature {
38            "probabilistic_sampler_full_trace_id" => ApmFeature::ProbabilisticSamplerFullTraceId,
39            other => ApmFeature::Other(other.to_owned()),
40        }
41    }
42}
43
44impl Serialize for ApmFeature {
45    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
46    where
47        S: Serializer,
48    {
49        serializer.serialize_str(self.as_str())
50    }
51}
52
53/// Resolved traces configuration.
54#[derive(Clone, Debug, PartialEq, Serialize)]
55pub struct Domain {
56    /// Environment tag applied to traces.
57    pub env: String,
58
59    /// Environment used for traces that carry no explicit environment. (not in Datadog Agent config
60    /// schema)
61    pub default_env: String,
62
63    /// Whether trace stats are computed separately per span kind.
64    pub compute_stats_by_span_kind: bool,
65
66    /// Span tags promoted to peer tags for peer-service aggregation.
67    pub peer_tags: Vec<String>,
68
69    /// Whether stats are aggregated by peer tags.
70    pub peer_tags_aggregation: bool,
71
72    /// Whether error spans are sampled independently of the base sampler. (not in Datadog Agent
73    /// config schema)
74    pub error_sampling_enabled: bool,
75
76    /// Whether error tracking runs standalone, without full trace ingestion.
77    pub error_tracking_standalone_enabled: bool,
78
79    /// Target number of error traces sampled per second.
80    pub errors_per_second: f64,
81
82    /// Target number of traces sampled per second.
83    pub target_traces_per_second: f64,
84
85    /// Multiplier applied to every learned keep-rate of the adaptive samplers.
86    ///
87    /// Defaults to 1.0, leaving the computed rates unchanged; values above 1.0 keep
88    /// proportionally more. A value of `0` drops every trace decided by a learned rate.
89    /// Signatures without a learned rate—new, or freshly cleaned up—fall to the sampler's
90    /// default rate, which this multiplier does not scale.
91    ///
92    /// Operators who want coarser or finer sampling than the adaptive target computes can set
93    /// this once instead of re-planning the target: raise it temporarily for incident
94    /// investigations, lower it to control ingestion cost. The trade-off is coverage against
95    /// volume—every adaptive sampler's output scales together, so the per-service distribution
96    /// is preserved while the total scales.
97    pub extra_sample_rate: f64,
98
99    /// Maximum number of service signatures the priority sampler tracks rates for.
100    ///
101    /// Defaults to 5000. If set to `0`, the default of 5000 applies. Distinct services beyond
102    /// the cap evict the least recently used entries and lose their learned rates.
103    ///
104    /// Deployments with more distinct (service, environment) pairs than the default should
105    /// raise this. The trade-off is memory—the catalog holds a map entry and an LRU entry
106    /// per tracked signature—against rate fidelity, since evicted services re-learn their
107    /// rates from cold.
108    pub max_catalog_entries: usize,
109
110    /// Beta APM feature flags enabled for traces.
111    ///
112    /// Empty by default. Unrecognized values are carried as `ApmFeature::Other` and ignored
113    /// without a warning: the flag inventory is not stable across versions. Enable
114    /// `ApmFeature::ProbabilisticSamplerFullTraceId` on every probabilistic sampler in the
115    /// ingestion path so they keep the same traces.
116    pub features: Vec<ApmFeature>,
117
118    /// Whether the rare-span sampler is enabled.
119    pub enable_rare_sampler: bool,
120
121    /// Rare-span sampler settings.
122    pub rare_sampler: RareSampler,
123
124    /// Probabilistic sampler settings.
125    pub probabilistic_sampler: ProbabilisticSampler,
126
127    /// Per-subsystem trace obfuscation settings.
128    pub obfuscation: Obfuscation,
129
130    /// OTTL span-drop filter settings.
131    pub ottl_filter: OttlFilter,
132
133    /// OTTL span-transform settings.
134    pub ottl_transform: OttlTransform,
135
136    /// Maximum length of a span's resource name, in bytes; longer resources are truncated.
137    ///
138    /// Defaults to 5000 bytes. If set to `0`, all span resources are truncated to empty strings.
139    /// Change this only if legitimate resources exceed the default.
140    pub max_resource_len: usize,
141
142    /// Regex-based trace tag replacement rules, used to scrub sensitive values the built-in
143    /// obfuscation passes through. Defaults to no rules.
144    ///
145    /// Rules run in order after obfuscation and before stats and sampling. A `"*"` rule rewrites
146    /// every non-`_`-prefixed tag, the resource, and span events; `"resource.name"` only the
147    /// resource; any other name only that tag. Matched values are stored as strings, and a
148    /// pattern that fails to compile prevents startup.
149    pub replace_tags: Vec<ReplaceRule>,
150}
151
152impl Default for Domain {
153    fn default() -> Self {
154        Self {
155            // Witnessed fields start as placeholders and are overwritten by Datadog `drive`.
156            env: String::new(),
157            compute_stats_by_span_kind: false,
158            peer_tags: Vec::new(),
159            peer_tags_aggregation: false,
160            error_tracking_standalone_enabled: false,
161            errors_per_second: 0.0,
162            target_traces_per_second: 0.0,
163            extra_sample_rate: 1.0,
164            max_catalog_entries: 0,
165            features: Vec::new(),
166            enable_rare_sampler: false,
167            probabilistic_sampler: ProbabilisticSampler::default(),
168            obfuscation: Obfuscation::default(),
169            // Saluki-only fields own their absent-key behavior here.
170            default_env: DEFAULT_TRACE_ENV.to_owned(),
171            max_resource_len: DEFAULT_MAX_RESOURCE_LEN,
172            replace_tags: Vec::new(),
173            error_sampling_enabled: DEFAULT_ERROR_SAMPLING_ENABLED,
174            rare_sampler: RareSampler::default(),
175            ottl_filter: OttlFilter::default(),
176            ottl_transform: OttlTransform::default(),
177        }
178    }
179}
180
181/// A regex-based trace tag replacement rule.
182#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
183pub struct ReplaceRule {
184    /// Tag key the rule targets: `"*"`, `"resource.name"`, or a literal tag key.
185    pub name: String,
186
187    /// Regular expression matched against each targeted value.
188    pub pattern: String,
189
190    /// Text spliced in place of each match; `$1`-style group references are supported.
191    pub repl: String,
192}
193
194/// Rare-span sampler.
195#[derive(Clone, Debug, PartialEq, Serialize)]
196pub struct RareSampler {
197    /// Maximum number of distinct span signatures tracked. (not in Datadog Agent config schema)
198    pub cardinality: usize,
199
200    /// Cooldown, in seconds, before a signature may be sampled again. (not in Datadog Agent config
201    /// schema)
202    pub cooldown: f64,
203
204    /// Target rare-span traces sampled per second. (not in Datadog Agent config schema)
205    pub tps: f64,
206}
207
208impl Default for RareSampler {
209    fn default() -> Self {
210        Self {
211            cardinality: DEFAULT_RARE_SAMPLER_CARDINALITY,
212            cooldown: DEFAULT_RARE_SAMPLER_COOLDOWN_SECS,
213            tps: DEFAULT_RARE_SAMPLER_TPS,
214        }
215    }
216}
217
218/// APM probabilistic sampler.
219#[derive(Clone, Debug, Default, PartialEq, Serialize)]
220pub struct ProbabilisticSampler {
221    /// Whether the probabilistic sampler is enabled.
222    pub enabled: bool,
223
224    /// Seed mixed into the trace-ID hash before sampling, from 0 to 4,294,967,295.
225    ///
226    /// Defaults to `0`. Samplers in the same ingestion path keep the same traces only when their
227    /// seeds match, so align this with every other probabilistic sampler that sees the traffic.
228    /// Values outside the range fail configuration.
229    pub hash_seed: u32,
230
231    /// Percentage of traces the probabilistic sampler keeps.
232    pub sampling_percentage: f64,
233}
234
235/// Trace obfuscation, one group per supported subsystem.
236#[derive(Clone, Debug, Default, PartialEq, Serialize)]
237pub struct Obfuscation {
238    /// Credit-card obfuscation in span metadata.
239    pub credit_cards: CreditCardObfuscation,
240
241    /// Elasticsearch query obfuscation.
242    pub elasticsearch: JsonQueryObfuscation,
243
244    /// HTTP path and query obfuscation.
245    pub http: HttpObfuscation,
246
247    /// Memcached command obfuscation.
248    pub memcached: MemcachedObfuscation,
249
250    /// MongoDB query obfuscation.
251    pub mongodb: JsonQueryObfuscation,
252
253    /// OpenSearch query obfuscation.
254    pub opensearch: JsonQueryObfuscation,
255
256    /// Redis command obfuscation.
257    pub redis: CacheObfuscation,
258
259    /// Valkey command obfuscation.
260    pub valkey: CacheObfuscation,
261
262    /// SQL query obfuscation. (not in Datadog Agent config schema)
263    pub sql: SqlObfuscation,
264}
265
266/// Credit-card obfuscation.
267#[derive(Clone, Debug, Default, PartialEq, Serialize)]
268pub struct CreditCardObfuscation {
269    /// Whether credit-card numbers are obfuscated.
270    pub enabled: bool,
271
272    /// Tag or field names whose values are not obfuscated.
273    pub keep_values: Vec<String>,
274
275    /// Whether a Luhn check is applied before a value is treated as a card number.
276    pub luhn: bool,
277}
278
279/// Obfuscation shape shared by the JSON-query engines (Elasticsearch, MongoDB, OpenSearch).
280#[derive(Clone, Debug, Default, PartialEq, Serialize)]
281pub struct JsonQueryObfuscation {
282    /// Whether queries are obfuscated.
283    pub enabled: bool,
284
285    /// JSON keys whose values are not obfuscated.
286    pub keep_values: Vec<String>,
287
288    /// JSON keys whose values are obfuscated as embedded SQL.
289    pub obfuscate_sql_values: Vec<String>,
290}
291
292/// HTTP path/query obfuscation.
293#[derive(Clone, Debug, Default, PartialEq, Serialize)]
294pub struct HttpObfuscation {
295    /// Whether path segments containing digits are removed.
296    pub remove_paths_with_digits: bool,
297
298    /// Whether the query string is removed.
299    pub remove_query_string: bool,
300}
301
302/// Memcached command obfuscation.
303#[derive(Clone, Debug, Default, PartialEq, Serialize)]
304pub struct MemcachedObfuscation {
305    /// Whether Memcached commands are obfuscated.
306    pub enabled: bool,
307
308    /// Whether the command verb is preserved.
309    pub keep_command: bool,
310}
311
312/// Obfuscation shape shared by the key/value caches (redis, valkey).
313#[derive(Clone, Debug, Default, PartialEq, Serialize)]
314pub struct CacheObfuscation {
315    /// Whether cache commands are obfuscated.
316    pub enabled: bool,
317
318    /// Whether all command arguments are removed.
319    pub remove_all_args: bool,
320}
321
322/// SQL obfuscation.
323#[derive(Clone, Debug, Default, PartialEq, Serialize)]
324pub struct SqlObfuscation {
325    /// SQL dialect the obfuscator parses against.
326    pub dbms: String,
327
328    /// Whether dollar-quoted function bodies are preserved.
329    pub dollar_quoted_func: bool,
330
331    /// Whether column and table aliases are preserved.
332    pub keep_sql_alias: bool,
333
334    /// Whether digits in identifiers are replaced with a placeholder.
335    pub replace_digits: bool,
336
337    /// Whether table names are collected as metadata.
338    pub table_names: bool,
339}
340
341/// Error-handling mode for OTTL condition/statement evaluation, shared by the OTTL filter and
342/// transform processors.
343#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Serialize)]
344pub enum OttlErrorMode {
345    /// Log evaluation errors and continue.
346    Ignore,
347    /// Swallow evaluation errors silently and continue.
348    Silent,
349    /// Propagate the error up the pipeline; the payload is dropped.
350    #[default]
351    Propagate,
352}
353
354/// OTTL filter processor: span-drop conditions applied during trace enrichment.
355#[derive(Clone, Debug, Default, PartialEq, Serialize)]
356pub struct OttlFilter {
357    /// How evaluation errors in the filter conditions are handled. (not in Datadog Agent config
358    /// schema)
359    pub error_mode: OttlErrorMode,
360
361    /// OTTL conditions; a span matching any of them is dropped. (not in Datadog Agent config
362    /// schema)
363    pub span_conditions: Vec<String>,
364}
365
366/// OTTL transform processor: span-mutating statements applied during trace enrichment.
367#[derive(Clone, Debug, Default, PartialEq, Serialize)]
368pub struct OttlTransform {
369    /// How evaluation errors in the transform statements are handled. (not in Datadog Agent config
370    /// schema)
371    pub error_mode: OttlErrorMode,
372
373    /// OTTL statements applied to each span. (not in Datadog Agent config schema)
374    pub trace_statements: Vec<String>,
375}
376
377#[cfg(test)]
378mod tests {
379    use super::*;
380
381    #[test]
382    fn known_feature_ids_map_to_their_variants() {
383        assert_eq!(
384            ApmFeature::from("probabilistic_sampler_full_trace_id"),
385            ApmFeature::ProbabilisticSamplerFullTraceId
386        );
387        assert_eq!(
388            ApmFeature::from("error_rare_sample_tracer_drop"),
389            ApmFeature::Other("error_rare_sample_tracer_drop".to_owned())
390        );
391    }
392
393    #[test]
394    fn feature_ids_round_trip_through_as_str() {
395        for feature in [
396            ApmFeature::ProbabilisticSamplerFullTraceId,
397            ApmFeature::Other("table_names".to_owned()),
398        ] {
399            assert_eq!(ApmFeature::from(feature.as_str()), feature);
400        }
401    }
402}