1use libdd_telemetry::data::Configuration;
5use serde::{Deserialize, Serialize};
6use std::collections::{HashMap, HashSet, VecDeque};
7use std::ops::Deref;
8use std::sync::{Arc, Mutex};
9use std::{borrow::Cow, fmt::Display, str::FromStr, sync::OnceLock};
10
11use rustc_version_runtime::version;
12
13use crate::core::configuration::sources::{
14 CompositeConfigSourceResult, CompositeSource, ConfigKey, ConfigSourceOrigin,
15};
16use crate::core::configuration::supported_configurations::SupportedConfigurations;
17use crate::core::log::LevelFilter;
18use crate::core::telemetry;
19use crate::{dd_error, dd_warn};
20
21#[derive(Debug, Clone)]
23pub enum RemoteConfigUpdate {
24 SamplingRules(Vec<SamplingRuleConfig>),
26 }
30
31type RemoteConfigCallback = Box<dyn Fn(&RemoteConfigUpdate) + Send + Sync>;
34
35pub struct RemoteConfigCallbacks {
37 pub sampling_rules_update: Option<RemoteConfigCallback>,
38 }
41
42impl std::fmt::Debug for RemoteConfigCallbacks {
43 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
44 f.debug_struct("RemoteConfigCallbacks")
45 .field(
46 "sampling_rules_update",
47 &self.sampling_rules_update.as_ref().map(|_| "<callback>"),
48 )
49 .finish()
50 }
51}
52
53impl RemoteConfigCallbacks {
54 pub fn new() -> Self {
55 Self {
56 sampling_rules_update: None,
57 }
58 }
59
60 pub fn set_sampling_rules_callback<F>(&mut self, callback: F)
61 where
62 F: Fn(&RemoteConfigUpdate) + Send + Sync + 'static,
63 {
64 self.sampling_rules_update = Some(Box::new(callback));
65 }
66
67 pub fn notify_update(&self, update: &RemoteConfigUpdate) {
70 match update {
71 RemoteConfigUpdate::SamplingRules(_) => {
72 if let Some(ref callback) = self.sampling_rules_update {
73 callback(update);
74 }
75 } }
77 }
78}
79
80impl Default for RemoteConfigCallbacks {
81 fn default() -> Self {
82 Self::new()
83 }
84}
85
86#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)]
88pub struct SamplingRuleConfig {
89 pub sample_rate: f64,
91
92 #[serde(default)]
94 pub service: Option<String>,
95
96 #[serde(default)]
98 pub name: Option<String>,
99
100 #[serde(default)]
102 pub resource: Option<String>,
103
104 #[serde(default)]
106 pub tags: HashMap<String, String>,
107
108 #[serde(default = "default_provenance")]
111 pub provenance: String,
112}
113
114impl Display for SamplingRuleConfig {
115 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
116 write!(f, "{}", serde_json::json!(self))
117 }
118}
119
120fn default_provenance() -> String {
121 "default".to_string()
122}
123
124pub const TRACER_VERSION: &str = env!("CARGO_PKG_VERSION");
125
126const DATADOG_TAGS_MAX_LENGTH: usize = 512;
127const RC_DEFAULT_POLL_INTERVAL: f64 = 5.0; #[derive(Debug, Default, Clone, PartialEq)]
130struct ParsedSamplingRules {
131 rules: Vec<SamplingRuleConfig>,
132}
133
134impl Deref for ParsedSamplingRules {
135 type Target = [SamplingRuleConfig];
136
137 fn deref(&self) -> &Self::Target {
138 &self.rules
139 }
140}
141
142impl From<ParsedSamplingRules> for Vec<SamplingRuleConfig> {
143 fn from(parsed: ParsedSamplingRules) -> Self {
144 parsed.rules
145 }
146}
147
148impl FromStr for ParsedSamplingRules {
149 type Err = serde_json::Error;
150
151 fn from_str(s: &str) -> Result<Self, Self::Err> {
152 if s.trim().is_empty() {
153 return Ok(ParsedSamplingRules::default());
154 }
155 let rules_vec: Vec<SamplingRuleConfig> = serde_json::from_str(s)?;
157 Ok(ParsedSamplingRules { rules: rules_vec })
158 }
159}
160
161impl Display for ParsedSamplingRules {
162 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
163 write!(
164 f,
165 "{}",
166 serde_json::to_string(&self.rules).unwrap_or_default()
167 )
168 }
169}
170
171enum ConfigItemRef<'a, T> {
172 Ref(&'a T),
173 ArcRef(arc_swap::Guard<Option<Arc<T>>>),
174}
175
176impl<T: Deref> Deref for ConfigItemRef<'_, T> {
177 type Target = T::Target;
178
179 fn deref(&self) -> &Self::Target {
180 match self {
181 ConfigItemRef::Ref(t) => t,
182 ConfigItemRef::ArcRef(guard) => guard.as_ref().unwrap(),
183 }
184 }
185}
186
187impl<T: ConfigurationValueProvider> ConfigurationValueProvider for ConfigItemRef<'_, T> {
188 fn get_configuration_value(&self) -> String {
189 match self {
190 ConfigItemRef::Ref(t) => t.get_configuration_value(),
191 ConfigItemRef::ArcRef(guard) => guard.as_ref().unwrap().get_configuration_value(),
192 }
193 }
194}
195
196pub trait ConfigurationProvider {
203 fn get_configuration(&self) -> Configuration;
206}
207
208trait ConfigurationValueProvider {
229 fn get_configuration_value(&self) -> String;
234}
235
236trait ValueSourceUpdater<T> {
244 fn name(&self) -> SupportedConfigurations;
245 fn set_value_source(&mut self, value: T, source: ConfigSourceOrigin);
247}
248
249#[derive(Debug)]
252struct ConfigItem<T: ConfigurationValueProvider> {
253 name: SupportedConfigurations,
254 default_value: T,
255 env_value: Option<T>,
256 code_value: Option<T>,
257 config_id: Option<String>,
258}
259
260impl<T: Clone + ConfigurationValueProvider> Clone for ConfigItem<T> {
261 fn clone(&self) -> Self {
262 Self {
263 name: self.name,
264 default_value: self.default_value.clone(),
265 env_value: self.env_value.clone(),
266 code_value: self.code_value.clone(),
267 config_id: self.config_id.clone(),
268 }
269 }
270}
271
272impl<T: Clone + ConfigurationValueProvider> ConfigItem<T> {
273 fn new(name: SupportedConfigurations, default: T) -> Self {
275 Self {
276 name,
277 default_value: default,
278 env_value: None,
279 code_value: None,
280 config_id: None,
281 }
282 }
283
284 fn set_code(&mut self, value: T) {
286 self.code_value = Some(value);
287 }
288
289 fn value(&self) -> &T {
292 self.code_value
293 .as_ref()
294 .or(self.env_value.as_ref())
295 .unwrap_or(&self.default_value)
296 }
297
298 #[allow(dead_code)] fn source(&self) -> ConfigSourceOrigin {
301 if self.code_value.is_some() {
302 ConfigSourceOrigin::Code
303 } else if self.env_value.is_some() {
304 ConfigSourceOrigin::EnvVar
305 } else {
306 ConfigSourceOrigin::Default
307 }
308 }
309}
310
311impl<T: Clone + ConfigurationValueProvider> ConfigurationProvider for ConfigItem<T> {
312 fn get_configuration(&self) -> Configuration {
314 Configuration {
315 name: self.name.as_str().to_string(),
316 value: self.value().get_configuration_value(),
317 origin: self.source().into(),
318 config_id: self.config_id.clone(),
319 }
320 }
321}
322
323impl<T: ConfigurationValueProvider> ValueSourceUpdater<T> for ConfigItem<T> {
324 fn name(&self) -> SupportedConfigurations {
325 self.name
326 }
327
328 fn set_value_source(&mut self, value: T, source: ConfigSourceOrigin) {
330 match source {
331 ConfigSourceOrigin::Code => self.code_value = Some(value),
332 ConfigSourceOrigin::EnvVar => self.env_value = Some(value),
333 ConfigSourceOrigin::RemoteConfig => {
334 dd_warn!("Cannot set a value from RC");
335 }
336 ConfigSourceOrigin::Default => {
337 dd_warn!("Cannot set default value after initialization");
338 }
339 }
340 }
341}
342
343#[derive(Debug)]
346struct ConfigItemWithOverride<T: ConfigurationValueProvider + Deref> {
347 config_item: ConfigItem<T>,
348 override_value: arc_swap::ArcSwapOption<T>,
349 override_origin: ConfigSourceOrigin,
350 config_id: arc_swap::ArcSwapOption<String>,
351}
352
353impl<T: Clone + ConfigurationValueProvider + Deref> Clone for ConfigItemWithOverride<T> {
354 fn clone(&self) -> Self {
355 Self {
356 config_item: self.config_item.clone(),
357 override_value: arc_swap::ArcSwapOption::new(self.override_value.load_full()),
358 override_origin: self.override_origin,
359 config_id: arc_swap::ArcSwapOption::new(self.config_id.load_full()),
360 }
361 }
362}
363
364impl<T: ConfigurationValueProvider + Clone + Deref> ConfigItemWithOverride<T> {
365 fn new_code(name: SupportedConfigurations, default: T) -> Self {
366 Self {
367 config_item: ConfigItem::new(name, default),
368 override_value: arc_swap::ArcSwapOption::const_empty(),
369 override_origin: ConfigSourceOrigin::Code,
370 config_id: arc_swap::ArcSwapOption::const_empty(),
371 }
372 }
373
374 fn new_rc(name: SupportedConfigurations, default: T) -> Self {
375 Self {
376 config_item: ConfigItem::new(name, default),
377 override_value: arc_swap::ArcSwapOption::const_empty(),
378 override_origin: ConfigSourceOrigin::RemoteConfig,
379 config_id: arc_swap::ArcSwapOption::const_empty(),
380 }
381 }
382
383 fn source(&self) -> ConfigSourceOrigin {
384 if self.override_value.load().is_some() {
385 self.override_origin
386 } else {
387 self.config_item.source()
388 }
389 }
390
391 fn set_override_value(&self, value: T, source: ConfigSourceOrigin) {
393 if source == self.override_origin {
394 self.override_value.store(Some(Arc::new(value)));
395 }
396 }
397
398 fn set_config_id(&self, config_id: Option<String>) {
399 match config_id {
400 Some(id) => self.config_id.store(Some(Arc::new(id))),
401 None => self.config_id.store(None),
402 }
403 }
404
405 fn unset_override_value(&self) {
407 self.override_value.store(None);
408 }
409
410 fn set_code(&mut self, value: T) {
412 self.set_value_source(value, ConfigSourceOrigin::Code);
413 }
414
415 fn value(&self) -> ConfigItemRef<'_, T> {
418 let override_value = self.override_value.load();
419 if override_value.is_some() {
420 ConfigItemRef::ArcRef(override_value)
421 } else {
422 ConfigItemRef::Ref(self.config_item.value())
423 }
424 }
425}
426
427impl<T: Clone + ConfigurationValueProvider + Deref> ConfigurationProvider
428 for ConfigItemWithOverride<T>
429{
430 fn get_configuration(&self) -> Configuration {
432 let config_id = self.config_id.load().as_ref().map(|id| (**id).clone());
433 Configuration {
434 name: self.config_item.name.as_str().to_string(),
435 value: self.value().get_configuration_value(),
436 origin: self.source().into(),
437 config_id,
438 }
439 }
440}
441
442impl<T: Clone + ConfigurationValueProvider + Deref> ValueSourceUpdater<T>
443 for ConfigItemWithOverride<T>
444{
445 fn name(&self) -> SupportedConfigurations {
446 self.config_item.name()
447 }
448
449 fn set_value_source(&mut self, value: T, source: ConfigSourceOrigin) {
451 if source == self.override_origin {
452 self.set_override_value(value, source);
453 } else {
454 self.config_item.set_value_source(value, source);
455 }
456 }
457}
458
459struct ConfigItemSourceUpdater<'a> {
460 sources: &'a CompositeSource,
461}
462
463impl ConfigItemSourceUpdater<'_> {
464 fn apply_result<ParsedConfig, RawConfig, ConfigItemType, F>(
465 &self,
466 mut item: ConfigItemType,
467 result: CompositeConfigSourceResult<RawConfig>,
468 transform: F,
469 ) -> ConfigItemType
470 where
471 ParsedConfig: Clone + ConfigurationValueProvider,
472 ConfigItemType: ValueSourceUpdater<ParsedConfig>,
473 F: FnOnce(RawConfig) -> ParsedConfig,
474 {
475 if !result.errors.is_empty() {
476 dd_error!(
477 "Configuration: Error parsing property {} - {:?}",
478 item.name().as_str(),
479 result.errors
480 );
481 }
482
483 if let Some(ConfigKey { value, origin }) = result.value {
484 item.set_value_source(transform(value), origin);
485 }
486 item
487 }
488
489 fn update_parsed<ParsedConfig, ConfigItemType>(&self, default: ConfigItemType) -> ConfigItemType
491 where
492 ParsedConfig: Clone + FromStr + ConfigurationValueProvider,
493 ParsedConfig::Err: std::fmt::Display,
494 ConfigItemType: ValueSourceUpdater<ParsedConfig>,
495 {
496 let result = self.sources.get_parse::<ParsedConfig>(default.name());
497 self.apply_result(default, result, |value| value)
498 }
499
500 pub fn update_string<ParsedConfig, ConfigItemType, F>(
502 &self,
503 default: ConfigItemType,
504 transform: F,
505 ) -> ConfigItemType
506 where
507 ParsedConfig: Clone + ConfigurationValueProvider,
508 ConfigItemType: ValueSourceUpdater<ParsedConfig>,
509 F: FnOnce(String) -> ParsedConfig,
510 {
511 let result = self.sources.get(default.name());
512 self.apply_result(default, result, transform)
513 }
514
515 pub fn update_parsed_with_transform<ParsedConfig, RawConfig, ConfigItemType, F>(
517 &self,
518 default: ConfigItemType,
519 transform: F,
520 ) -> ConfigItemType
521 where
522 ParsedConfig: Clone + ConfigurationValueProvider,
523 RawConfig: FromStr,
524 RawConfig::Err: std::fmt::Display,
525 ConfigItemType: ValueSourceUpdater<ParsedConfig>,
526 F: FnOnce(RawConfig) -> ParsedConfig,
527 {
528 let result = self.sources.get_parse::<RawConfig>(default.name());
529 self.apply_result(default, result, transform)
530 }
531}
532
533macro_rules! impl_config_value_provider {
535 (option: $($type:ty),* $(,)?) => {
537 $(
538 impl ConfigurationValueProvider for Option<$type> {
539 fn get_configuration_value(&self) -> String {
540 match self {
541 Some(value) => value.to_string(),
542 None => String::new(),
543 }
544 }
545 }
546 )*
547 };
548
549 (simple: $($type:ty),* $(,)?) => {
551 $(
552 impl ConfigurationValueProvider for $type {
553 fn get_configuration_value(&self) -> String {
554 self.to_string()
555 }
556 }
557 )*
558 };
559}
560
561type SamplingRulesConfigItem = ConfigItemWithOverride<ParsedSamplingRules>;
562
563#[derive(Debug, Clone)]
566struct ExtraServicesTracker {
567 extra_services: Arc<Mutex<HashSet<String>>>,
569 extra_services_sent: Arc<Mutex<HashSet<String>>>,
571 extra_services_queue: Arc<Mutex<Option<VecDeque<String>>>>,
573}
574
575impl ExtraServicesTracker {
576 fn new() -> Self {
577 Self {
578 extra_services: Arc::new(Mutex::new(HashSet::new())),
579 extra_services_sent: Arc::new(Mutex::new(HashSet::new())),
580 extra_services_queue: Arc::new(Mutex::new(Some(VecDeque::new()))),
581 }
582 }
583
584 fn add_extra_service(&self, service_name: &str, main_service: &str) {
585 if service_name == main_service {
586 return;
587 }
588
589 let mut sent = match self.extra_services_sent.lock() {
590 Ok(s) => s,
591 Err(_) => return,
592 };
593
594 if sent.contains(service_name) {
595 return;
596 }
597
598 let mut queue = match self.extra_services_queue.lock() {
599 Ok(q) => q,
600 Err(_) => return,
601 };
602
603 if let Some(ref mut q) = *queue {
605 q.push_back(service_name.to_string());
606 }
607 sent.insert(service_name.to_string());
608 }
609
610 fn get_extra_services(&self) -> Vec<String> {
612 let mut queue = match self.extra_services_queue.lock() {
613 Ok(q) => q,
614 Err(_) => return Vec::new(),
615 };
616
617 let mut services = match self.extra_services.lock() {
618 Ok(s) => s,
619 Err(_) => return Vec::new(),
620 };
621
622 if let Some(ref mut q) = *queue {
624 while let Some(service) = q.pop_front() {
625 services.insert(service);
626
627 if services.len() > 64 {
629 if let Some(to_remove) = services.iter().next().cloned() {
631 dd_warn!("ExtraServicesTracker:RemoteConfig: Exceeded 64 service limit, removing service: {}", to_remove);
632 services.remove(&to_remove);
633 }
634 }
635 }
636 }
637
638 services.iter().cloned().collect()
639 }
640}
641
642#[derive(Clone, Copy, Debug, PartialEq, Eq)]
643pub enum TracePropagationStyle {
644 Datadog,
645 TraceContext,
646 None,
647}
648
649impl TracePropagationStyle {
650 fn from_tags(tags: Option<Vec<String>>) -> Option<Vec<TracePropagationStyle>> {
651 match tags {
652 Some(tags) if !tags.is_empty() => Some(
653 tags.iter()
654 .filter_map(|value| match TracePropagationStyle::from_str(value) {
655 Ok(style) => Some(style),
656 Err(err) => {
657 dd_warn!("Error parsing: {err}");
658 None
659 }
660 })
661 .collect::<Vec<TracePropagationStyle>>(),
662 ),
663 Some(_) => None,
664 None => None,
665 }
666 }
667}
668
669impl FromStr for TracePropagationStyle {
670 type Err = String;
671
672 fn from_str(s: &str) -> Result<Self, Self::Err> {
673 match s.trim().to_lowercase().as_str() {
674 "datadog" => Ok(TracePropagationStyle::Datadog),
675 "tracecontext" => Ok(TracePropagationStyle::TraceContext),
676 "none" => Ok(TracePropagationStyle::None),
677 _ => Err(format!("Unknown trace propagation style: '{s}'")),
678 }
679 }
680}
681
682impl Display for TracePropagationStyle {
683 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
684 let style = match self {
685 TracePropagationStyle::Datadog => "datadog",
686 TracePropagationStyle::TraceContext => "tracecontext",
687 TracePropagationStyle::None => "none",
688 };
689 write!(f, "{style}")
690 }
691}
692
693#[derive(Debug, Clone)]
694enum ServiceName {
695 Default,
696 Configured(String),
697}
698
699impl ServiceName {
700 fn is_default(&self) -> bool {
701 matches!(self, ServiceName::Default)
702 }
703
704 fn as_str(&self) -> &str {
705 match self {
706 ServiceName::Default => "unnamed-rust-service",
707 ServiceName::Configured(name) => name,
708 }
709 }
710}
711
712impl std::ops::Deref for ServiceName {
713 type Target = str;
714
715 fn deref(&self) -> &Self::Target {
716 self.as_str()
717 }
718}
719
720impl Display for ServiceName {
721 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
722 write!(f, "{}", self.as_str())
723 }
724}
725
726impl ConfigurationValueProvider for Vec<(String, String)> {
727 fn get_configuration_value(&self) -> String {
728 self.iter()
729 .map(|(key, value)| format!("{key}:{value}"))
730 .collect::<Vec<_>>()
731 .join(",")
732 }
733}
734
735impl ConfigurationValueProvider for Option<Vec<TracePropagationStyle>> {
736 fn get_configuration_value(&self) -> String {
737 match &self {
738 Some(styles) => styles
739 .iter()
740 .map(|style| style.to_string())
741 .collect::<Vec<_>>()
742 .join(","),
743 None => "".to_string(),
744 }
745 }
746}
747
748impl_config_value_provider!(simple: Cow<'static, str>, bool, u32, usize, i32, f64, ServiceName, LevelFilter, ParsedSamplingRules);
749impl_config_value_provider!(option: String);
750
751#[derive(Clone)]
752#[non_exhaustive]
753pub struct Config {
766 runtime_id: &'static str,
768
769 tracer_version: &'static str,
771 language_version: String,
772 language: &'static str,
773
774 service: ConfigItemWithOverride<ServiceName>,
776 env: ConfigItem<Option<String>>,
777 version: ConfigItem<Option<String>>,
778
779 global_tags: ConfigItem<Vec<(String, String)>>,
783 agent_host: ConfigItem<Cow<'static, str>>,
785 trace_agent_port: ConfigItem<u32>,
787 trace_agent_url: ConfigItem<Cow<'static, str>>,
789 dogstatsd_agent_host: ConfigItem<Cow<'static, str>>,
791 dogstatsd_agent_port: ConfigItem<u32>,
793 dogstatsd_agent_url: ConfigItem<Cow<'static, str>>,
795
796 trace_sampling_rules: SamplingRulesConfigItem,
800
801 trace_rate_limit: ConfigItem<i32>,
804
805 enabled: ConfigItem<bool>,
807 log_level_filter: ConfigItem<LevelFilter>,
809
810 trace_stats_computation_enabled: ConfigItem<bool>,
813
814 #[cfg(feature = "test-utils")]
816 wait_agent_info_ready: bool,
817
818 telemetry_enabled: ConfigItem<bool>,
821 telemetry_log_collection_enabled: ConfigItem<bool>,
823 telemetry_heartbeat_interval: ConfigItem<f64>,
825
826 trace_partial_flush_enabled: ConfigItem<bool>,
828 trace_partial_flush_min_spans: ConfigItem<usize>,
829
830 trace_propagation_style: ConfigItem<Option<Vec<TracePropagationStyle>>>,
832 trace_propagation_style_extract: ConfigItem<Option<Vec<TracePropagationStyle>>>,
833 trace_propagation_style_inject: ConfigItem<Option<Vec<TracePropagationStyle>>>,
834 trace_propagation_extract_first: ConfigItem<bool>,
835
836 remote_config_enabled: ConfigItem<bool>,
838
839 remote_config_poll_interval: ConfigItem<f64>,
842
843 extra_services_tracker: ExtraServicesTracker,
846
847 remote_config_callbacks: Arc<Mutex<RemoteConfigCallbacks>>,
850
851 datadog_tags_max_length: ConfigItem<usize>,
854}
855
856impl Config {
857 fn from_sources(sources: &CompositeSource) -> Self {
858 let default = default_config();
859
860 struct DdTags(Vec<String>);
862
863 impl FromStr for DdTags {
864 type Err = &'static str;
865
866 fn from_str(s: &str) -> Result<Self, Self::Err> {
867 Ok(DdTags(
868 s.split(',').map(|s| s.to_string()).collect::<Vec<String>>(),
869 ))
870 }
871 }
872
873 struct DdKeyValueTags(Vec<(String, String)>);
876
877 impl FromStr for DdKeyValueTags {
878 type Err = &'static str;
879
880 fn from_str(s: &str) -> Result<Self, Self::Err> {
881 Ok(DdKeyValueTags(
882 s.split(',')
883 .filter_map(|s| {
884 s.split_once(':')
885 .map(|(k, v)| (k.trim().to_string(), v.trim().to_string()))
886 })
887 .collect(),
888 ))
889 }
890 }
891
892 let parsed_sampling_rules_config = sources
893 .get_parse::<ParsedSamplingRules>(SupportedConfigurations::DD_TRACE_SAMPLING_RULES);
894
895 let mut sampling_rules_item = ConfigItemWithOverride::new_rc(
896 parsed_sampling_rules_config.name,
897 ParsedSamplingRules::default(), );
899
900 if let Some(rules) = parsed_sampling_rules_config.value {
902 sampling_rules_item.set_value_source(rules.value, rules.origin);
903 }
904
905 let cisu = ConfigItemSourceUpdater { sources };
906
907 Self {
908 runtime_id: default.runtime_id,
909 tracer_version: default.tracer_version,
910 language_version: default.language_version,
911 language: default.language,
912 service: cisu.update_string(default.service, ServiceName::Configured),
913 env: cisu.update_string(default.env, Some),
914 version: cisu.update_string(default.version, Some),
915 global_tags: cisu
917 .update_parsed_with_transform(default.global_tags, |DdKeyValueTags(tags)| tags),
918 agent_host: cisu.update_string(default.agent_host, Cow::Owned),
919 trace_agent_port: cisu.update_parsed(default.trace_agent_port),
920 trace_agent_url: cisu.update_string(default.trace_agent_url, Cow::Owned),
921 dogstatsd_agent_host: cisu.update_string(default.dogstatsd_agent_host, Cow::Owned),
922 dogstatsd_agent_port: cisu.update_parsed(default.dogstatsd_agent_port),
923 dogstatsd_agent_url: cisu.update_string(default.dogstatsd_agent_url, Cow::Owned),
924
925 trace_partial_flush_enabled: cisu.update_parsed(default.trace_partial_flush_enabled),
926 trace_partial_flush_min_spans: cisu
927 .update_parsed(default.trace_partial_flush_min_spans),
928
929 trace_sampling_rules: sampling_rules_item,
931 trace_rate_limit: cisu.update_parsed(default.trace_rate_limit),
932
933 enabled: cisu.update_parsed(default.enabled),
934 log_level_filter: cisu.update_parsed(default.log_level_filter),
935 trace_stats_computation_enabled: cisu
936 .update_parsed(default.trace_stats_computation_enabled),
937 telemetry_enabled: cisu.update_parsed(default.telemetry_enabled),
938 telemetry_log_collection_enabled: cisu
939 .update_parsed(default.telemetry_log_collection_enabled),
940 telemetry_heartbeat_interval: cisu.update_parsed_with_transform(
941 default.telemetry_heartbeat_interval,
942 |interval: f64| interval.abs(),
943 ),
944 trace_propagation_style: cisu
945 .update_parsed_with_transform(default.trace_propagation_style, |DdTags(tags)| {
946 TracePropagationStyle::from_tags(Some(tags))
947 }),
948 trace_propagation_style_extract: cisu.update_parsed_with_transform(
949 default.trace_propagation_style_extract,
950 |DdTags(tags)| TracePropagationStyle::from_tags(Some(tags)),
951 ),
952 trace_propagation_style_inject: cisu.update_parsed_with_transform(
953 default.trace_propagation_style_inject,
954 |DdTags(tags)| TracePropagationStyle::from_tags(Some(tags)),
955 ),
956 trace_propagation_extract_first: cisu
957 .update_parsed(default.trace_propagation_extract_first),
958 #[cfg(feature = "test-utils")]
959 wait_agent_info_ready: default.wait_agent_info_ready,
960 extra_services_tracker: ExtraServicesTracker::new(),
961 remote_config_enabled: cisu.update_parsed(default.remote_config_enabled),
962 remote_config_poll_interval: cisu.update_parsed_with_transform(
963 default.remote_config_poll_interval,
964 |interval: f64| interval.abs().min(RC_DEFAULT_POLL_INTERVAL),
965 ),
966 remote_config_callbacks: Arc::new(Mutex::new(RemoteConfigCallbacks::new())),
967 datadog_tags_max_length: cisu
968 .update_parsed_with_transform(default.datadog_tags_max_length, |max: usize| {
969 max.min(DATADOG_TAGS_MAX_LENGTH)
970 }),
971 }
972 }
973
974 fn builder_with_sources(sources: &CompositeSource) -> ConfigBuilder {
975 ConfigBuilder {
976 config: Config::from_sources(sources),
977 }
978 }
979
980 pub fn builder() -> ConfigBuilder {
982 Self::builder_with_sources(&CompositeSource::default_sources())
983 }
984
985 pub(crate) fn get_telemetry_configuration(&self) -> Vec<&dyn ConfigurationProvider> {
986 vec![
987 &self.service,
988 &self.env,
989 &self.version,
990 &self.global_tags,
991 &self.agent_host,
992 &self.trace_agent_port,
993 &self.trace_agent_url,
994 &self.dogstatsd_agent_host,
995 &self.dogstatsd_agent_port,
996 &self.dogstatsd_agent_url,
997 &self.trace_sampling_rules,
998 &self.trace_rate_limit,
999 &self.enabled,
1000 &self.log_level_filter,
1001 &self.trace_stats_computation_enabled,
1002 &self.telemetry_enabled,
1003 &self.telemetry_log_collection_enabled,
1004 &self.telemetry_heartbeat_interval,
1005 &self.trace_partial_flush_enabled,
1006 &self.trace_partial_flush_min_spans,
1007 &self.trace_propagation_style,
1008 &self.trace_propagation_style_extract,
1009 &self.trace_propagation_style_inject,
1010 &self.trace_propagation_extract_first,
1011 &self.remote_config_enabled,
1012 &self.remote_config_poll_interval,
1013 &self.datadog_tags_max_length,
1014 ]
1015 }
1016
1017 pub fn runtime_id(&self) -> &str {
1018 self.runtime_id
1019 }
1020
1021 pub fn tracer_version(&self) -> &str {
1022 self.tracer_version
1023 }
1024
1025 pub fn language(&self) -> &str {
1026 self.language
1027 }
1028
1029 pub fn language_version(&self) -> &str {
1030 self.language_version.as_str()
1031 }
1032
1033 pub fn service(&self) -> impl Deref<Target = str> + use<'_> {
1034 self.service.value()
1035 }
1036
1037 pub fn service_is_default(&self) -> bool {
1038 match self.service.value() {
1039 ConfigItemRef::Ref(t) => t.is_default(),
1040 ConfigItemRef::ArcRef(guard) => guard.as_ref().unwrap().is_default(),
1041 }
1042 }
1043
1044 pub fn env(&self) -> Option<&str> {
1045 self.env.value().as_deref()
1046 }
1047
1048 pub fn version(&self) -> Option<&str> {
1049 self.version.value().as_deref()
1050 }
1051
1052 pub fn global_tags(&self) -> impl Iterator<Item = (&str, &str)> {
1053 self.global_tags
1054 .value()
1055 .iter()
1056 .map(|tag| (tag.0.as_str(), tag.1.as_str()))
1057 }
1058
1059 pub fn trace_agent_url(&self) -> &Cow<'static, str> {
1060 self.trace_agent_url.value()
1061 }
1062
1063 pub fn dogstatsd_agent_host(&self) -> &Cow<'static, str> {
1064 self.dogstatsd_agent_host.value()
1065 }
1066
1067 pub fn dogstatsd_agent_port(&self) -> &u32 {
1068 self.dogstatsd_agent_port.value()
1069 }
1070
1071 pub fn dogstatsd_agent_url(&self) -> &Cow<'static, str> {
1072 self.dogstatsd_agent_url.value()
1073 }
1074
1075 pub fn trace_sampling_rules(&self) -> impl Deref<Target = [SamplingRuleConfig]> + use<'_> {
1076 self.trace_sampling_rules.value()
1077 }
1078
1079 pub fn trace_rate_limit(&self) -> i32 {
1080 *self.trace_rate_limit.value()
1081 }
1082
1083 pub fn enabled(&self) -> bool {
1084 *self.enabled.value()
1085 }
1086
1087 pub fn log_level_filter(&self) -> &LevelFilter {
1088 self.log_level_filter.value()
1089 }
1090
1091 pub fn trace_stats_computation_enabled(&self) -> bool {
1092 *self.trace_stats_computation_enabled.value()
1093 }
1094
1095 #[cfg(feature = "test-utils")]
1096 pub fn __internal_wait_agent_info_ready(&self) -> bool {
1097 self.wait_agent_info_ready
1098 }
1099
1100 fn process_runtime_id() -> &'static str {
1102 static RUNTIME_ID: OnceLock<String> = OnceLock::new();
1104 RUNTIME_ID.get_or_init(|| uuid::Uuid::new_v4().to_string())
1105 }
1106
1107 pub fn telemetry_enabled(&self) -> bool {
1108 *self.telemetry_enabled.value()
1109 }
1110
1111 pub fn telemetry_log_collection_enabled(&self) -> bool {
1112 *self.telemetry_log_collection_enabled.value()
1113 }
1114
1115 pub fn telemetry_heartbeat_interval(&self) -> f64 {
1116 *self.telemetry_heartbeat_interval.value()
1117 }
1118
1119 pub fn trace_partial_flush_enabled(&self) -> bool {
1120 *self.trace_partial_flush_enabled.value()
1121 }
1122
1123 pub fn trace_partial_flush_min_spans(&self) -> usize {
1124 *self.trace_partial_flush_min_spans.value()
1125 }
1126
1127 pub fn trace_propagation_style(&self) -> Option<&[TracePropagationStyle]> {
1128 self.trace_propagation_style.value().as_deref()
1129 }
1130
1131 pub fn trace_propagation_style_extract(&self) -> Option<&[TracePropagationStyle]> {
1132 self.trace_propagation_style_extract.value().as_deref()
1133 }
1134
1135 pub fn trace_propagation_style_inject(&self) -> Option<&[TracePropagationStyle]> {
1136 self.trace_propagation_style_inject.value().as_deref()
1137 }
1138
1139 pub fn trace_propagation_extract_first(&self) -> bool {
1140 *self.trace_propagation_extract_first.value()
1141 }
1142
1143 pub(crate) fn update_sampling_rules_from_remote(
1144 &self,
1145 rules_json: &str,
1146 config_id: Option<String>,
1147 ) -> Result<(), String> {
1148 let rules: Vec<SamplingRuleConfig> = serde_json::from_str(rules_json)
1150 .map_err(|e| format!("Failed to parse sampling rules JSON: {e}"))?;
1151
1152 if rules.is_empty() {
1154 self.clear_remote_sampling_rules(config_id);
1155 } else {
1156 self.trace_sampling_rules.set_override_value(
1157 ParsedSamplingRules { rules },
1158 ConfigSourceOrigin::RemoteConfig,
1159 );
1160 self.trace_sampling_rules.set_config_id(config_id);
1161
1162 self.remote_config_callbacks.lock().unwrap().notify_update(
1164 &RemoteConfigUpdate::SamplingRules(self.trace_sampling_rules().to_vec()),
1165 );
1166
1167 telemetry::notify_configuration_update(&self.trace_sampling_rules);
1168 }
1169
1170 Ok(())
1171 }
1172
1173 pub(crate) fn update_service_name(&self, service_name: Option<String>) {
1174 if let Some(service_name) = service_name {
1175 self.service.set_override_value(
1176 ServiceName::Configured(service_name),
1177 ConfigSourceOrigin::Code,
1178 );
1179 }
1180 }
1181
1182 pub(crate) fn clear_remote_sampling_rules(&self, config_id: Option<String>) {
1183 self.trace_sampling_rules.unset_override_value();
1184 self.trace_sampling_rules.set_config_id(config_id);
1185
1186 self.remote_config_callbacks.lock().unwrap().notify_update(
1187 &RemoteConfigUpdate::SamplingRules(self.trace_sampling_rules().to_vec()),
1188 );
1189
1190 telemetry::notify_configuration_update(&self.trace_sampling_rules);
1191 }
1192
1193 pub(crate) fn set_sampling_rules_callback<F>(&self, callback: F)
1200 where
1201 F: Fn(&RemoteConfigUpdate) + Send + Sync + 'static,
1202 {
1203 self.remote_config_callbacks
1204 .lock()
1205 .unwrap()
1206 .set_sampling_rules_callback(callback);
1207 }
1208
1209 pub(crate) fn add_extra_service(&self, service_name: &str) {
1212 if !self.remote_config_enabled() {
1213 return;
1214 }
1215 self.extra_services_tracker
1216 .add_extra_service(service_name, &self.service());
1217 }
1218
1219 pub(crate) fn get_extra_services(&self) -> Vec<String> {
1221 if !self.remote_config_enabled() {
1222 return Vec::new();
1223 }
1224 self.extra_services_tracker.get_extra_services()
1225 }
1226
1227 pub fn remote_config_enabled(&self) -> bool {
1229 *self.remote_config_enabled.value()
1230 }
1231
1232 pub fn remote_config_poll_interval(&self) -> f64 {
1234 *self.remote_config_poll_interval.value()
1235 }
1236
1237 pub fn datadog_tags_max_length(&self) -> usize {
1239 *self.datadog_tags_max_length.value()
1240 }
1241}
1242
1243impl std::fmt::Debug for Config {
1244 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1245 f.debug_struct("Config")
1246 .field("runtime_id", &self.runtime_id)
1247 .field("tracer_version", &self.tracer_version)
1248 .field("language_version", &self.language_version)
1249 .field("service", &self.service)
1250 .field("env", &self.env)
1251 .field("version", &self.version)
1252 .field("global_tags", &self.global_tags)
1253 .field("trace_agent_url", &self.trace_agent_url)
1254 .field("dogstatsd_agent_url", &self.dogstatsd_agent_url)
1255 .field("trace_sampling_rules", &self.trace_sampling_rules)
1256 .field("trace_rate_limit", &self.trace_rate_limit)
1257 .field("enabled", &self.enabled)
1258 .field("log_level_filter", &self.log_level_filter)
1259 .field(
1260 "trace_stats_computation_enabled",
1261 &self.trace_stats_computation_enabled,
1262 )
1263 .field("trace_propagation_style", &self.trace_propagation_style)
1264 .field(
1265 "trace_propagation_style_extract",
1266 &self.trace_propagation_style_extract,
1267 )
1268 .field(
1269 "trace_propagation_style_inject",
1270 &self.trace_propagation_style_inject,
1271 )
1272 .field(
1273 "trace_propagation_extract_first",
1274 &self.trace_propagation_extract_first,
1275 )
1276 .field("extra_services_tracker", &self.extra_services_tracker)
1277 .field("remote_config_enabled", &self.remote_config_enabled)
1278 .field(
1279 "remote_config_poll_interval",
1280 &self.remote_config_poll_interval,
1281 )
1282 .field("remote_config_callbacks", &self.remote_config_callbacks)
1283 .finish()
1284 }
1285}
1286
1287fn default_config() -> Config {
1288 Config {
1289 runtime_id: Config::process_runtime_id(),
1290 env: ConfigItem::new(SupportedConfigurations::DD_ENV, None),
1291 service: ConfigItemWithOverride::new_code(
1293 SupportedConfigurations::DD_SERVICE,
1294 ServiceName::Default,
1295 ),
1296 version: ConfigItem::new(SupportedConfigurations::DD_VERSION, None),
1297 global_tags: ConfigItem::new(SupportedConfigurations::DD_TAGS, Vec::new()),
1298
1299 agent_host: ConfigItem::new(
1300 SupportedConfigurations::DD_AGENT_HOST,
1301 Cow::Borrowed("localhost"),
1302 ),
1303 trace_agent_port: ConfigItem::new(SupportedConfigurations::DD_TRACE_AGENT_PORT, 8126),
1304 trace_agent_url: ConfigItem::new(
1305 SupportedConfigurations::DD_TRACE_AGENT_URL,
1306 Cow::Borrowed(""),
1307 ),
1308 dogstatsd_agent_host: ConfigItem::new(
1309 SupportedConfigurations::DD_DOGSTATSD_HOST,
1310 Cow::Borrowed("localhost"),
1311 ),
1312 dogstatsd_agent_port: ConfigItem::new(SupportedConfigurations::DD_DOGSTATSD_PORT, 8125),
1313 dogstatsd_agent_url: ConfigItem::new(
1314 SupportedConfigurations::DD_DOGSTATSD_URL,
1315 Cow::Borrowed(""),
1316 ),
1317 trace_sampling_rules: ConfigItemWithOverride::new_rc(
1318 SupportedConfigurations::DD_TRACE_SAMPLING_RULES,
1319 ParsedSamplingRules::default(), ),
1321 trace_rate_limit: ConfigItem::new(SupportedConfigurations::DD_TRACE_RATE_LIMIT, 100),
1322 enabled: ConfigItem::new(SupportedConfigurations::DD_TRACE_ENABLED, true),
1323 log_level_filter: ConfigItem::new(
1324 SupportedConfigurations::DD_LOG_LEVEL,
1325 LevelFilter::default(),
1326 ),
1327 tracer_version: TRACER_VERSION,
1328 language: "rust",
1329 language_version: version().to_string(),
1330 trace_stats_computation_enabled: ConfigItem::new(
1331 SupportedConfigurations::DD_TRACE_STATS_COMPUTATION_ENABLED,
1332 true,
1333 ),
1334 #[cfg(feature = "test-utils")]
1335 wait_agent_info_ready: false,
1336
1337 telemetry_enabled: ConfigItem::new(
1338 SupportedConfigurations::DD_INSTRUMENTATION_TELEMETRY_ENABLED,
1339 true,
1340 ),
1341 telemetry_log_collection_enabled: ConfigItem::new(
1342 SupportedConfigurations::DD_TELEMETRY_LOG_COLLECTION_ENABLED,
1343 true,
1344 ),
1345 telemetry_heartbeat_interval: ConfigItem::new(
1346 SupportedConfigurations::DD_TELEMETRY_HEARTBEAT_INTERVAL,
1347 60.0,
1348 ),
1349 trace_partial_flush_enabled: ConfigItem::new(
1350 SupportedConfigurations::DD_TRACE_PARTIAL_FLUSH_ENABLED,
1351 false,
1352 ),
1353 trace_partial_flush_min_spans: ConfigItem::new(
1354 SupportedConfigurations::DD_TRACE_PARTIAL_FLUSH_MIN_SPANS,
1355 300,
1356 ),
1357 trace_propagation_style: ConfigItem::new(
1358 SupportedConfigurations::DD_TRACE_PROPAGATION_STYLE,
1359 Some(vec![
1360 TracePropagationStyle::Datadog,
1361 TracePropagationStyle::TraceContext,
1362 ]),
1363 ),
1364 trace_propagation_style_extract: ConfigItem::new(
1365 SupportedConfigurations::DD_TRACE_PROPAGATION_STYLE_EXTRACT,
1366 None,
1367 ),
1368 trace_propagation_style_inject: ConfigItem::new(
1369 SupportedConfigurations::DD_TRACE_PROPAGATION_STYLE_INJECT,
1370 None,
1371 ),
1372 trace_propagation_extract_first: ConfigItem::new(
1373 SupportedConfigurations::DD_TRACE_PROPAGATION_EXTRACT_FIRST,
1374 false,
1375 ),
1376 extra_services_tracker: ExtraServicesTracker::new(),
1377 remote_config_enabled: ConfigItem::new(
1378 SupportedConfigurations::DD_REMOTE_CONFIGURATION_ENABLED,
1379 true,
1380 ),
1381 remote_config_poll_interval: ConfigItem::new(
1382 SupportedConfigurations::DD_REMOTE_CONFIG_POLL_INTERVAL_SECONDS,
1383 RC_DEFAULT_POLL_INTERVAL,
1384 ),
1385 remote_config_callbacks: Arc::new(Mutex::new(RemoteConfigCallbacks::new())),
1386 datadog_tags_max_length: ConfigItem::new(
1387 SupportedConfigurations::DD_TRACE_X_DATADOG_TAGS_MAX_LENGTH,
1388 DATADOG_TAGS_MAX_LENGTH,
1389 ),
1390 }
1391}
1392
1393pub struct ConfigBuilder {
1394 config: Config,
1395}
1396
1397impl ConfigBuilder {
1398 pub fn build(&self) -> Config {
1400 crate::core::log::set_max_level(*self.config.log_level_filter.value());
1401 let mut config = self.config.clone();
1402
1403 if config.trace_agent_url.value().is_empty() {
1405 let host = &config.agent_host.value();
1406 let port = *config.trace_agent_port.value();
1407 config
1408 .trace_agent_url
1409 .set_code(Cow::Owned(format!("http://{host}:{port}")));
1410 }
1411
1412 if config.dogstatsd_agent_url.value().is_empty() {
1414 let host = &config.dogstatsd_agent_host.value();
1415 let port = *config.dogstatsd_agent_port.value();
1416 config
1417 .dogstatsd_agent_url
1418 .set_code(Cow::Owned(format!("http://{host}:{port}")));
1419 }
1420
1421 config
1422 }
1423
1424 pub fn set_service(&mut self, service: String) -> &mut Self {
1430 self.config
1431 .service
1432 .set_code(ServiceName::Configured(service));
1433 self
1434 }
1435
1436 pub fn set_env(&mut self, env: String) -> &mut Self {
1442 self.config.env.set_code(Some(env));
1443 self
1444 }
1445
1446 pub fn set_version(&mut self, version: String) -> &mut Self {
1452 self.config.version.set_code(Some(version));
1453 self
1454 }
1455
1456 pub fn set_global_tags(&mut self, tags: Vec<(String, String)>) -> &mut Self {
1463 self.config.global_tags.set_code(tags);
1464 self
1465 }
1466
1467 pub fn add_global_tag(&mut self, tag: (String, String)) -> &mut Self {
1470 let mut current_tags = self.config.global_tags.value().clone();
1471 current_tags.push(tag);
1472 self.config.global_tags.set_code(current_tags);
1473 self
1474 }
1475
1476 pub fn set_telemetry_enabled(&mut self, enabled: bool) -> &mut Self {
1482 self.config.telemetry_enabled.set_code(enabled);
1483 self
1484 }
1485
1486 pub fn set_telemetry_log_collection_enabled(&mut self, enabled: bool) -> &mut Self {
1492 self.config
1493 .telemetry_log_collection_enabled
1494 .set_code(enabled);
1495 self
1496 }
1497
1498 pub fn set_telemetry_heartbeat_interval(&mut self, seconds: f64) -> &mut Self {
1504 self.config
1505 .telemetry_heartbeat_interval
1506 .set_code(seconds.abs());
1507 self
1508 }
1509
1510 pub fn set_agent_host(&mut self, host: String) -> &mut Self {
1516 self.config
1517 .agent_host
1518 .set_code(Cow::Owned(host.to_string()));
1519 self
1520 }
1521
1522 pub fn set_trace_agent_port(&mut self, port: u32) -> &mut Self {
1528 self.config.trace_agent_port.set_code(port);
1529 self
1530 }
1531
1532 pub fn set_trace_agent_url(&mut self, url: String) -> &mut Self {
1539 self.config
1540 .trace_agent_url
1541 .set_code(Cow::Owned(url.to_string()));
1542 self
1543 }
1544
1545 pub fn set_dogstatsd_agent_host(&mut self, host: String) -> &mut Self {
1551 self.config
1552 .dogstatsd_agent_host
1553 .set_code(Cow::Owned(host.to_string()));
1554 self
1555 }
1556
1557 pub fn set_dogstatsd_agent_port(&mut self, port: u32) -> &mut Self {
1563 self.config.dogstatsd_agent_port.set_code(port);
1564 self
1565 }
1566
1567 pub fn set_trace_partial_flush_enabled(&mut self, enabled: bool) -> &mut Self {
1573 self.config.trace_partial_flush_enabled.set_code(enabled);
1574 self
1575 }
1576
1577 pub fn set_trace_partial_flush_min_spans(&mut self, min_spans: usize) -> &mut Self {
1583 self.config
1584 .trace_partial_flush_min_spans
1585 .set_code(min_spans);
1586 self
1587 }
1588
1589 pub fn set_trace_sampling_rules(&mut self, rules: Vec<SamplingRuleConfig>) -> &mut Self {
1596 self.config
1597 .trace_sampling_rules
1598 .set_code(ParsedSamplingRules { rules });
1599 self
1600 }
1601
1602 pub fn set_trace_rate_limit(&mut self, rate_limit: i32) -> &mut Self {
1609 self.config.trace_rate_limit.set_code(rate_limit);
1610 self
1611 }
1612
1613 pub fn set_trace_propagation_style(&mut self, styles: Vec<TracePropagationStyle>) -> &mut Self {
1620 self.config.trace_propagation_style.set_code(Some(styles));
1621 self
1622 }
1623
1624 pub fn set_trace_propagation_style_extract(
1631 &mut self,
1632 styles: Vec<TracePropagationStyle>,
1633 ) -> &mut Self {
1634 self.config
1635 .trace_propagation_style_extract
1636 .set_code(Some(styles));
1637 self
1638 }
1639
1640 pub fn set_trace_propagation_style_inject(
1647 &mut self,
1648 styles: Vec<TracePropagationStyle>,
1649 ) -> &mut Self {
1650 self.config
1651 .trace_propagation_style_inject
1652 .set_code(Some(styles));
1653 self
1654 }
1655
1656 pub fn set_trace_propagation_extract_first(&mut self, first: bool) -> &mut Self {
1662 self.config.trace_propagation_extract_first.set_code(first);
1663 self
1664 }
1665
1666 pub fn set_enabled(&mut self, enabled: bool) -> &mut Self {
1672 self.config.enabled.set_code(enabled);
1673 self
1674 }
1675
1676 pub fn set_log_level_filter(&mut self, filter: LevelFilter) -> &mut Self {
1682 self.config.log_level_filter.set_code(filter);
1683 self
1684 }
1685
1686 pub fn set_trace_stats_computation_enabled(
1692 &mut self,
1693 trace_stats_computation_enabled: bool,
1694 ) -> &mut Self {
1695 self.config
1696 .trace_stats_computation_enabled
1697 .set_code(trace_stats_computation_enabled);
1698 self
1699 }
1700
1701 pub fn set_remote_config_enabled(&mut self, enabled: bool) -> &mut Self {
1707 self.config.remote_config_enabled.set_code(enabled);
1708 self
1709 }
1710
1711 pub fn set_remote_config_poll_interval(&mut self, seconds: f64) -> &mut Self {
1717 self.config
1718 .remote_config_poll_interval
1719 .set_code(seconds.abs().min(RC_DEFAULT_POLL_INTERVAL));
1720 self
1721 }
1722
1723 pub fn set_datadog_tags_max_length(&mut self, length: usize) -> &mut Self {
1729 self.config
1730 .datadog_tags_max_length
1731 .set_code(length.min(DATADOG_TAGS_MAX_LENGTH));
1732 self
1733 }
1734
1735 #[cfg(feature = "test-utils")]
1736 pub fn set_datadog_tags_max_length_with_no_limit(&mut self, length: usize) -> &mut Self {
1737 self.config.datadog_tags_max_length.set_code(length);
1738 self
1739 }
1740
1741 #[cfg(feature = "test-utils")]
1742 pub fn __internal_set_wait_agent_info_ready(
1743 &mut self,
1744 wait_agent_info_ready: bool,
1745 ) -> &mut Self {
1746 self.config.wait_agent_info_ready = wait_agent_info_ready;
1747 self
1748 }
1749}
1750
1751#[cfg(test)]
1752mod tests {
1753 use libdd_telemetry::data::ConfigurationOrigin;
1754
1755 use super::Config;
1756 use super::*;
1757 use crate::core::configuration::sources::{CompositeSource, ConfigSourceOrigin, HashMapSource};
1758
1759 #[test]
1760 fn test_config_from_source() {
1761 let mut sources = CompositeSource::new();
1762 sources.add_source(HashMapSource::from_iter(
1763 [
1764 ("DD_SERVICE", "test-service"),
1765 ("DD_ENV", "test-env"),
1766 ("DD_TRACE_SAMPLING_RULES",
1767 r#"[{"sample_rate":0.5,"service":"web-api","name":null,"resource":null,"tags":{},"provenance":"customer"}]"#),
1768 ("DD_TRACE_RATE_LIMIT", "123"),
1769 ("DD_TRACE_ENABLED", "true"),
1770 ("DD_LOG_LEVEL", "DEBUG"),
1771 ],
1772 ConfigSourceOrigin::EnvVar,
1773 ));
1774 let config = Config::builder_with_sources(&sources).build();
1775
1776 assert_eq!(&*config.service(), "test-service");
1777 assert_eq!(config.env(), Some("test-env"));
1778 assert_eq!(config.trace_rate_limit(), 123);
1779 let rules = config.trace_sampling_rules();
1780 assert_eq!(rules.len(), 1, "Should have one rule");
1781 assert_eq!(
1782 &rules[0],
1783 &SamplingRuleConfig {
1784 sample_rate: 0.5,
1785 service: Some("web-api".to_string()),
1786 provenance: "customer".to_string(),
1787 ..SamplingRuleConfig::default()
1788 }
1789 );
1790
1791 assert!(config.enabled());
1792 assert_eq!(*config.log_level_filter(), super::LevelFilter::Debug);
1793 }
1794
1795 #[test]
1796 fn test_sampling_rules() {
1797 let mut sources = CompositeSource::new();
1798 sources.add_source(HashMapSource::from_iter(
1799 [(
1800 "DD_TRACE_SAMPLING_RULES",
1801 r#"[{"sample_rate":0.5,"service":"test-service","provenance":"customer"}]"#,
1802 )],
1803 ConfigSourceOrigin::EnvVar,
1804 ));
1805 let config = Config::builder_with_sources(&sources).build();
1806
1807 assert_eq!(config.trace_sampling_rules().len(), 1);
1808 assert_eq!(
1809 &config.trace_sampling_rules()[0],
1810 &SamplingRuleConfig {
1811 sample_rate: 0.5,
1812 service: Some("test-service".to_string()),
1813 provenance: "customer".to_string(),
1814 ..SamplingRuleConfig::default()
1815 }
1816 );
1817 }
1818
1819 #[test]
1820 fn test_config_from_source_manual_override() {
1821 let mut sources = CompositeSource::new();
1822 sources.add_source(HashMapSource::from_iter(
1823 [
1824 ("DD_SERVICE", "test-service"),
1825 ("DD_TRACE_RATE_LIMIT", "50"),
1826 ],
1827 ConfigSourceOrigin::EnvVar,
1828 ));
1829 let config = Config::builder_with_sources(&sources)
1830 .set_trace_sampling_rules(vec![SamplingRuleConfig {
1831 sample_rate: 0.8,
1832 service: Some("manual-service".to_string()),
1833 name: None,
1834 resource: None,
1835 tags: HashMap::new(),
1836 provenance: "manual".to_string(),
1837 }])
1838 .set_trace_rate_limit(200)
1839 .set_service("manual-service".to_string())
1840 .set_env("manual-env".to_string())
1841 .set_log_level_filter(super::LevelFilter::Warn)
1842 .build();
1843
1844 assert_eq!(config.trace_rate_limit(), 200);
1845 let rules = config.trace_sampling_rules();
1846 assert_eq!(rules.len(), 1);
1847 assert_eq!(
1848 &config.trace_sampling_rules()[0],
1849 &SamplingRuleConfig {
1850 sample_rate: 0.8,
1851 service: Some("manual-service".to_string()),
1852 provenance: "manual".to_string(),
1853 ..SamplingRuleConfig::default()
1854 }
1855 );
1856
1857 assert!(config.enabled());
1858 assert_eq!(*config.log_level_filter(), super::LevelFilter::Warn);
1859 }
1860
1861 #[test]
1862 fn test_propagation_config_from_source() {
1863 let mut sources = CompositeSource::new();
1864 sources.add_source(HashMapSource::from_iter(
1865 [
1866 ("DD_TRACE_PROPAGATION_STYLE", ""),
1867 (
1868 "DD_TRACE_PROPAGATION_STYLE_EXTRACT",
1869 "datadog, tracecontext, invalid",
1870 ),
1871 ("DD_TRACE_PROPAGATION_STYLE_INJECT", "tracecontext"),
1872 ("DD_TRACE_PROPAGATION_EXTRACT_FIRST", "true"),
1873 ],
1874 ConfigSourceOrigin::EnvVar,
1875 ));
1876 let config = Config::builder_with_sources(&sources).build();
1877
1878 assert_eq!(config.trace_propagation_style(), Some(vec![]).as_deref());
1879 assert_eq!(
1880 config.trace_propagation_style_extract(),
1881 Some(vec![
1882 TracePropagationStyle::Datadog,
1883 TracePropagationStyle::TraceContext
1884 ])
1885 .as_deref()
1886 );
1887 assert_eq!(
1888 config.trace_propagation_style_inject(),
1889 Some(vec![TracePropagationStyle::TraceContext]).as_deref()
1890 );
1891 assert!(config.trace_propagation_extract_first())
1892 }
1893
1894 #[test]
1895 fn test_propagation_config_from_source_override() {
1896 let mut sources = CompositeSource::new();
1897 sources.add_source(HashMapSource::from_iter(
1898 [
1899 ("DD_TRACE_PROPAGATION_STYLE", ""),
1900 (
1901 "DD_TRACE_PROPAGATION_STYLE_EXTRACT",
1902 "datadog, tracecontext",
1903 ),
1904 ("DD_TRACE_PROPAGATION_STYLE_INJECT", "tracecontext"),
1905 ("DD_TRACE_PROPAGATION_EXTRACT_FIRST", "true"),
1906 ],
1907 ConfigSourceOrigin::EnvVar,
1908 ));
1909 let config = Config::builder_with_sources(&sources)
1910 .set_trace_propagation_style(vec![
1911 TracePropagationStyle::TraceContext,
1912 TracePropagationStyle::Datadog,
1913 ])
1914 .set_trace_propagation_style_extract(vec![TracePropagationStyle::TraceContext])
1915 .set_trace_propagation_style_inject(vec![TracePropagationStyle::Datadog])
1916 .set_trace_propagation_extract_first(false)
1917 .build();
1918
1919 assert_eq!(
1920 config.trace_propagation_style(),
1921 Some(vec![
1922 TracePropagationStyle::TraceContext,
1923 TracePropagationStyle::Datadog
1924 ])
1925 .as_deref()
1926 );
1927 assert_eq!(
1928 config.trace_propagation_style_extract(),
1929 Some(vec![TracePropagationStyle::TraceContext]).as_deref()
1930 );
1931 assert_eq!(
1932 config.trace_propagation_style_inject(),
1933 Some(vec![TracePropagationStyle::Datadog]).as_deref()
1934 );
1935 assert!(!config.trace_propagation_extract_first());
1936 }
1937
1938 #[test]
1939 fn test_propagation_config_incorrect_extract() {
1940 let mut sources = CompositeSource::new();
1941 sources.add_source(HashMapSource::from_iter(
1942 [
1943 ("DD_TRACE_PROPAGATION_STYLE", "datadog, tracecontext"),
1944 ("DD_TRACE_PROPAGATION_STYLE_EXTRACT", "incorrect,"),
1945 ("DD_TRACE_PROPAGATION_STYLE_INJECT", "tracecontext"),
1946 ("DD_TRACE_PROPAGATION_EXTRACT_FIRST", "true"),
1947 ],
1948 ConfigSourceOrigin::EnvVar,
1949 ));
1950 let config = Config::builder_with_sources(&sources).build();
1951
1952 assert_eq!(
1953 config.trace_propagation_style(),
1954 Some(vec![
1955 TracePropagationStyle::Datadog,
1956 TracePropagationStyle::TraceContext,
1957 ])
1958 .as_deref()
1959 );
1960 assert_eq!(
1961 config.trace_propagation_style_extract(),
1962 Some(vec![]).as_deref()
1963 );
1964 assert_eq!(
1965 config.trace_propagation_style_inject(),
1966 Some(vec![TracePropagationStyle::TraceContext]).as_deref()
1967 );
1968 assert!(config.trace_propagation_extract_first());
1969 }
1970 #[test]
1971 fn test_propagation_config_empty_extract() {
1972 let mut sources = CompositeSource::new();
1973 sources.add_source(HashMapSource::from_iter(
1974 [
1975 ("DD_TRACE_PROPAGATION_STYLE", ""),
1976 ("DD_TRACE_PROPAGATION_STYLE_EXTRACT", ""),
1977 ("DD_TRACE_PROPAGATION_STYLE_INJECT", "tracecontext"),
1978 ("DD_TRACE_PROPAGATION_EXTRACT_FIRST", "true"),
1979 ],
1980 ConfigSourceOrigin::EnvVar,
1981 ));
1982 let config = Config::builder_with_sources(&sources).build();
1983
1984 assert_eq!(config.trace_propagation_style(), Some(vec![]).as_deref());
1985 assert_eq!(
1986 config.trace_propagation_style_extract(),
1987 Some(vec![]).as_deref()
1988 );
1989 assert_eq!(
1990 config.trace_propagation_style_inject(),
1991 Some(vec![TracePropagationStyle::TraceContext]).as_deref()
1992 );
1993 assert!(config.trace_propagation_extract_first());
1994 }
1995
1996 #[test]
1997 fn test_propagation_config_not_present_extract() {
1998 let mut sources = CompositeSource::new();
1999 sources.add_source(HashMapSource::from_iter(
2000 [
2001 ("DD_TRACE_PROPAGATION_STYLE_INJECT", "tracecontext"),
2002 ("DD_TRACE_PROPAGATION_EXTRACT_FIRST", "true"),
2003 ],
2004 ConfigSourceOrigin::EnvVar,
2005 ));
2006 let config = Config::builder_with_sources(&sources).build();
2007
2008 assert_eq!(
2009 config.trace_propagation_style(),
2010 Some(vec![
2011 TracePropagationStyle::Datadog,
2012 TracePropagationStyle::TraceContext,
2013 ])
2014 .as_deref()
2015 );
2016 assert_eq!(config.trace_propagation_style_extract(), None);
2017 assert_eq!(
2018 config.trace_propagation_style_inject(),
2019 Some(vec![TracePropagationStyle::TraceContext]).as_deref()
2020 );
2021 assert!(config.trace_propagation_extract_first());
2022 }
2023
2024 #[test]
2025 fn test_stats_computation_enabled_config() {
2026 let mut sources = CompositeSource::new();
2027 sources.add_source(HashMapSource::from_iter(
2028 [("DD_TRACE_STATS_COMPUTATION_ENABLED", "false")],
2029 ConfigSourceOrigin::EnvVar,
2030 ));
2031 let config = Config::builder_with_sources(&sources).build();
2032 assert!(!config.trace_stats_computation_enabled());
2033
2034 let mut sources = CompositeSource::new();
2035 sources.add_source(HashMapSource::from_iter(
2036 [("DD_TRACE_STATS_COMPUTATION_ENABLED", "true")],
2037 ConfigSourceOrigin::EnvVar,
2038 ));
2039 let config = Config::builder_with_sources(&sources).build();
2040 assert!(config.trace_stats_computation_enabled());
2041
2042 let mut sources = CompositeSource::new();
2043 sources.add_source(HashMapSource::from_iter(
2044 [("DD_TRACE_STATS_COMPUTATION_ENABLED", "a")],
2045 ConfigSourceOrigin::EnvVar,
2046 ));
2047 let config = Config::builder_with_sources(&sources).build();
2048 assert!(config.trace_stats_computation_enabled());
2049
2050 let config = Config::builder()
2051 .set_trace_stats_computation_enabled(false)
2052 .build();
2053
2054 assert!(!config.trace_stats_computation_enabled());
2055 }
2056
2057 #[test]
2058 fn test_extra_services_tracking() {
2059 let config = Config::builder()
2060 .set_service("main-service".to_string())
2061 .build();
2062
2063 assert_eq!(config.get_extra_services().len(), 0);
2065
2066 config.add_extra_service("service-1");
2068 config.add_extra_service("service-2");
2069 config.add_extra_service("service-3");
2070
2071 config.add_extra_service("main-service");
2073
2074 config.add_extra_service("service-1");
2076
2077 let services = config.get_extra_services();
2078 assert_eq!(services.len(), 3);
2079 assert!(services.contains(&"service-1".to_string()));
2080 assert!(services.contains(&"service-2".to_string()));
2081 assert!(services.contains(&"service-3".to_string()));
2082 assert!(!services.contains(&"main-service".to_string()));
2083 }
2084
2085 #[test]
2086 fn test_extra_services_disabled_when_remote_config_disabled() {
2087 let mut sources = CompositeSource::new();
2089 sources.add_source(HashMapSource::from_iter(
2090 [("DD_REMOTE_CONFIGURATION_ENABLED", "false")],
2091 ConfigSourceOrigin::EnvVar,
2092 ));
2093 let config = Config::builder_with_sources(&sources)
2094 .set_service("main-service".to_string())
2095 .build();
2096
2097 config.add_extra_service("service-1");
2099 config.add_extra_service("service-2");
2100
2101 let services = config.get_extra_services();
2103 assert_eq!(services.len(), 0);
2104 }
2105
2106 #[test]
2107 fn test_extra_services_limit() {
2108 let config = Config::builder()
2109 .set_service("main-service".to_string())
2110 .build();
2111
2112 for i in 0..70 {
2114 config.add_extra_service(&format!("service-{i}"));
2115 }
2116
2117 let services = config.get_extra_services();
2119 assert_eq!(services.len(), 64);
2120 }
2121
2122 #[test]
2123 fn test_remote_config_enabled_from_env() {
2124 let mut sources = CompositeSource::new();
2126 sources.add_source(HashMapSource::from_iter(
2127 [("DD_REMOTE_CONFIGURATION_ENABLED", "true")],
2128 ConfigSourceOrigin::EnvVar,
2129 ));
2130 let config = Config::builder_with_sources(&sources).build();
2131 assert!(config.remote_config_enabled());
2132
2133 let mut sources = CompositeSource::new();
2135 sources.add_source(HashMapSource::from_iter(
2136 [("DD_REMOTE_CONFIGURATION_ENABLED", "false")],
2137 ConfigSourceOrigin::EnvVar,
2138 ));
2139 let config = Config::builder_with_sources(&sources).build();
2140 assert!(!config.remote_config_enabled());
2141
2142 let mut sources = CompositeSource::new();
2144 sources.add_source(HashMapSource::from_iter(
2145 [("DD_REMOTE_CONFIGURATION_ENABLED", "invalid")],
2146 ConfigSourceOrigin::EnvVar,
2147 ));
2148 let config = Config::builder_with_sources(&sources).build();
2149 assert!(config.remote_config_enabled());
2150
2151 let config = Config::builder().build();
2153 assert!(config.remote_config_enabled()); }
2155
2156 #[test]
2157 fn test_sampling_rules_update_callbacks() {
2158 let config = Config::builder().build();
2159
2160 let callback_called = Arc::new(Mutex::new(false));
2162 let callback_rules = Arc::new(Mutex::new(Vec::<SamplingRuleConfig>::new()));
2163
2164 let callback_called_clone = callback_called.clone();
2165 let callback_rules_clone = callback_rules.clone();
2166
2167 config.set_sampling_rules_callback(move |update| {
2168 *callback_called_clone.lock().unwrap() = true;
2169 let RemoteConfigUpdate::SamplingRules(rules) = update;
2171 *callback_rules_clone.lock().unwrap() = rules.clone();
2172 });
2173
2174 assert!(!*callback_called.lock().unwrap());
2176 assert!(callback_rules.lock().unwrap().is_empty());
2177
2178 let new_rules = vec![SamplingRuleConfig {
2180 sample_rate: 0.5,
2181 service: Some("test-service".to_string()),
2182 provenance: "remote".to_string(),
2183 ..SamplingRuleConfig::default()
2184 }];
2185
2186 let rules_json = serde_json::to_string(&new_rules).unwrap();
2187 config
2188 .update_sampling_rules_from_remote(&rules_json, None)
2189 .unwrap();
2190
2191 assert!(*callback_called.lock().unwrap());
2193 assert_eq!(*callback_rules.lock().unwrap(), new_rules);
2194
2195 *callback_called.lock().unwrap() = false;
2197 callback_rules.lock().unwrap().clear();
2198
2199 config.clear_remote_sampling_rules(None);
2200
2201 assert!(*callback_called.lock().unwrap());
2204 assert!(callback_rules.lock().unwrap().is_empty());
2205 }
2206
2207 #[test]
2208 fn test_config_item_priority() {
2209 let mut config_item = ConfigItemWithOverride::new_rc(
2211 SupportedConfigurations::DD_TRACE_SAMPLING_RULES,
2212 ParsedSamplingRules::default(),
2213 );
2214
2215 assert_eq!(config_item.source(), ConfigSourceOrigin::Default);
2217 assert_eq!(config_item.value().len(), 0);
2218
2219 config_item.set_value_source(
2221 ParsedSamplingRules {
2222 rules: vec![SamplingRuleConfig {
2223 sample_rate: 0.3,
2224 ..SamplingRuleConfig::default()
2225 }],
2226 },
2227 ConfigSourceOrigin::EnvVar,
2228 );
2229 assert_eq!(config_item.source(), ConfigSourceOrigin::EnvVar);
2230 assert_eq!(config_item.value()[0].sample_rate, 0.3);
2231
2232 config_item.set_code(ParsedSamplingRules {
2234 rules: vec![SamplingRuleConfig {
2235 sample_rate: 0.5,
2236 ..SamplingRuleConfig::default()
2237 }],
2238 });
2239 assert_eq!(config_item.source(), ConfigSourceOrigin::Code);
2240 assert_eq!(config_item.value()[0].sample_rate, 0.5);
2241
2242 config_item.set_value_source(
2244 ParsedSamplingRules {
2245 rules: vec![SamplingRuleConfig {
2246 sample_rate: 0.8,
2247 ..SamplingRuleConfig::default()
2248 }],
2249 },
2250 ConfigSourceOrigin::RemoteConfig,
2251 );
2252 assert_eq!(config_item.source(), ConfigSourceOrigin::RemoteConfig);
2253 assert_eq!(config_item.value()[0].sample_rate, 0.8);
2254
2255 config_item.unset_override_value();
2257 assert_eq!(config_item.source(), ConfigSourceOrigin::Code);
2258 assert_eq!(config_item.value()[0].sample_rate, 0.5);
2259 }
2260
2261 #[test]
2262 fn test_sampling_rules_with_config_item() {
2263 let mut sources = CompositeSource::new();
2265 sources.add_source(HashMapSource::from_iter(
2266 [(
2267 "DD_TRACE_SAMPLING_RULES",
2268 r#"[{"sample_rate":0.25,"service":"env-service"}]"#,
2269 )],
2270 ConfigSourceOrigin::EnvVar,
2271 ));
2272
2273 let config = Config::builder_with_sources(&sources).build();
2275 assert_eq!(config.trace_sampling_rules().len(), 1);
2276 assert_eq!(config.trace_sampling_rules()[0].sample_rate, 0.25);
2277
2278 let config = Config::builder_with_sources(&sources)
2280 .set_trace_sampling_rules(vec![SamplingRuleConfig {
2281 sample_rate: 0.75,
2282 service: Some("code-service".to_string()),
2283 ..SamplingRuleConfig::default()
2284 }])
2285 .build();
2286 assert_eq!(config.trace_sampling_rules()[0].sample_rate, 0.75);
2287 assert_eq!(
2288 config.trace_sampling_rules()[0].service.as_ref().unwrap(),
2289 "code-service"
2290 );
2291 }
2292
2293 #[test]
2294 fn test_empty_remote_rules_fallback_behavior() {
2295 let mut config = Config::builder().build();
2296
2297 let local_rules = ParsedSamplingRules {
2299 rules: vec![SamplingRuleConfig {
2300 sample_rate: 0.3,
2301 service: Some("local-service".to_string()),
2302 provenance: "local".to_string(),
2303 ..SamplingRuleConfig::default()
2304 }],
2305 };
2306 config
2307 .trace_sampling_rules
2308 .set_value_source(local_rules.clone(), ConfigSourceOrigin::EnvVar);
2309
2310 assert_eq!(config.trace_sampling_rules().len(), 1);
2312 assert_eq!(config.trace_sampling_rules()[0].sample_rate, 0.3);
2313 assert_eq!(
2314 config.trace_sampling_rules.source(),
2315 ConfigSourceOrigin::EnvVar
2316 );
2317
2318 let remote_rules_json =
2320 r#"[{"sample_rate": 0.8, "service": "remote-service", "provenance": "remote"}]"#;
2321 config
2322 .update_sampling_rules_from_remote(remote_rules_json, None)
2323 .unwrap();
2324
2325 assert_eq!(config.trace_sampling_rules().len(), 1);
2327 assert_eq!(config.trace_sampling_rules()[0].sample_rate, 0.8);
2328 assert_eq!(
2329 config.trace_sampling_rules.source(),
2330 ConfigSourceOrigin::RemoteConfig
2331 );
2332
2333 let empty_remote_rules_json = "[]";
2335 config
2336 .update_sampling_rules_from_remote(empty_remote_rules_json, None)
2337 .unwrap();
2338
2339 assert_eq!(config.trace_sampling_rules().len(), 1); assert_eq!(config.trace_sampling_rules()[0].sample_rate, 0.3); assert_eq!(
2343 config.trace_sampling_rules.source(),
2344 ConfigSourceOrigin::EnvVar
2345 ); config.clear_remote_sampling_rules(None);
2350
2351 assert_eq!(config.trace_sampling_rules().len(), 1);
2353 assert_eq!(config.trace_sampling_rules()[0].sample_rate, 0.3);
2354 assert_eq!(
2355 config.trace_sampling_rules.source(),
2356 ConfigSourceOrigin::EnvVar
2357 );
2358 }
2359
2360 #[test]
2361 fn test_update_sampling_rules_from_remote_config_id() {
2362 let config = Config::builder().build();
2363
2364 let new_rules = vec![SamplingRuleConfig {
2365 sample_rate: 0.5,
2366 service: Some("test-service".to_string()),
2367 provenance: "remote".to_string(),
2368 ..SamplingRuleConfig::default()
2369 }];
2370
2371 let rules_json = serde_json::to_string(&new_rules).unwrap();
2372 config
2373 .update_sampling_rules_from_remote(&rules_json, Some("config_id_1".to_string()))
2374 .unwrap();
2375
2376 assert_eq!(
2377 config.trace_sampling_rules.get_configuration().config_id,
2378 Some("config_id_1".to_string())
2379 );
2380
2381 config
2382 .update_sampling_rules_from_remote(&rules_json, Some("config_id_2".to_string()))
2383 .unwrap();
2384 assert_eq!(
2385 config.trace_sampling_rules.get_configuration().config_id,
2386 Some("config_id_2".to_string())
2387 );
2388
2389 config
2390 .update_sampling_rules_from_remote("[]", None)
2391 .unwrap();
2392 assert_eq!(
2393 config.trace_sampling_rules.get_configuration().config_id,
2394 None
2395 );
2396 }
2397
2398 #[test]
2399 fn test_telemetry_config_from_sources() {
2400 let mut sources = CompositeSource::new();
2401 sources.add_source(HashMapSource::from_iter(
2402 [
2403 ("DD_INSTRUMENTATION_TELEMETRY_ENABLED", "false"),
2404 ("DD_TELEMETRY_LOG_COLLECTION_ENABLED", "false"),
2405 ("DD_TELEMETRY_HEARTBEAT_INTERVAL", "42"),
2406 ],
2407 ConfigSourceOrigin::EnvVar,
2408 ));
2409 let config = Config::builder_with_sources(&sources).build();
2410
2411 assert!(!config.telemetry_enabled());
2412 assert!(!config.telemetry_log_collection_enabled());
2413 assert_eq!(config.telemetry_heartbeat_interval(), 42.0);
2414 }
2415
2416 #[test]
2417 fn test_telemetry_config() {
2418 let mut sources = CompositeSource::new();
2419 sources.add_source(HashMapSource::from_iter(
2420 [
2421 ("DD_INSTRUMENTATION_TELEMETRY_ENABLED", "false"),
2422 ("DD_TELEMETRY_LOG_COLLECTION_ENABLED", "false"),
2423 ("DD_TELEMETRY_HEARTBEAT_INTERVAL", "42"),
2424 ],
2425 ConfigSourceOrigin::EnvVar,
2426 ));
2427 let mut builder = Config::builder_with_sources(&sources);
2428
2429 builder
2430 .set_telemetry_enabled(true)
2431 .set_telemetry_log_collection_enabled(true)
2432 .set_telemetry_heartbeat_interval(0.1);
2433
2434 let config = builder.build();
2435
2436 assert!(config.telemetry_enabled());
2437 assert!(config.telemetry_log_collection_enabled());
2438 assert_eq!(config.telemetry_heartbeat_interval(), 0.1);
2439 }
2440
2441 #[test]
2442 fn test_dd_tags() {
2443 let mut sources = CompositeSource::new();
2444 sources.add_source(HashMapSource::from_iter(
2445 [("DD_TAGS", "key1 :value1 , key2:,key3")],
2446 ConfigSourceOrigin::EnvVar,
2447 ));
2448 let config = Config::builder_with_sources(&sources).build();
2449
2450 let tags: Vec<(&str, &str)> = config.global_tags().collect();
2451
2452 assert_eq!(tags.len(), 2);
2453 assert_eq!(tags, vec![("key1", "value1"), ("key2", "")]);
2454 }
2455
2456 #[test]
2457 fn test_dd_agent_url_default() {
2458 let config = Config::builder().build();
2459
2460 assert_eq!(config.trace_agent_url(), "http://localhost:8126");
2461 }
2462
2463 #[test]
2464 fn test_dd_agent_url_from_host_and_port() {
2465 let mut sources = CompositeSource::new();
2466 sources.add_source(HashMapSource::from_iter(
2467 [
2468 ("DD_AGENT_HOST", "agent-host"),
2469 ("DD_TRACE_AGENT_PORT", "4242"),
2470 ],
2471 ConfigSourceOrigin::EnvVar,
2472 ));
2473 let config = Config::builder_with_sources(&sources).build();
2474
2475 assert_eq!(config.trace_agent_url(), "http://agent-host:4242");
2476 }
2477
2478 #[test]
2479 fn test_dd_agent_url_from_url() {
2480 let mut sources = CompositeSource::new();
2481 sources.add_source(HashMapSource::from_iter(
2482 [
2483 ("DD_TRACE_AGENT_URL", "https://test-host"),
2484 ("DD_AGENT_HOST", "agent-host"),
2485 ("DD_TRACE_AGENT_PORT", "4242"),
2486 ],
2487 ConfigSourceOrigin::EnvVar,
2488 ));
2489 let config = Config::builder_with_sources(&sources).build();
2490
2491 assert_eq!(config.trace_agent_url(), "https://test-host");
2492 }
2493
2494 #[test]
2495 fn test_dd_agent_url_from_url_empty() {
2496 let mut sources = CompositeSource::new();
2497 sources.add_source(HashMapSource::from_iter(
2498 [
2499 ("DD_TRACE_AGENT_URL", ""),
2500 ("DD_AGENT_HOST", "agent-host"),
2501 ("DD_TRACE_AGENT_PORT", "4242"),
2502 ],
2503 ConfigSourceOrigin::EnvVar,
2504 ));
2505 let config = Config::builder_with_sources(&sources).build();
2506
2507 assert_eq!(config.trace_agent_url(), "http://agent-host:4242");
2508 }
2509
2510 #[test]
2511 fn test_dd_agent_url_from_host_and_port_using_builder() {
2512 let config = Config::builder()
2513 .set_agent_host("agent-host".into())
2514 .set_trace_agent_port(4242)
2515 .build();
2516
2517 assert_eq!(config.trace_agent_url(), "http://agent-host:4242");
2518 }
2519
2520 #[test]
2521 fn test_dd_agent_url_from_url_using_builder() {
2522 let config = Config::builder()
2523 .set_agent_host("agent-host".into())
2524 .set_trace_agent_port(4242)
2525 .set_trace_agent_url("https://test-host".into())
2526 .build();
2527
2528 assert_eq!(config.trace_agent_url(), "https://test-host");
2529 }
2530
2531 #[test]
2532 fn test_dogstatsd_agent_url_default() {
2533 let config = Config::builder().build();
2534
2535 assert_eq!(config.dogstatsd_agent_url(), "http://localhost:8125");
2536 }
2537
2538 #[test]
2539 fn test_dogstatsd_agent_url_from_host_and_port() {
2540 let mut sources = CompositeSource::new();
2541 sources.add_source(HashMapSource::from_iter(
2542 [
2543 ("DD_DOGSTATSD_HOST", "dogstatsd-host"),
2544 ("DD_DOGSTATSD_PORT", "4242"),
2545 ],
2546 ConfigSourceOrigin::EnvVar,
2547 ));
2548 let config = Config::builder_with_sources(&sources).build();
2549
2550 assert_eq!(config.dogstatsd_agent_url(), "http://dogstatsd-host:4242");
2551 }
2552
2553 #[test]
2554 fn test_dogstatsd_agent_url_from_url_using_builder() {
2555 let config = Config::builder()
2556 .set_dogstatsd_agent_host("dogstatsd-host".into())
2557 .set_dogstatsd_agent_port(4242)
2558 .build();
2559
2560 assert_eq!(config.dogstatsd_agent_url(), "http://dogstatsd-host:4242");
2561 }
2562
2563 #[test]
2564 fn test_config_source_updater() {
2565 let mut sources = CompositeSource::new();
2566 sources.add_source(HashMapSource::from_iter(
2567 [("DD_ENV", "test-env")],
2568 ConfigSourceOrigin::EnvVar,
2569 ));
2570 sources.add_source(HashMapSource::from_iter(
2571 [("DD_ENABLED", "false")],
2572 ConfigSourceOrigin::RemoteConfig,
2573 ));
2574 sources.add_source(HashMapSource::from_iter(
2575 [("DD_TAGS", "v1,v2")],
2576 ConfigSourceOrigin::Code,
2577 ));
2578 let default = default_config();
2579
2580 let cisu = ConfigItemSourceUpdater { sources: &sources };
2581
2582 assert_eq!(default.env(), None);
2583 assert!(default.enabled());
2584 assert_eq!(default.global_tags().collect::<Vec<_>>(), vec![]);
2585
2586 let env = cisu.update_string(default.env, Some);
2587 assert_eq!(env.default_value, None);
2588 assert_eq!(env.env_value, Some(Some("test-env".to_string())));
2589 assert_eq!(env.code_value, None);
2590
2591 let enabled = cisu.update_parsed(default.enabled);
2592 assert!(enabled.default_value);
2593 assert_eq!(enabled.env_value, None);
2594 assert_eq!(enabled.code_value, None);
2595
2596 struct Tags(Vec<(String, String)>);
2597
2598 impl FromStr for Tags {
2599 type Err = &'static str;
2600
2601 fn from_str(s: &str) -> Result<Self, Self::Err> {
2602 Ok(Tags(
2603 s.split(',')
2604 .enumerate()
2605 .map(|(index, s)| (index.to_string(), s.to_string()))
2606 .collect(),
2607 ))
2608 }
2609 }
2610
2611 let tags = cisu.update_parsed_with_transform(default.global_tags, |Tags(tags)| tags);
2612 assert_eq!(tags.default_value, vec![]);
2613 assert_eq!(tags.env_value, None);
2614 assert_eq!(
2615 tags.code_value,
2616 Some(vec![
2617 ("0".to_string(), "v1".to_string()),
2618 ("1".to_string(), "v2".to_string())
2619 ])
2620 );
2621 }
2622
2623 #[test]
2624 fn test_get_configuration_config_item_rc() {
2625 let mut sources = CompositeSource::new();
2626 sources.add_source(HashMapSource::from_iter(
2627 [
2628 ("DD_TRACE_SAMPLING_RULES",
2629 r#"[{"sample_rate":0.5,"service":"web-api","name":null,"resource":null,"tags":{},"provenance":"customer"}]"#),
2630 ],
2631 ConfigSourceOrigin::EnvVar,
2632 ));
2633 let config = Config::builder_with_sources(&sources).build();
2634
2635 let expected = ParsedSamplingRules::from_str(
2636 r#"[{"sample_rate":0.5,"service":"web-api","name":null,"resource":null,"tags":{},"provenance":"customer"}]"#
2637 ).unwrap();
2638
2639 let configuration = &config.trace_sampling_rules.get_configuration();
2640 assert_eq!(configuration.origin, ConfigurationOrigin::EnvVar);
2641
2642 assert_eq!(
2645 ParsedSamplingRules::from_str(&configuration.value).unwrap(),
2646 expected.clone()
2647 );
2648
2649 let expected_rc = ParsedSamplingRules::from_str(r#"[{"sample_rate":1,"service":"web-api","name":null,"resource":null,"tags":{},"provenance":"customer"}]"#).unwrap();
2651 config
2652 .trace_sampling_rules
2653 .set_override_value(expected_rc.clone(), ConfigSourceOrigin::RemoteConfig);
2654
2655 let configuration_after_rc = &config.trace_sampling_rules.get_configuration();
2656 assert_eq!(
2657 configuration_after_rc.origin,
2658 ConfigurationOrigin::RemoteConfig
2659 );
2660 assert_eq!(
2661 ParsedSamplingRules::from_str(&configuration_after_rc.value).unwrap(),
2662 expected_rc
2663 );
2664
2665 config.trace_sampling_rules.unset_override_value();
2667
2668 let configuration = &config.trace_sampling_rules.get_configuration();
2669 assert_eq!(configuration.origin, ConfigurationOrigin::EnvVar);
2670 assert_eq!(
2671 ParsedSamplingRules::from_str(&configuration.value).unwrap(),
2672 expected
2673 );
2674 }
2675
2676 #[test]
2677 fn test_datadog_tags_max_length() {
2678 let config = Config::builder().set_datadog_tags_max_length(4242).build();
2679
2680 assert_eq!(config.datadog_tags_max_length(), DATADOG_TAGS_MAX_LENGTH);
2681
2682 let mut sources = CompositeSource::new();
2683 sources.add_source(HashMapSource::from_iter(
2684 [("DD_TRACE_X_DATADOG_TAGS_MAX_LENGTH", "4242")],
2685 ConfigSourceOrigin::EnvVar,
2686 ));
2687 let config = Config::builder_with_sources(&sources).build();
2688 assert_eq!(config.datadog_tags_max_length(), DATADOG_TAGS_MAX_LENGTH);
2689
2690 let mut sources = CompositeSource::new();
2691 sources.add_source(HashMapSource::from_iter(
2692 [("DD_TRACE_X_DATADOG_TAGS_MAX_LENGTH", "42")],
2693 ConfigSourceOrigin::EnvVar,
2694 ));
2695 let config = Config::builder_with_sources(&sources).build();
2696 assert_eq!(config.datadog_tags_max_length(), 42);
2697 }
2698
2699 #[test]
2700 fn test_remote_config_poll_interval() {
2701 let config = Config::builder()
2702 .set_remote_config_poll_interval(42.0)
2703 .build();
2704
2705 assert_eq!(config.remote_config_poll_interval(), 5.0);
2706
2707 let config = Config::builder()
2708 .set_remote_config_poll_interval(-0.2)
2709 .build();
2710
2711 assert_eq!(config.remote_config_poll_interval(), 0.2);
2712 }
2713}