1use std::collections::HashMap;
10use std::env;
11
12use super::parse::{
13 env_value, nonempty_env_value, parse_bool, parse_module_levels, parse_non_negative_float,
14 parse_otlp_headers, parse_rate, parse_usize,
15};
16use super::{
17 BackpressureConfig, EventSchemaConfig, ExporterPolicyConfig, LoggingConfig, MetricsConfig,
18 SLOConfig, SamplingConfig, SecurityConfig, TelemetryConfig, TracingConfig,
19};
20use crate::errors::ConfigurationError;
21
22impl TelemetryConfig {
23 pub fn from_env() -> Result<Self, ConfigurationError> {
24 let env_map = env::vars().collect::<HashMap<_, _>>();
25 Self::from_map(&env_map)
26 }
27
28 pub fn from_map(env: &HashMap<String, String>) -> Result<Self, ConfigurationError> {
29 let shared_headers =
30 parse_otlp_headers(env_value(env, &["OTEL_EXPORTER_OTLP_HEADERS"])).unwrap_or_default();
31 let shared_endpoint = nonempty_env_value(env, &["OTEL_EXPORTER_OTLP_ENDPOINT"]);
32 let shared_protocol = env_value(env, &["OTEL_EXPORTER_OTLP_PROTOCOL"]).unwrap_or("");
33 let with_signal_path = |signal_path: &str| -> Option<String> {
37 shared_endpoint.map(|base| format!("{}/{}", base.trim_end_matches('/'), signal_path))
38 };
39
40 Ok(Self {
41 service_name: env_value(env, &["PROVIDE_TELEMETRY_SERVICE_NAME"])
42 .unwrap_or("provide-service")
43 .to_string(),
44 environment: env_value(env, &["PROVIDE_TELEMETRY_ENV", "PROVIDE_ENV"])
45 .unwrap_or("dev")
46 .to_string(),
47 version: env_value(env, &["PROVIDE_TELEMETRY_VERSION", "PROVIDE_VERSION"])
48 .unwrap_or("0.0.0")
49 .to_string(),
50 strict_schema: parse_bool(
51 env_value(env, &["PROVIDE_TELEMETRY_STRICT_SCHEMA"]),
52 false,
53 "PROVIDE_TELEMETRY_STRICT_SCHEMA",
54 )?,
55 pii_max_depth: parse_usize(
56 env_value(env, &["PROVIDE_LOG_PII_MAX_DEPTH"]),
57 8,
58 "PROVIDE_LOG_PII_MAX_DEPTH",
59 )?,
60 logging: LoggingConfig {
61 level: env_value(env, &["PROVIDE_LOG_LEVEL"])
62 .unwrap_or("INFO")
63 .to_string(),
64 fmt: env_value(env, &["PROVIDE_LOG_FORMAT"])
65 .unwrap_or("console")
66 .to_string(),
67 include_timestamp: parse_bool(
68 env_value(env, &["PROVIDE_LOG_INCLUDE_TIMESTAMP"]),
69 true,
70 "PROVIDE_LOG_INCLUDE_TIMESTAMP",
71 )?,
72 otlp_headers: parse_otlp_headers(env_value(
73 env,
74 &["OTEL_EXPORTER_OTLP_LOGS_HEADERS"],
75 ))
76 .unwrap_or_else(|| shared_headers.clone()),
77 otlp_endpoint: nonempty_env_value(env, &["OTEL_EXPORTER_OTLP_LOGS_ENDPOINT"])
78 .map(str::to_string)
79 .or_else(|| with_signal_path("v1/logs")),
80 otlp_protocol: env_value(env, &["OTEL_EXPORTER_OTLP_LOGS_PROTOCOL"])
81 .unwrap_or(shared_protocol)
82 .to_string(),
83 module_levels: parse_module_levels(
84 env_value(env, &["PROVIDE_LOG_MODULE_LEVELS"]).unwrap_or(""),
85 ),
86 },
87 tracing: TracingConfig {
88 enabled: parse_bool(
89 env_value(env, &["PROVIDE_TRACE_ENABLED"]),
90 true,
91 "PROVIDE_TRACE_ENABLED",
92 )?,
93 sample_rate: parse_rate(
94 env_value(env, &["PROVIDE_TRACE_SAMPLE_RATE"]),
95 1.0,
96 "PROVIDE_TRACE_SAMPLE_RATE",
97 )?,
98 otlp_headers: parse_otlp_headers(env_value(
99 env,
100 &["OTEL_EXPORTER_OTLP_TRACES_HEADERS"],
101 ))
102 .unwrap_or_else(|| shared_headers.clone()),
103 otlp_endpoint: nonempty_env_value(env, &["OTEL_EXPORTER_OTLP_TRACES_ENDPOINT"])
104 .map(str::to_string)
105 .or_else(|| with_signal_path("v1/traces")),
106 otlp_protocol: env_value(env, &["OTEL_EXPORTER_OTLP_TRACES_PROTOCOL"])
107 .unwrap_or(shared_protocol)
108 .to_string(),
109 },
110 metrics: MetricsConfig {
111 enabled: parse_bool(
112 env_value(env, &["PROVIDE_METRICS_ENABLED"]),
113 true,
114 "PROVIDE_METRICS_ENABLED",
115 )?,
116 otlp_headers: parse_otlp_headers(env_value(
117 env,
118 &["OTEL_EXPORTER_OTLP_METRICS_HEADERS"],
119 ))
120 .unwrap_or(shared_headers),
121 otlp_endpoint: nonempty_env_value(env, &["OTEL_EXPORTER_OTLP_METRICS_ENDPOINT"])
122 .map(str::to_string)
123 .or_else(|| with_signal_path("v1/metrics")),
124 otlp_protocol: env_value(env, &["OTEL_EXPORTER_OTLP_METRICS_PROTOCOL"])
125 .unwrap_or(shared_protocol)
126 .to_string(),
127 metric_export_interval_ms: parse_usize(
128 env_value(env, &["OTEL_METRIC_EXPORT_INTERVAL"]),
129 60_000,
130 "OTEL_METRIC_EXPORT_INTERVAL",
131 )? as u64,
132 },
133 event_schema: EventSchemaConfig {
134 strict_event_name: parse_bool(
135 env_value(env, &["PROVIDE_TELEMETRY_STRICT_EVENT_NAME"]),
136 false,
137 "PROVIDE_TELEMETRY_STRICT_EVENT_NAME",
138 )?,
139 required_keys: env_value(env, &["PROVIDE_TELEMETRY_REQUIRED_KEYS"])
140 .unwrap_or("")
141 .split(',')
142 .map(str::trim)
143 .filter(|value| !value.is_empty())
144 .map(str::to_string)
145 .collect(),
146 },
147 sampling: SamplingConfig {
148 logs_rate: parse_rate(
149 env_value(env, &["PROVIDE_SAMPLING_LOGS_RATE"]),
150 1.0,
151 "PROVIDE_SAMPLING_LOGS_RATE",
152 )?,
153 traces_rate: parse_rate(
154 env_value(env, &["PROVIDE_SAMPLING_TRACES_RATE"]),
155 1.0,
156 "PROVIDE_SAMPLING_TRACES_RATE",
157 )?,
158 metrics_rate: parse_rate(
159 env_value(env, &["PROVIDE_SAMPLING_METRICS_RATE"]),
160 1.0,
161 "PROVIDE_SAMPLING_METRICS_RATE",
162 )?,
163 },
164 backpressure: BackpressureConfig {
165 logs_maxsize: parse_usize(
166 env_value(env, &["PROVIDE_BACKPRESSURE_LOGS_MAXSIZE"]),
167 0,
168 "PROVIDE_BACKPRESSURE_LOGS_MAXSIZE",
169 )?,
170 traces_maxsize: parse_usize(
171 env_value(env, &["PROVIDE_BACKPRESSURE_TRACES_MAXSIZE"]),
172 0,
173 "PROVIDE_BACKPRESSURE_TRACES_MAXSIZE",
174 )?,
175 metrics_maxsize: parse_usize(
176 env_value(env, &["PROVIDE_BACKPRESSURE_METRICS_MAXSIZE"]),
177 0,
178 "PROVIDE_BACKPRESSURE_METRICS_MAXSIZE",
179 )?,
180 },
181 exporter: ExporterPolicyConfig {
182 logs_retries: parse_usize(
183 env_value(env, &["PROVIDE_EXPORTER_LOGS_RETRIES"]),
184 0,
185 "PROVIDE_EXPORTER_LOGS_RETRIES",
186 )?,
187 traces_retries: parse_usize(
188 env_value(env, &["PROVIDE_EXPORTER_TRACES_RETRIES"]),
189 0,
190 "PROVIDE_EXPORTER_TRACES_RETRIES",
191 )?,
192 metrics_retries: parse_usize(
193 env_value(env, &["PROVIDE_EXPORTER_METRICS_RETRIES"]),
194 0,
195 "PROVIDE_EXPORTER_METRICS_RETRIES",
196 )?,
197 logs_backoff_seconds: parse_non_negative_float(
198 env_value(env, &["PROVIDE_EXPORTER_LOGS_BACKOFF_SECONDS"]),
199 0.0,
200 "PROVIDE_EXPORTER_LOGS_BACKOFF_SECONDS",
201 )?,
202 traces_backoff_seconds: parse_non_negative_float(
203 env_value(env, &["PROVIDE_EXPORTER_TRACES_BACKOFF_SECONDS"]),
204 0.0,
205 "PROVIDE_EXPORTER_TRACES_BACKOFF_SECONDS",
206 )?,
207 metrics_backoff_seconds: parse_non_negative_float(
208 env_value(env, &["PROVIDE_EXPORTER_METRICS_BACKOFF_SECONDS"]),
209 0.0,
210 "PROVIDE_EXPORTER_METRICS_BACKOFF_SECONDS",
211 )?,
212 logs_timeout_seconds: parse_non_negative_float(
213 env_value(env, &["PROVIDE_EXPORTER_LOGS_TIMEOUT_SECONDS"]),
214 10.0,
215 "PROVIDE_EXPORTER_LOGS_TIMEOUT_SECONDS",
216 )?,
217 traces_timeout_seconds: parse_non_negative_float(
218 env_value(env, &["PROVIDE_EXPORTER_TRACES_TIMEOUT_SECONDS"]),
219 10.0,
220 "PROVIDE_EXPORTER_TRACES_TIMEOUT_SECONDS",
221 )?,
222 metrics_timeout_seconds: parse_non_negative_float(
223 env_value(env, &["PROVIDE_EXPORTER_METRICS_TIMEOUT_SECONDS"]),
224 10.0,
225 "PROVIDE_EXPORTER_METRICS_TIMEOUT_SECONDS",
226 )?,
227 logs_fail_open: parse_bool(
228 env_value(env, &["PROVIDE_EXPORTER_LOGS_FAIL_OPEN"]),
229 true,
230 "PROVIDE_EXPORTER_LOGS_FAIL_OPEN",
231 )?,
232 traces_fail_open: parse_bool(
233 env_value(env, &["PROVIDE_EXPORTER_TRACES_FAIL_OPEN"]),
234 true,
235 "PROVIDE_EXPORTER_TRACES_FAIL_OPEN",
236 )?,
237 metrics_fail_open: parse_bool(
238 env_value(env, &["PROVIDE_EXPORTER_METRICS_FAIL_OPEN"]),
239 true,
240 "PROVIDE_EXPORTER_METRICS_FAIL_OPEN",
241 )?,
242 },
243 slo: SLOConfig {
244 enable_red_metrics: parse_bool(
245 env_value(env, &["PROVIDE_SLO_ENABLE_RED_METRICS"]),
246 false,
247 "PROVIDE_SLO_ENABLE_RED_METRICS",
248 )?,
249 enable_use_metrics: parse_bool(
250 env_value(env, &["PROVIDE_SLO_ENABLE_USE_METRICS"]),
251 false,
252 "PROVIDE_SLO_ENABLE_USE_METRICS",
253 )?,
254 },
255 security: SecurityConfig {
256 max_attr_value_length: parse_usize(
257 env_value(env, &["PROVIDE_SECURITY_MAX_ATTR_VALUE_LENGTH"]),
258 1024,
259 "PROVIDE_SECURITY_MAX_ATTR_VALUE_LENGTH",
260 )?,
261 max_attr_count: parse_usize(
262 env_value(env, &["PROVIDE_SECURITY_MAX_ATTR_COUNT"]),
263 64,
264 "PROVIDE_SECURITY_MAX_ATTR_COUNT",
265 )?,
266 max_nesting_depth: parse_usize(
267 env_value(env, &["PROVIDE_SECURITY_MAX_NESTING_DEPTH"]),
268 8,
269 "PROVIDE_SECURITY_MAX_NESTING_DEPTH",
270 )?,
271 },
272 })
273 }
274}
275
276#[cfg(test)]
277#[path = "from_env_tests.rs"]
278mod tests;