saluki_components/config/
agent_env.rs1use saluki_common::strings::StringBuilder;
4use stringtheory::MetaString;
5use tracing::{debug, warn};
6
7use crate::common::otlp::traces::normalize::normalize_tag_value_into_unchecked;
8
9const FALLBACK_ENV: &str = "none";
11
12const ENV_TAG_PREFIX: &str = "env:";
14
15#[derive(Clone, Debug, Eq, PartialEq)]
20pub struct AgentEnv(MetaString);
21
22impl AgentEnv {
23 pub fn resolve(apm_env: Option<&str>, env: Option<&str>, tags: &[String], extra_tags: &[String]) -> Self {
36 let (source, configured) = apm_env
37 .map(|value| ("apm_config.env", value))
38 .or_else(|| env.map(|value| ("env", value)))
39 .or_else(|| {
40 let tags = tags.iter().map(|tag| ("tags", tag));
41 let extra_tags = extra_tags.iter().map(|tag| ("extra_tags", tag));
42 tags.chain(extra_tags)
43 .find_map(|(source, tag)| tag.strip_prefix(ENV_TAG_PREFIX).map(|value| (source, value)))
44 })
45 .unwrap_or(("default", ""));
46
47 let selected = if configured.is_empty() {
48 FALLBACK_ENV
49 } else {
50 configured
51 };
52 let mut normalized = StringBuilder::new();
53 normalize_tag_value_into_unchecked(selected, &mut normalized);
54 if normalized.as_str().is_empty() {
55 warn!(
58 source,
59 configured,
60 "The trace agent environment normalizes to an empty value. Set `apm_config.env`, `env`, or the \
61 `env:` global tag to a valid tag value, such as `prod`."
62 );
63 } else {
64 debug!(
65 source,
66 configured,
67 env = normalized.as_str(),
68 "Resolved the trace agent environment."
69 );
70 }
71
72 Self(MetaString::from(normalized.as_str()))
73 }
74
75 pub fn as_str(&self) -> &str {
77 &self.0
78 }
79}
80
81impl From<AgentEnv> for MetaString {
82 fn from(env: AgentEnv) -> Self {
83 env.0
84 }
85}
86
87#[cfg(test)]
88mod tests {
89 use super::*;
90
91 #[track_caller]
92 fn assert_resolves(apm_env: Option<&str>, env: Option<&str>, tags: &[&str], extra_tags: &[&str], expected: &str) {
93 let tags: Vec<String> = tags.iter().map(|tag| tag.to_string()).collect();
94 let extra_tags: Vec<String> = extra_tags.iter().map(|tag| tag.to_string()).collect();
95 assert_eq!(AgentEnv::resolve(apm_env, env, &tags, &extra_tags).as_str(), expected);
96 }
97
98 #[test]
103 fn resolve_follows_agent_precedence() {
104 assert_resolves(None, None, &[], &[], "none");
105
106 assert_resolves(Some("Staging"), Some("prod"), &["env:t"], &[], "staging");
108 assert_resolves(None, Some("Prod"), &["env:t"], &[], "prod");
109 assert_resolves(None, None, &["a:b", "env:first", "env:second"], &["env:x"], "first");
110 assert_resolves(None, None, &["a:b"], &["env:x"], "x");
111 assert_resolves(None, None, &["ENV:up", "env:low"], &[], "low");
112
113 assert_resolves(None, None, &["env:", "env:x"], &[], "none");
115 assert_resolves(Some(""), Some("prod"), &[], &[], "none");
116 assert_resolves(None, Some(""), &["env:t"], &[], "none");
117
118 assert_resolves(Some("!!!"), None, &[], &[], "");
120
121 assert_resolves(None, None, &["a:b", "env:tagged"], &[], "tagged");
123
124 assert_resolves(None, Some("prod"), &[], &[], "prod");
126 }
127}