use anyhow::{anyhow, Result}; use once_cell::sync::Lazy; use opentelemetry::{trace::TracerProvider as _, KeyValue}; use opentelemetry_sdk::{ metrics::{MeterProviderBuilder, PeriodicReader, SdkMeterProvider}, runtime, trace::{RandomIdGenerator, Sampler, TracerProvider}, Resource, }; use tracing_opentelemetry::{MetricsLayer, OpenTelemetryLayer}; use tracing_subscriber::layer::SubscriberExt; use tracing_subscriber::util::SubscriberInitExt; static RESOURCE: Lazy<Resource> = Lazy::new(|| { Resource::new(vec![ KeyValue::new( opentelemetry_semantic_conventions::resource::SERVICE_NAME, env!("CARGO_PKG_NAME"), ), KeyValue::new( opentelemetry_semantic_conventions::resource::SERVICE_VERSION, env!("CARGO_PKG_VERSION"), ), ]) }); // Construct MeterProvider for MetricsLayer fn init_meter_provider() -> Result<SdkMeterProvider> { let exporter = opentelemetry_otlp::MetricExporter::builder() .with_tonic() .with_temporality(opentelemetry_sdk::metrics::Temporality::default()) .build() .map_err(|e| anyhow!("Error creating OTLP metric exporter: {:?}", e))?; let meter_provider = MeterProviderBuilder::default() .with_resource(RESOURCE.clone()) .with_reader( PeriodicReader::builder(exporter, runtime::Tokio) .with_interval(std::time::Duration::from_secs(10)) .build(), ) .build(); opentelemetry::global::set_meter_provider(meter_provider.clone()); Ok(meter_provider) } // Construct TracerProvider for OpenTelemetryLayer fn init_tracer_provider() -> Result<TracerProvider> { let exporter = opentelemetry_otlp::SpanExporter::builder() .with_tonic() .build() .map_err(|e| anyhow!("Error creating OTLP span exporter: {:?}", e))?; let tracer_provider = opentelemetry_sdk::trace::TracerProvider::builder() .with_sampler(Sampler::ParentBased(Box::new(Sampler::TraceIdRatioBased( 1.0, )))) .with_resource(RESOURCE.clone()) .with_id_generator(RandomIdGenerator::default()) .with_batch_exporter(exporter, opentelemetry_sdk::runtime::Tokio) .build(); Ok(tracer_provider) } // Initialize tracing-subscriber and return OtelGuard for opentelemetry-related termination processing pub fn init_tracing_subscriber() -> Result<OtelGuard> { let meter_provider = init_meter_provider()?; let tracer_provider = init_tracer_provider()?; let tracer = tracer_provider.tracer("tracing-otel-subscriber"); tracing_subscriber::registry() .with(tracing_subscriber::EnvFilter::from_default_env()) .with(tracing_subscriber::fmt::layer()) .with(MetricsLayer::new(meter_provider.clone())) .with(OpenTelemetryLayer::new(tracer)) .init(); Ok(OtelGuard { meter_provider, tracer_provider, }) } pub struct OtelGuard { meter_provider: SdkMeterProvider, tracer_provider: TracerProvider, } impl Drop for OtelGuard { fn drop(&mut self) { if let Err(err) = self.tracer_provider.shutdown() { eprintln!("{err:?}"); } if let Err(err) = self.meter_provider.shutdown() { eprintln!("{err:?}"); } } }