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_retries, 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_enabled: parse_bool(
81 env_value(env, &["PROVIDE_LOG_OTLP_ENABLED"]),
82 true,
83 "PROVIDE_LOG_OTLP_ENABLED",
84 )?,
85 otlp_protocol: env_value(env, &["OTEL_EXPORTER_OTLP_LOGS_PROTOCOL"])
86 .unwrap_or(shared_protocol)
87 .to_string(),
88 module_levels: parse_module_levels(
89 env_value(env, &["PROVIDE_LOG_MODULE_LEVELS"]).unwrap_or(""),
90 ),
91 },
92 tracing: TracingConfig {
93 enabled: parse_bool(
94 env_value(env, &["PROVIDE_TRACE_ENABLED"]),
95 true,
96 "PROVIDE_TRACE_ENABLED",
97 )?,
98 sample_rate: parse_rate(
99 env_value(env, &["PROVIDE_TRACE_SAMPLE_RATE"]),
100 1.0,
101 "PROVIDE_TRACE_SAMPLE_RATE",
102 )?,
103 otlp_headers: parse_otlp_headers(env_value(
104 env,
105 &["OTEL_EXPORTER_OTLP_TRACES_HEADERS"],
106 ))
107 .unwrap_or_else(|| shared_headers.clone()),
108 otlp_endpoint: nonempty_env_value(env, &["OTEL_EXPORTER_OTLP_TRACES_ENDPOINT"])
109 .map(str::to_string)
110 .or_else(|| with_signal_path("v1/traces")),
111 otlp_protocol: env_value(env, &["OTEL_EXPORTER_OTLP_TRACES_PROTOCOL"])
112 .unwrap_or(shared_protocol)
113 .to_string(),
114 },
115 metrics: MetricsConfig {
116 enabled: parse_bool(
117 env_value(env, &["PROVIDE_METRICS_ENABLED"]),
118 true,
119 "PROVIDE_METRICS_ENABLED",
120 )?,
121 otlp_headers: parse_otlp_headers(env_value(
122 env,
123 &["OTEL_EXPORTER_OTLP_METRICS_HEADERS"],
124 ))
125 .unwrap_or(shared_headers),
126 otlp_endpoint: nonempty_env_value(env, &["OTEL_EXPORTER_OTLP_METRICS_ENDPOINT"])
127 .map(str::to_string)
128 .or_else(|| with_signal_path("v1/metrics")),
129 otlp_protocol: env_value(env, &["OTEL_EXPORTER_OTLP_METRICS_PROTOCOL"])
130 .unwrap_or(shared_protocol)
131 .to_string(),
132 metric_export_interval_ms: parse_usize(
133 env_value(env, &["OTEL_METRIC_EXPORT_INTERVAL"]),
134 60_000,
135 "OTEL_METRIC_EXPORT_INTERVAL",
136 )? as u64,
137 },
138 event_schema: EventSchemaConfig {
139 strict_event_name: parse_bool(
140 env_value(env, &["PROVIDE_TELEMETRY_STRICT_EVENT_NAME"]),
141 false,
142 "PROVIDE_TELEMETRY_STRICT_EVENT_NAME",
143 )?,
144 required_keys: env_value(env, &["PROVIDE_TELEMETRY_REQUIRED_KEYS"])
145 .unwrap_or("")
146 .split(',')
147 .map(str::trim)
148 .filter(|value| !value.is_empty())
149 .map(str::to_string)
150 .collect(),
151 },
152 sampling: SamplingConfig {
153 logs_rate: parse_rate(
154 env_value(env, &["PROVIDE_SAMPLING_LOGS_RATE"]),
155 1.0,
156 "PROVIDE_SAMPLING_LOGS_RATE",
157 )?,
158 traces_rate: parse_rate(
159 env_value(env, &["PROVIDE_SAMPLING_TRACES_RATE"]),
160 1.0,
161 "PROVIDE_SAMPLING_TRACES_RATE",
162 )?,
163 metrics_rate: parse_rate(
164 env_value(env, &["PROVIDE_SAMPLING_METRICS_RATE"]),
165 1.0,
166 "PROVIDE_SAMPLING_METRICS_RATE",
167 )?,
168 },
169 backpressure: BackpressureConfig {
170 logs_maxsize: parse_usize(
171 env_value(env, &["PROVIDE_BACKPRESSURE_LOGS_MAXSIZE"]),
172 0,
173 "PROVIDE_BACKPRESSURE_LOGS_MAXSIZE",
174 )?,
175 traces_maxsize: parse_usize(
176 env_value(env, &["PROVIDE_BACKPRESSURE_TRACES_MAXSIZE"]),
177 0,
178 "PROVIDE_BACKPRESSURE_TRACES_MAXSIZE",
179 )?,
180 metrics_maxsize: parse_usize(
181 env_value(env, &["PROVIDE_BACKPRESSURE_METRICS_MAXSIZE"]),
182 0,
183 "PROVIDE_BACKPRESSURE_METRICS_MAXSIZE",
184 )?,
185 },
186 exporter: ExporterPolicyConfig {
187 logs_retries: parse_retries(
188 env_value(env, &["PROVIDE_EXPORTER_LOGS_RETRIES"]),
189 0,
190 "PROVIDE_EXPORTER_LOGS_RETRIES",
191 )?,
192 traces_retries: parse_retries(
193 env_value(env, &["PROVIDE_EXPORTER_TRACES_RETRIES"]),
194 0,
195 "PROVIDE_EXPORTER_TRACES_RETRIES",
196 )?,
197 metrics_retries: parse_retries(
198 env_value(env, &["PROVIDE_EXPORTER_METRICS_RETRIES"]),
199 0,
200 "PROVIDE_EXPORTER_METRICS_RETRIES",
201 )?,
202 logs_backoff_seconds: parse_non_negative_float(
203 env_value(env, &["PROVIDE_EXPORTER_LOGS_BACKOFF_SECONDS"]),
204 0.0,
205 "PROVIDE_EXPORTER_LOGS_BACKOFF_SECONDS",
206 )?,
207 traces_backoff_seconds: parse_non_negative_float(
208 env_value(env, &["PROVIDE_EXPORTER_TRACES_BACKOFF_SECONDS"]),
209 0.0,
210 "PROVIDE_EXPORTER_TRACES_BACKOFF_SECONDS",
211 )?,
212 metrics_backoff_seconds: parse_non_negative_float(
213 env_value(env, &["PROVIDE_EXPORTER_METRICS_BACKOFF_SECONDS"]),
214 0.0,
215 "PROVIDE_EXPORTER_METRICS_BACKOFF_SECONDS",
216 )?,
217 logs_timeout_seconds: parse_non_negative_float(
218 env_value(env, &["PROVIDE_EXPORTER_LOGS_TIMEOUT_SECONDS"]),
219 10.0,
220 "PROVIDE_EXPORTER_LOGS_TIMEOUT_SECONDS",
221 )?,
222 traces_timeout_seconds: parse_non_negative_float(
223 env_value(env, &["PROVIDE_EXPORTER_TRACES_TIMEOUT_SECONDS"]),
224 10.0,
225 "PROVIDE_EXPORTER_TRACES_TIMEOUT_SECONDS",
226 )?,
227 metrics_timeout_seconds: parse_non_negative_float(
228 env_value(env, &["PROVIDE_EXPORTER_METRICS_TIMEOUT_SECONDS"]),
229 10.0,
230 "PROVIDE_EXPORTER_METRICS_TIMEOUT_SECONDS",
231 )?,
232 logs_shutdown_timeout_seconds: parse_non_negative_float(
233 env_value(env, &["PROVIDE_EXPORTER_LOGS_SHUTDOWN_TIMEOUT_SECONDS"]),
234 5.0,
235 "PROVIDE_EXPORTER_LOGS_SHUTDOWN_TIMEOUT_SECONDS",
236 )?,
237 logs_fail_open: parse_bool(
238 env_value(env, &["PROVIDE_EXPORTER_LOGS_FAIL_OPEN"]),
239 true,
240 "PROVIDE_EXPORTER_LOGS_FAIL_OPEN",
241 )?,
242 traces_fail_open: parse_bool(
243 env_value(env, &["PROVIDE_EXPORTER_TRACES_FAIL_OPEN"]),
244 true,
245 "PROVIDE_EXPORTER_TRACES_FAIL_OPEN",
246 )?,
247 metrics_fail_open: parse_bool(
248 env_value(env, &["PROVIDE_EXPORTER_METRICS_FAIL_OPEN"]),
249 true,
250 "PROVIDE_EXPORTER_METRICS_FAIL_OPEN",
251 )?,
252 },
253 slo: SLOConfig {
254 enable_red_metrics: parse_bool(
255 env_value(env, &["PROVIDE_SLO_ENABLE_RED_METRICS"]),
256 false,
257 "PROVIDE_SLO_ENABLE_RED_METRICS",
258 )?,
259 enable_use_metrics: parse_bool(
260 env_value(env, &["PROVIDE_SLO_ENABLE_USE_METRICS"]),
261 false,
262 "PROVIDE_SLO_ENABLE_USE_METRICS",
263 )?,
264 },
265 security: SecurityConfig {
266 max_attr_value_length: parse_usize(
267 env_value(env, &["PROVIDE_SECURITY_MAX_ATTR_VALUE_LENGTH"]),
268 1024,
269 "PROVIDE_SECURITY_MAX_ATTR_VALUE_LENGTH",
270 )?,
271 max_attr_count: parse_usize(
272 env_value(env, &["PROVIDE_SECURITY_MAX_ATTR_COUNT"]),
273 64,
274 "PROVIDE_SECURITY_MAX_ATTR_COUNT",
275 )?,
276 max_nesting_depth: parse_usize(
277 env_value(env, &["PROVIDE_SECURITY_MAX_NESTING_DEPTH"]),
278 8,
279 "PROVIDE_SECURITY_MAX_NESTING_DEPTH",
280 )?,
281 },
282 })
283 }
284}
285
286#[cfg(test)]
287#[path = "from_env_tests.rs"]
288mod tests;