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}