1use crate::macros::build_exporter;
9use anyhow::Result;
10use opentelemetry::global;
11use opentelemetry_sdk::{
12 Resource,
13 logs::SdkLoggerProvider,
14 metrics::{MeterProviderBuilder, PeriodicReader, SdkMeterProvider, Temporality},
15 propagation::TraceContextPropagator,
16 trace::{RandomIdGenerator, Sampler, SdkTracerProvider},
17};
18use std::{env::var, time::Duration};
19
20const OTEL_EXPORTER_OTLP_ENDPOINT: &str = "OTEL_EXPORTER_OTLP_ENDPOINT";
22const OTEL_EXPORTER_OTLP_TRACES_PROTOCOL: &str = "OTEL_EXPORTER_OTLP_TRACES_PROTOCOL";
24const OTEL_EXPORTER_OTLP_TRACES_ENDPOINT: &str = "OTEL_EXPORTER_OTLP_TRACES_ENDPOINT";
26const OTEL_EXPORTER_OTLP_METRICS_PROTOCOL: &str = "OTEL_EXPORTER_OTLP_METRICS_PROTOCOL";
28const OTEL_EXPORTER_OTLP_METRICS_ENDPOINT: &str = "OTEL_EXPORTER_OTLP_METRICS_ENDPOINT";
30const OTEL_EXPORTER_OTLP_LOGS_PROTOCOL: &str = "OTEL_EXPORTER_OTLP_LOGS_PROTOCOL";
32const OTEL_EXPORTER_OTLP_LOGS_ENDPOINT: &str = "OTEL_EXPORTER_OTLP_LOGS_ENDPOINT";
34
35fn exporter_enabled(endpoint_env: &str) -> bool {
51 endpoint_configured(endpoint_env) || endpoint_configured(OTEL_EXPORTER_OTLP_ENDPOINT)
52}
53
54fn endpoint_configured(endpoint_env: &str) -> bool {
68 var(endpoint_env)
69 .ok()
70 .is_some_and(|value| !value.trim().is_empty())
71}
72
73fn build_span_exporter() -> Result<opentelemetry_otlp::SpanExporter> {
86 build_exporter!(
87 opentelemetry_otlp::SpanExporter::builder(),
88 OTEL_EXPORTER_OTLP_TRACES_PROTOCOL,
89 "Failed to build OTLP span exporter"
90 )
91}
92
93fn build_metric_exporter() -> Result<opentelemetry_otlp::MetricExporter> {
106 build_exporter!(
107 opentelemetry_otlp::MetricExporter::builder(),
108 OTEL_EXPORTER_OTLP_METRICS_PROTOCOL,
109 "Failed to build OTLP metric exporter",
110 |b| b.with_temporality(Temporality::default())
111 )
112}
113
114fn build_log_exporter() -> Result<opentelemetry_otlp::LogExporter> {
127 build_exporter!(
128 opentelemetry_otlp::LogExporter::builder(),
129 OTEL_EXPORTER_OTLP_LOGS_PROTOCOL,
130 "Failed to build OTLP log exporter"
131 )
132}
133
134pub fn init_tracer_provider(resource: &Resource, sample_ratio: f64) -> Result<SdkTracerProvider> {
161 global::set_text_map_propagator(TraceContextPropagator::new());
162
163 let builder = SdkTracerProvider::builder()
164 .with_sampler(Sampler::ParentBased(Box::new(Sampler::TraceIdRatioBased(
165 sample_ratio,
166 ))))
167 .with_id_generator(RandomIdGenerator::default())
168 .with_resource(resource.clone());
169
170 let tracer_provider = if exporter_enabled(OTEL_EXPORTER_OTLP_TRACES_ENDPOINT) {
171 let exporter = build_span_exporter()?;
172 builder.with_batch_exporter(exporter).build()
173 } else {
174 builder.build()
175 };
176
177 global::set_tracer_provider(tracer_provider.clone());
178
179 Ok(tracer_provider)
180}
181
182pub fn init_meter_provider(
209 resource: &Resource,
210 metrics_interval_secs: u64,
211) -> Result<SdkMeterProvider> {
212 let builder = MeterProviderBuilder::default().with_resource(resource.clone());
213
214 let meter_provider = if exporter_enabled(OTEL_EXPORTER_OTLP_METRICS_ENDPOINT) {
215 let exporter = build_metric_exporter()?;
216 let reader = PeriodicReader::builder(exporter)
217 .with_interval(Duration::from_secs(metrics_interval_secs))
218 .build();
219
220 builder.with_reader(reader).build()
221 } else {
222 builder.build()
223 };
224
225 global::set_meter_provider(meter_provider.clone());
226
227 Ok(meter_provider)
228}
229
230pub fn init_logger_provider(resource: &Resource) -> Result<SdkLoggerProvider> {
256 let builder = SdkLoggerProvider::builder().with_resource(resource.clone());
257
258 let logger_provider = if exporter_enabled(OTEL_EXPORTER_OTLP_LOGS_ENDPOINT) {
259 let exporter = build_log_exporter()?;
260 builder.with_batch_exporter(exporter).build()
261 } else {
262 builder.build()
263 };
264
265 Ok(logger_provider)
266}
267
268#[cfg(test)]
269mod tests {
270 use super::{
271 OTEL_EXPORTER_OTLP_ENDPOINT, OTEL_EXPORTER_OTLP_LOGS_ENDPOINT,
272 OTEL_EXPORTER_OTLP_METRICS_ENDPOINT, OTEL_EXPORTER_OTLP_TRACES_ENDPOINT, exporter_enabled,
273 };
274 use std::sync::Mutex;
275
276 static ENV_LOCK: Mutex<()> = Mutex::new(());
277
278 fn clear_endpoint_envs() {
279 unsafe {
280 std::env::remove_var(OTEL_EXPORTER_OTLP_ENDPOINT);
281 std::env::remove_var(OTEL_EXPORTER_OTLP_TRACES_ENDPOINT);
282 std::env::remove_var(OTEL_EXPORTER_OTLP_METRICS_ENDPOINT);
283 std::env::remove_var(OTEL_EXPORTER_OTLP_LOGS_ENDPOINT);
284 }
285 }
286
287 #[test]
288 fn exporter_is_disabled_without_global_or_signal_endpoint() {
289 let _guard = ENV_LOCK
290 .lock()
291 .expect("environment lock should not be poisoned");
292 clear_endpoint_envs();
293
294 assert!(!exporter_enabled(OTEL_EXPORTER_OTLP_TRACES_ENDPOINT));
295 assert!(!exporter_enabled(OTEL_EXPORTER_OTLP_METRICS_ENDPOINT));
296 assert!(!exporter_enabled(OTEL_EXPORTER_OTLP_LOGS_ENDPOINT));
297 }
298
299 #[test]
300 fn exporter_is_disabled_with_empty_endpoint() {
301 let _guard = ENV_LOCK
302 .lock()
303 .expect("environment lock should not be poisoned");
304 clear_endpoint_envs();
305 unsafe {
306 std::env::set_var(OTEL_EXPORTER_OTLP_ENDPOINT, " ");
307 std::env::set_var(OTEL_EXPORTER_OTLP_TRACES_ENDPOINT, "");
308 }
309
310 assert!(!exporter_enabled(OTEL_EXPORTER_OTLP_TRACES_ENDPOINT));
311
312 clear_endpoint_envs();
313 }
314
315 #[test]
316 fn exporter_is_enabled_with_global_endpoint() {
317 let _guard = ENV_LOCK
318 .lock()
319 .expect("environment lock should not be poisoned");
320 clear_endpoint_envs();
321 unsafe {
322 std::env::set_var(OTEL_EXPORTER_OTLP_ENDPOINT, "http://localhost:4317");
323 }
324
325 assert!(exporter_enabled(OTEL_EXPORTER_OTLP_TRACES_ENDPOINT));
326 assert!(exporter_enabled(OTEL_EXPORTER_OTLP_METRICS_ENDPOINT));
327 assert!(exporter_enabled(OTEL_EXPORTER_OTLP_LOGS_ENDPOINT));
328
329 clear_endpoint_envs();
330 }
331
332 #[test]
333 fn exporter_is_enabled_with_signal_endpoint() {
334 let _guard = ENV_LOCK
335 .lock()
336 .expect("environment lock should not be poisoned");
337 clear_endpoint_envs();
338 unsafe {
339 std::env::set_var(
340 OTEL_EXPORTER_OTLP_TRACES_ENDPOINT,
341 "http://localhost:4318/v1/traces",
342 );
343 }
344
345 assert!(exporter_enabled(OTEL_EXPORTER_OTLP_TRACES_ENDPOINT));
346 assert!(!exporter_enabled(OTEL_EXPORTER_OTLP_METRICS_ENDPOINT));
347 assert!(!exporter_enabled(OTEL_EXPORTER_OTLP_LOGS_ENDPOINT));
348
349 clear_endpoint_envs();
350 }
351}