pub mod attributes;
pub mod context;
pub mod sampler;
use opentelemetry::KeyValue;
use opentelemetry::global;
use opentelemetry_otlp::WithExportConfig;
use opentelemetry_sdk::propagation::TraceContextPropagator;
use opentelemetry_sdk::trace::{Config, TracerProvider};
use std::collections::HashMap;
pub struct TracingGuard {
_provider: TracerProvider,
}
impl Drop for TracingGuard {
fn drop(&mut self) {
global::shutdown_tracer_provider();
}
}
pub async fn init(exporter: &str, endpoint: &str) -> Result<TracingGuard, String> {
let provider: TracerProvider = match exporter.to_lowercase().as_str() {
"otlp" => init_otlp(endpoint).await?,
"stdout" => init_stdout()?,
_ => init_stdout()?,
};
global::set_tracer_provider(provider.clone());
let propagator = TraceContextPropagator::default();
global::set_text_map_propagator(propagator);
Ok(TracingGuard { _provider: provider })
}
pub async fn init_with_sampling(exporter: &str, endpoint: &str, sampling_rate: f64) -> Result<TracingGuard, String> {
let provider: TracerProvider = match exporter.to_lowercase().as_str() {
"otlp" => init_otlp(endpoint).await?,
"stdout" => init_stdout()?,
_ => init_stdout()?,
};
let sampled_provider = crate::tracing::sampler::create_trace_provider_with_sampling(sampling_rate)
.map_err(|e| format!("Failed to create sampler: {}", e))?;
global::set_tracer_provider(sampled_provider);
let propagator = TraceContextPropagator::default();
global::set_text_map_propagator(propagator);
Ok(TracingGuard { _provider: provider })
}
async fn init_otlp(endpoint: &str) -> Result<TracerProvider, String> {
let resource = opentelemetry_sdk::Resource::new(vec![KeyValue::new("service.name", "dbnexus")]);
let config = Config::default().with_resource(resource);
let provider = opentelemetry_otlp::new_pipeline()
.tracing()
.with_exporter(opentelemetry_otlp::new_exporter().tonic().with_endpoint(endpoint))
.with_trace_config(config)
.install_simple()
.map_err(|e| e.to_string())?;
Ok(provider)
}
fn init_stdout() -> Result<TracerProvider, String> {
let resource = opentelemetry_sdk::Resource::new(vec![KeyValue::new("service.name", "dbnexus")]);
let config = Config::default().with_resource(resource);
let provider = opentelemetry_otlp::new_pipeline()
.tracing()
.with_exporter(opentelemetry_otlp::new_exporter().tonic().with_endpoint("stdout"))
.with_trace_config(config)
.install_simple()
.map_err(|e| e.to_string())?;
Ok(provider)
}
pub fn inject(headers: &mut HashMap<String, String>) {
global::get_text_map_propagator(|propagator| {
propagator.inject(headers);
});
}
pub fn extract(headers: &HashMap<String, String>) {
global::get_text_map_propagator(|propagator| {
let _ = propagator.extract(headers);
});
}
#[cfg(test)]
mod tests {
use super::*;
#[tokio::test]
async fn test_tracing_init_and_propagation() {
let mut headers = HashMap::new();
headers.insert("x-test".to_string(), "1".to_string());
let guard = init("stdout", "unused").await.expect("init stdout");
inject(&mut headers);
extract(&headers);
drop(guard);
let guard = init("unknown", "unused").await.expect("init fallback");
inject(&mut headers);
extract(&headers);
drop(guard);
let guard = init("otlp", "http://localhost:4317").await.expect("init otlp");
inject(&mut headers);
extract(&headers);
drop(guard);
}
}