use anyhow::{anyhow, Result}; use once_cell::sync::Lazy; use opentelemetry::{trace::TracerProvider as _, KeyValue}; use opentelemetry_otlp::{WithExportConfig, WithTonicConfig}; use opentelemetry_sdk::{ metrics::{MeterProviderBuilder, PeriodicReader, SdkMeterProvider}, propagation::TraceContextPropagator, 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(otel_endpoint: &String) -> Result<SdkMeterProvider> { let exporter = opentelemetry_otlp::MetricExporter::builder() .with_tonic() .with_endpoint(otel_endpoint) .with_tls_config(tonic::transport::ClientTlsConfig::new().with_native_roots()) .with_compression(opentelemetry_otlp::Compression::Gzip) .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(otel_endpoint: &String) -> Result<TracerProvider> { let exporter = opentelemetry_otlp::SpanExporter::builder() .with_tonic() .with_tls_config(tonic::transport::ClientTlsConfig::new().with_native_roots()) .with_compression(opentelemetry_otlp::Compression::Gzip) .with_endpoint(otel_endpoint) .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 TracingGuard for opentelemetry-related termination processing pub fn init_tracing(otel_endpoint: Option<&String>) -> Result<TracingGuard> { let env_filter = tracing_subscriber::EnvFilter::try_from_default_env() .unwrap_or_else(|_| tracing_subscriber::EnvFilter::new("")); let fmt_layer = tracing_subscriber::fmt::layer().with_writer(std::io::stderr); let meter_provider = match otel_endpoint { Some(endpoint) => Some(init_meter_provider(endpoint)?), None => None, }; let metrics_layer = match meter_provider { Some(ref p) => Some(MetricsLayer::new(p.to_owned())), None => None, }; let tracer_provider = match otel_endpoint { Some(endpoint) => Some(init_tracer_provider(endpoint)?), None => None, }; let trace_layer = match tracer_provider { Some(ref p) => Some(OpenTelemetryLayer::new(p.tracer("tracing-otel-subscriber"))), None => None, }; opentelemetry::global::set_text_map_propagator(TraceContextPropagator::new()); tracing_subscriber::registry() .with(env_filter) .with(fmt_layer) .with(metrics_layer) .with(trace_layer) .init(); Ok(TracingGuard { meter_provider, tracer_provider, }) } pub struct TracingGuard { meter_provider: Option<SdkMeterProvider>, tracer_provider: Option<TracerProvider>, } impl Drop for TracingGuard { fn drop(&mut self) { if let Some(tracer_provider) = &self.tracer_provider { for result in tracer_provider.force_flush() { if let Err(err) = result { eprintln!("{err:?}"); } } if let Err(err) = tracer_provider.shutdown() { eprintln!("{err:?}"); } } if let Some(meter_provider) = &self.meter_provider { if let Err(err) = meter_provider.force_flush() { eprintln!("{err:?}"); } if let Err(err) = meter_provider.shutdown() { eprintln!("{err:?}"); } } } }