1use crate::config::OtelConfig;
14use crate::error::Result;
15#[cfg(feature = "otel")]
16use crate::error::ForgeError;
17
18pub fn init_otel(config: &OtelConfig) -> Result<()> {
26 init_otel_inner(config)
27}
28
29#[cfg(feature = "otel")]
30fn init_otel_inner(config: &OtelConfig) -> Result<()> {
31 use opentelemetry::global;
32 use opentelemetry::KeyValue;
33 use opentelemetry_otlp::WithExportConfig;
34 use opentelemetry_sdk::propagation::TraceContextPropagator;
35 use opentelemetry_sdk::trace::Config as SdkConfig;
36 use opentelemetry_sdk::Resource;
37 use std::sync::OnceLock;
38
39 static INIT: OnceLock<()> = OnceLock::new();
40 if INIT.get().is_some() {
41 return Ok(());
42 }
43
44 let mut resource_kvs: Vec<KeyValue> = Vec::new();
47 if let Some(name) = &config.service_name {
48 resource_kvs.push(KeyValue::new("service.name", name.clone()));
49 }
50 for (k, v) in &config.resource_attributes {
51 resource_kvs.push(KeyValue::new(k.clone(), v.clone()));
52 }
53 let resource = Resource::new(resource_kvs);
54
55 let mut exporter_builder = opentelemetry_otlp::new_exporter()
58 .http()
59 .with_protocol(opentelemetry_otlp::Protocol::HttpBinary);
60 if let Some(endpoint) = &config.endpoint {
61 exporter_builder = exporter_builder.with_endpoint(endpoint.clone());
62 }
63 if !config.headers.is_empty() {
64 exporter_builder = exporter_builder.with_headers(config.headers.clone());
65 }
66
67 let tracer_provider = opentelemetry_otlp::new_pipeline()
68 .tracing()
69 .with_exporter(exporter_builder)
70 .with_trace_config(SdkConfig::default().with_resource(resource))
71 .install_batch(opentelemetry_sdk::runtime::Tokio)
72 .map_err(|e| ForgeError::config(format!("failed to install OTLP pipeline: {}", e)))?;
73
74 global::set_text_map_propagator(TraceContextPropagator::new());
75 global::set_tracer_provider(tracer_provider);
76 let _ = INIT.set(());
77 Ok(())
78}
79
80#[cfg(not(feature = "otel"))]
81#[inline]
82fn init_otel_inner(_config: &OtelConfig) -> Result<()> {
83 Ok(())
86}
87
88pub const fn otel_feature_enabled() -> bool {
92 cfg!(feature = "otel")
93}