diff --git a/README.md b/README.md index 7c0df1434e..fb54e62066 100644 --- a/README.md +++ b/README.md @@ -24,6 +24,10 @@ observability tools. ## Project Status +The table below summarizes the overall status of each component. Some components +include unstable features, which are documented in their respective crate +documentation. + | Signal/Component | Overall Status | | -------------------- | ------------------ | | Logs-API | RC* | @@ -31,8 +35,8 @@ observability tools. | Logs-OTLP Exporter | Beta | | Logs-Appender-Tracing | Beta | | Metrics-API | RC | -| Metrics-SDK | Beta | -| Metrics-OTLP Exporter | Beta | +| Metrics-SDK | Beta | +| Metrics-OTLP Exporter | Beta | | Traces-API | Beta | | Traces-SDK | Beta | | Traces-OTLP Exporter | Beta | diff --git a/examples/self-diagnostics/src/main.rs b/examples/self-diagnostics/src/main.rs index 02ea356b0d..2c38458d84 100644 --- a/examples/self-diagnostics/src/main.rs +++ b/examples/self-diagnostics/src/main.rs @@ -1,6 +1,7 @@ use opentelemetry::global; use opentelemetry::KeyValue; use opentelemetry_sdk::metrics::PeriodicReader; +use opentelemetry_sdk::Resource; use std::error::Error; use tracing::info; use tracing_subscriber::fmt; @@ -13,6 +14,10 @@ fn init_meter_provider() -> opentelemetry_sdk::metrics::SdkMeterProvider { let reader = PeriodicReader::builder(exporter, opentelemetry_sdk::runtime::Tokio).build(); let provider = opentelemetry_sdk::metrics::SdkMeterProvider::builder() + .with_resource(Resource::new([KeyValue::new( + "service.name", + "self-diagnostics-example", + )])) .with_reader(reader) .build(); @@ -26,7 +31,7 @@ async fn main() -> Result<(), Box> { // OpenTelemetry uses `tracing` crate for its internal logging. Unless a // tracing subscriber is set, the logs will be discarded. In this example, // we configure a `tracing` subscriber to: - // 1. Print logs of level INFO or higher to stdout using tracing's fmt layer. + // 1. Print logs of level DEBUG or higher to stdout using tracing's fmt layer. // 2. Filter logs from OpenTelemetry's dependencies (like tonic, hyper, // reqwest etc. which are commonly used by the OTLP exporter) to only print // ERROR-level logs. This filtering helps reduce repetitive log messages @@ -39,7 +44,7 @@ async fn main() -> Result<(), Box> { // Hence, one may use "add_directive("opentelemetry=off".parse().unwrap())" // to turn off all logs from OpenTelemetry. - let filter = EnvFilter::new("info") + let filter = EnvFilter::new("debug") .add_directive("hyper=error".parse().unwrap()) .add_directive("tonic=error".parse().unwrap()) .add_directive("h2=error".parse().unwrap()) @@ -54,11 +59,14 @@ async fn main() -> Result<(), Box> { info!("Starting self-diagnostics example"); let meter = global::meter("example"); - // Create a counter using an invalid name to trigger - // internal log about the same. - let counter = meter.u64_counter("my_counter with_space").build(); + let counter = meter.u64_counter("my_counter").build(); counter.add(10, &[KeyValue::new("key", "value")]); + let _observable_counter = meter + .u64_observable_counter("my_observable_counter") + .with_callback(|observer| observer.observe(10, &[KeyValue::new("key", "value")])) + .build(); + meter_provider.shutdown()?; info!("Shutdown complete. Bye!"); Ok(()) diff --git a/opentelemetry-sdk/CHANGELOG.md b/opentelemetry-sdk/CHANGELOG.md index 9b32d9579f..9c298a2a03 100644 --- a/opentelemetry-sdk/CHANGELOG.md +++ b/opentelemetry-sdk/CHANGELOG.md @@ -25,6 +25,12 @@ - Bug fix: Empty Logger names are retained as-is instead of replacing with "rust.opentelemetry.io/sdk/logger" [#2316](https://github.com/open-telemetry/opentelemetry-rust/pull/2316) + + - `Logger::provider`: This method is deprecated as of version `0.27.1`. To be removed in `0.28.0`. + - `Logger::instrumentation_scope`: This method is deprecated as of version `0.27.1`. To be removed in `0.28.0` + Migration Guidance: + - These methods are intended for log appenders. Keep the clone of the provider handle, instead of depending on above methods. + ## 0.27.0 diff --git a/opentelemetry-sdk/Cargo.toml b/opentelemetry-sdk/Cargo.toml index bf03c8d34a..39b5fddfa2 100644 --- a/opentelemetry-sdk/Cargo.toml +++ b/opentelemetry-sdk/Cargo.toml @@ -17,7 +17,6 @@ async-trait = { workspace = true, optional = true } futures-channel = "0.3" futures-executor = { workspace = true } futures-util = { workspace = true, features = ["std", "sink", "async-await-macro"] } -once_cell = { workspace = true } percent-encoding = { version = "2.0", optional = true } rand = { workspace = true, features = ["std", "std_rng","small_rng"], optional = true } glob = { version = "0.3.1", optional =true} diff --git a/opentelemetry-sdk/src/logs/log_emitter.rs b/opentelemetry-sdk/src/logs/log_emitter.rs index 1ebe76de9a..a39152dbfd 100644 --- a/opentelemetry-sdk/src/logs/log_emitter.rs +++ b/opentelemetry-sdk/src/logs/log_emitter.rs @@ -11,20 +11,24 @@ use std::{ borrow::Cow, sync::{ atomic::{AtomicBool, Ordering}, - Arc, + Arc, OnceLock, }, }; -use once_cell::sync::Lazy; - // a no nop logger provider used as placeholder when the provider is shutdown -static NOOP_LOGGER_PROVIDER: Lazy = Lazy::new(|| LoggerProvider { - inner: Arc::new(LoggerProviderInner { - processors: Vec::new(), - resource: Resource::empty(), - is_shutdown: AtomicBool::new(true), - }), -}); +// TODO - replace it with LazyLock once it is stable +static NOOP_LOGGER_PROVIDER: OnceLock = OnceLock::new(); + +#[inline] +fn noop_logger_provider() -> &'static LoggerProvider { + NOOP_LOGGER_PROVIDER.get_or_init(|| LoggerProvider { + inner: Arc::new(LoggerProviderInner { + processors: Vec::new(), + resource: Resource::empty(), + is_shutdown: AtomicBool::new(true), + }), + }) +} #[derive(Debug, Clone)] /// Handles the creation and coordination of [`Logger`]s. @@ -55,7 +59,7 @@ impl opentelemetry::logs::LoggerProvider for LoggerProvider { fn logger_with_scope(&self, scope: InstrumentationScope) -> Self::Logger { // If the provider is shutdown, new logger will refer a no-op logger provider. if self.inner.is_shutdown.load(Ordering::Relaxed) { - return Logger::new(scope, NOOP_LOGGER_PROVIDER.clone()); + return Logger::new(scope, noop_logger_provider().clone()); } if scope.name().is_empty() { otel_info!(name: "LoggerNameEmpty", message = "Logger name is empty; consider providing a meaningful name. Logger will function normally and the provided name will be used as-is."); @@ -238,11 +242,19 @@ impl Logger { Logger { scope, provider } } + #[deprecated( + since = "0.27.1", + note = "This method was intended for appender developers, but has no defined use-case in typical workflows. It is deprecated and will be removed in the next major release." + )] /// LoggerProvider associated with this logger. pub fn provider(&self) -> &LoggerProvider { &self.provider } + #[deprecated( + since = "0.27.1", + note = "This method was intended for appender developers, but has no defined use-case in typical workflows. It is deprecated and will be removed in the next major release." + )] /// Instrumentation scope of this logger. pub fn instrumentation_scope(&self) -> &InstrumentationScope { &self.scope @@ -258,7 +270,7 @@ impl opentelemetry::logs::Logger for Logger { /// Emit a `LogRecord`. fn emit(&self, mut record: Self::LogRecord) { - let provider = self.provider(); + let provider = &self.provider; let processors = provider.log_processors(); //let mut log_record = record; @@ -277,22 +289,17 @@ impl opentelemetry::logs::Logger for Logger { } for p in processors { - p.emit(&mut record, self.instrumentation_scope()); + p.emit(&mut record, &self.scope); } } #[cfg(feature = "spec_unstable_logs_enabled")] fn event_enabled(&self, level: Severity, target: &str) -> bool { - let provider = self.provider(); + let provider = &self.provider; let mut enabled = false; for processor in provider.log_processors() { - enabled = enabled - || processor.event_enabled( - level, - target, - self.instrumentation_scope().name().as_ref(), - ); + enabled = enabled || processor.event_enabled(level, target, self.scope.name().as_ref()); } enabled } @@ -725,14 +732,14 @@ mod tests { emitted_logs[0].clone().record.body, Some(AnyValue::String("Testing empty logger name".into())) ); - assert_eq!(logger.instrumentation_scope().name(), ""); + assert_eq!(logger.scope.name(), ""); // Assert the second log created through the scope assert_eq!( emitted_logs[1].clone().record.body, Some(AnyValue::String("Testing empty logger scope name".into())) ); - assert_eq!(scoped_logger.instrumentation_scope().name(), ""); + assert_eq!(scoped_logger.scope.name(), ""); } #[derive(Debug)] diff --git a/opentelemetry-sdk/src/logs/mod.rs b/opentelemetry-sdk/src/logs/mod.rs index f4f55cd390..d1212bbdeb 100644 --- a/opentelemetry-sdk/src/logs/mod.rs +++ b/opentelemetry-sdk/src/logs/mod.rs @@ -108,6 +108,7 @@ mod tests { } #[test] + #[allow(deprecated)] fn logger_attributes() { let provider = LoggerProvider::builder().build(); let scope = InstrumentationScope::builder("test_logger") diff --git a/opentelemetry-sdk/src/metrics/internal/aggregate.rs b/opentelemetry-sdk/src/metrics/internal/aggregate.rs index 9bc95b43b5..cd99979923 100644 --- a/opentelemetry-sdk/src/metrics/internal/aggregate.rs +++ b/opentelemetry-sdk/src/metrics/internal/aggregate.rs @@ -12,11 +12,11 @@ use super::{ precomputed_sum::PrecomputedSum, sum::Sum, Number, }; -const STREAM_CARDINALITY_LIMIT: u32 = 2000; +pub(crate) const STREAM_CARDINALITY_LIMIT: usize = 2000; /// Checks whether aggregator has hit cardinality limit for metric streams pub(crate) fn is_under_cardinality_limit(size: usize) -> bool { - size < STREAM_CARDINALITY_LIMIT as usize + size < STREAM_CARDINALITY_LIMIT } /// Receives measurements to be aggregated. diff --git a/opentelemetry-sdk/src/metrics/internal/exponential_histogram.rs b/opentelemetry-sdk/src/metrics/internal/exponential_histogram.rs index c0d4263946..7975c1419e 100644 --- a/opentelemetry-sdk/src/metrics/internal/exponential_histogram.rs +++ b/opentelemetry-sdk/src/metrics/internal/exponential_histogram.rs @@ -1,7 +1,7 @@ use std::{f64::consts::LOG2_E, mem::replace, ops::DerefMut, sync::Mutex, time::SystemTime}; -use once_cell::sync::Lazy; use opentelemetry::{otel_debug, KeyValue}; +use std::sync::OnceLock; use crate::metrics::{ data::{self, Aggregation}, @@ -131,7 +131,7 @@ impl ExpoHistogramDataPoint { } return (exp - correction) >> -self.scale; } - (exp << self.scale) + (frac.ln() * SCALE_FACTORS[self.scale as usize]) as i32 - 1 + (exp << self.scale) + (frac.ln() * scale_factors()[self.scale as usize]) as i32 - 1 } } @@ -165,32 +165,38 @@ fn scale_change(max_size: i32, bin: i32, start_bin: i32, length: i32) -> u32 { count } -/// Constants used in calculating the logarithm index. -static SCALE_FACTORS: Lazy<[f64; 21]> = Lazy::new(|| { - [ - LOG2_E * 2f64.powi(0), - LOG2_E * 2f64.powi(1), - LOG2_E * 2f64.powi(2), - LOG2_E * 2f64.powi(3), - LOG2_E * 2f64.powi(4), - LOG2_E * 2f64.powi(5), - LOG2_E * 2f64.powi(6), - LOG2_E * 2f64.powi(7), - LOG2_E * 2f64.powi(8), - LOG2_E * 2f64.powi(9), - LOG2_E * 2f64.powi(10), - LOG2_E * 2f64.powi(11), - LOG2_E * 2f64.powi(12), - LOG2_E * 2f64.powi(13), - LOG2_E * 2f64.powi(14), - LOG2_E * 2f64.powi(15), - LOG2_E * 2f64.powi(16), - LOG2_E * 2f64.powi(17), - LOG2_E * 2f64.powi(18), - LOG2_E * 2f64.powi(19), - LOG2_E * 2f64.powi(20), - ] -}); +// TODO - replace it with LazyLock once it is stable +static SCALE_FACTORS: OnceLock<[f64; 21]> = OnceLock::new(); + +/// returns constants used in calculating the logarithm index. +#[inline] +fn scale_factors() -> &'static [f64; 21] { + SCALE_FACTORS.get_or_init(|| { + [ + LOG2_E * 2f64.powi(0), + LOG2_E * 2f64.powi(1), + LOG2_E * 2f64.powi(2), + LOG2_E * 2f64.powi(3), + LOG2_E * 2f64.powi(4), + LOG2_E * 2f64.powi(5), + LOG2_E * 2f64.powi(6), + LOG2_E * 2f64.powi(7), + LOG2_E * 2f64.powi(8), + LOG2_E * 2f64.powi(9), + LOG2_E * 2f64.powi(10), + LOG2_E * 2f64.powi(11), + LOG2_E * 2f64.powi(12), + LOG2_E * 2f64.powi(13), + LOG2_E * 2f64.powi(14), + LOG2_E * 2f64.powi(15), + LOG2_E * 2f64.powi(16), + LOG2_E * 2f64.powi(17), + LOG2_E * 2f64.powi(18), + LOG2_E * 2f64.powi(19), + LOG2_E * 2f64.powi(20), + ] + }) +} /// Breaks the number into a normalized fraction and a base-2 exponent. /// diff --git a/opentelemetry-sdk/src/metrics/internal/mod.rs b/opentelemetry-sdk/src/metrics/internal/mod.rs index 4eaea7972c..1b5a6a4de5 100644 --- a/opentelemetry-sdk/src/metrics/internal/mod.rs +++ b/opentelemetry-sdk/src/metrics/internal/mod.rs @@ -7,19 +7,23 @@ mod sum; use core::fmt; use std::collections::{HashMap, HashSet}; -use std::mem::take; +use std::mem::swap; use std::ops::{Add, AddAssign, DerefMut, Sub}; use std::sync::atomic::{AtomicBool, AtomicI64, AtomicU64, AtomicUsize, Ordering}; -use std::sync::{Arc, RwLock}; +use std::sync::{Arc, OnceLock, RwLock}; -use aggregate::is_under_cardinality_limit; +use aggregate::{is_under_cardinality_limit, STREAM_CARDINALITY_LIMIT}; pub(crate) use aggregate::{AggregateBuilder, ComputeAggregation, Measure}; pub(crate) use exponential_histogram::{EXPO_MAX_SCALE, EXPO_MIN_SCALE}; -use once_cell::sync::Lazy; use opentelemetry::{otel_warn, KeyValue}; -pub(crate) static STREAM_OVERFLOW_ATTRIBUTES: Lazy> = - Lazy::new(|| vec![KeyValue::new("otel.metric.overflow", "true")]); +// TODO Replace it with LazyLock once it is stable +pub(crate) static STREAM_OVERFLOW_ATTRIBUTES: OnceLock> = OnceLock::new(); + +#[inline] +fn stream_overflow_attributes() -> &'static Vec { + STREAM_OVERFLOW_ATTRIBUTES.get_or_init(|| vec![KeyValue::new("otel.metric.overflow", "true")]) +} pub(crate) trait Aggregator { /// A static configuration that is needed in order to initialize aggregator. @@ -51,6 +55,12 @@ where { /// Trackers store the values associated with different attribute sets. trackers: RwLock, Arc>>, + + /// Used ONLY by Delta collect. The data type must match the one used in + /// `trackers` to allow mem::swap. Wrapping the type in `OnceLock` to + /// avoid this allocation for Cumulative aggregation. + trackers_for_collect: OnceLock, Arc>>>, + /// Number of different attribute set stored in the `trackers` map. count: AtomicUsize, /// Indicates whether a value with no attributes has been stored. @@ -67,7 +77,8 @@ where { fn new(config: A::InitConfig) -> Self { ValueMap { - trackers: RwLock::new(HashMap::new()), + trackers: RwLock::new(HashMap::with_capacity(1 + STREAM_CARDINALITY_LIMIT)), + trackers_for_collect: OnceLock::new(), has_no_attribute_value: AtomicBool::new(false), no_attribute_tracker: A::create(&config), count: AtomicUsize::new(0), @@ -75,6 +86,12 @@ where } } + #[inline] + fn trackers_for_collect(&self) -> &RwLock, Arc>> { + self.trackers_for_collect + .get_or_init(|| RwLock::new(HashMap::with_capacity(1 + STREAM_CARDINALITY_LIMIT))) + } + fn measure(&self, value: A::PreComputedValue, attributes: &[KeyValue]) { if attributes.is_empty() { self.no_attribute_tracker.update(value); @@ -121,12 +138,12 @@ where trackers.insert(sorted_attrs, new_tracker); self.count.fetch_add(1, Ordering::SeqCst); - } else if let Some(overflow_value) = trackers.get(STREAM_OVERFLOW_ATTRIBUTES.as_slice()) { + } else if let Some(overflow_value) = trackers.get(stream_overflow_attributes().as_slice()) { overflow_value.update(value); } else { let new_tracker = A::create(&self.config); new_tracker.update(value); - trackers.insert(STREAM_OVERFLOW_ATTRIBUTES.clone(), Arc::new(new_tracker)); + trackers.insert(stream_overflow_attributes().clone(), Arc::new(new_tracker)); otel_warn!( name: "ValueMap.measure", message = "Maximum data points for metric stream exceeded. Entry added to overflow. Subsequent overflows to same metric until next collect will not be logged." ); @@ -170,19 +187,23 @@ where )); } - let trackers = match self.trackers.write() { - Ok(mut trackers) => { + if let Ok(mut trackers_collect) = self.trackers_for_collect().write() { + if let Ok(mut trackers_current) = self.trackers.write() { + swap(trackers_collect.deref_mut(), trackers_current.deref_mut()); self.count.store(0, Ordering::SeqCst); - take(trackers.deref_mut()) + } else { + otel_warn!(name: "MeterProvider.InternalError", message = "Metric collection failed. Report this issue in OpenTelemetry repo.", details ="ValueMap trackers lock poisoned"); + return; } - Err(_) => todo!(), - }; - let mut seen = HashSet::new(); - for (attrs, tracker) in trackers.into_iter() { - if seen.insert(Arc::as_ptr(&tracker)) { - dest.push(map_fn(attrs, tracker.clone_and_reset(&self.config))); + let mut seen = HashSet::new(); + for (attrs, tracker) in trackers_collect.drain() { + if seen.insert(Arc::as_ptr(&tracker)) { + dest.push(map_fn(attrs, tracker.clone_and_reset(&self.config))); + } } + } else { + otel_warn!(name: "MeterProvider.InternalError", message = "Metric collection failed. Report this issue in OpenTelemetry repo.", details ="ValueMap trackers for collect lock poisoned"); } } } diff --git a/opentelemetry-sdk/src/metrics/meter_provider.rs b/opentelemetry-sdk/src/metrics/meter_provider.rs index 032ea747e6..b57e614959 100644 --- a/opentelemetry-sdk/src/metrics/meter_provider.rs +++ b/opentelemetry-sdk/src/metrics/meter_provider.rs @@ -174,6 +174,10 @@ impl MeterProvider for SdkMeterProvider { fn meter_with_scope(&self, scope: InstrumentationScope) -> Meter { if self.inner.is_shutdown.load(Ordering::Relaxed) { + otel_debug!( + name: "MeterProvider.NoOpMeterReturned", + meter_name = scope.name(), + ); return Meter::new(Arc::new(NoopMeter::new())); } @@ -182,14 +186,26 @@ impl MeterProvider for SdkMeterProvider { }; if let Ok(mut meters) = self.inner.meters.lock() { - let meter = meters - .entry(scope) - .or_insert_with_key(|scope| { - Arc::new(SdkMeter::new(scope.clone(), self.inner.pipes.clone())) - }) - .clone(); - Meter::new(meter) + if let Some(existing_meter) = meters.get(&scope) { + otel_debug!( + name: "MeterProvider.ExistingMeterReturned", + meter_name = scope.name(), + ); + Meter::new(existing_meter.clone()) + } else { + let new_meter = Arc::new(SdkMeter::new(scope.clone(), self.inner.pipes.clone())); + meters.insert(scope.clone(), new_meter.clone()); + otel_debug!( + name: "MeterProvider.NewMeterCreated", + meter_name = scope.name(), + ); + Meter::new(new_meter) + } } else { + otel_debug!( + name: "MeterProvider.NoOpMeterReturned", + meter_name = scope.name(), + ); Meter::new(Arc::new(NoopMeter::new())) } } @@ -242,6 +258,11 @@ impl MeterProviderBuilder { /// Construct a new [MeterProvider] with this configuration. pub fn build(self) -> SdkMeterProvider { + otel_debug!( + name: "MeterProvider.Building", + builder = format!("{:?}", &self), + ); + let meter_provider = SdkMeterProvider { inner: Arc::new(SdkMeterProviderInner { pipes: Arc::new(Pipelines::new( diff --git a/opentelemetry-sdk/src/metrics/periodic_reader.rs b/opentelemetry-sdk/src/metrics/periodic_reader.rs index c0a40b5f8c..f8dffdcbd0 100644 --- a/opentelemetry-sdk/src/metrics/periodic_reader.rs +++ b/opentelemetry-sdk/src/metrics/periodic_reader.rs @@ -132,6 +132,7 @@ where name: "PeriodicReader.BuildCompleted", message = "Periodic reader built.", interval_in_secs = self.interval.as_secs(), + temporality = format!("{:?}", self.exporter.temporality()), ); PeriodicReader { diff --git a/opentelemetry-sdk/src/metrics/pipeline.rs b/opentelemetry-sdk/src/metrics/pipeline.rs index 8da41f38fb..add9ba16dc 100644 --- a/opentelemetry-sdk/src/metrics/pipeline.rs +++ b/opentelemetry-sdk/src/metrics/pipeline.rs @@ -72,6 +72,10 @@ impl Pipeline { /// unique values. fn add_sync(&self, scope: InstrumentationScope, i_sync: InstrumentSync) { let _ = self.inner.lock().map(|mut inner| { + otel_debug!( + name : "InstrumentCreated", + instrument_name = i_sync.name.as_ref(), + ); inner.aggregations.entry(scope).or_default().push(i_sync); }); } @@ -99,6 +103,10 @@ impl SdkProducer for Pipeline { /// Returns aggregated metrics from a single collection. fn produce(&self, rm: &mut ResourceMetrics) -> MetricResult<()> { let inner = self.inner.lock()?; + otel_debug!( + name: "MeterProviderInvokingObservableCallbacks", + count = inner.callbacks.len(), + ); for cb in &inner.callbacks { // TODO consider parallel callbacks. cb(); diff --git a/opentelemetry-sdk/src/propagation/baggage.rs b/opentelemetry-sdk/src/propagation/baggage.rs index 35567b4b4b..05d93e632f 100644 --- a/opentelemetry-sdk/src/propagation/baggage.rs +++ b/opentelemetry-sdk/src/propagation/baggage.rs @@ -1,4 +1,3 @@ -use once_cell::sync::Lazy; use opentelemetry::{ baggage::{BaggageExt, KeyValueMetadata}, otel_warn, @@ -7,10 +6,17 @@ use opentelemetry::{ }; use percent_encoding::{percent_decode_str, utf8_percent_encode, AsciiSet, CONTROLS}; use std::iter; +use std::sync::OnceLock; static BAGGAGE_HEADER: &str = "baggage"; const FRAGMENT: &AsciiSet = &CONTROLS.add(b' ').add(b'"').add(b';').add(b',').add(b'='); -static BAGGAGE_FIELDS: Lazy<[String; 1]> = Lazy::new(|| [BAGGAGE_HEADER.to_owned()]); + +// TODO Replace this with LazyLock once it is stable. +static BAGGAGE_FIELDS: OnceLock<[String; 1]> = OnceLock::new(); +#[inline] +fn baggage_fields() -> &'static [String; 1] { + BAGGAGE_FIELDS.get_or_init(|| [BAGGAGE_HEADER.to_owned()]) +} /// Propagates name-value pairs in [W3C Baggage] format. /// @@ -149,7 +155,7 @@ impl TextMapPropagator for BaggagePropagator { } fn fields(&self) -> FieldIter<'_> { - FieldIter::new(BAGGAGE_FIELDS.as_ref()) + FieldIter::new(baggage_fields()) } } diff --git a/opentelemetry-sdk/src/propagation/trace_context.rs b/opentelemetry-sdk/src/propagation/trace_context.rs index ced269c2d2..2a6d53ef04 100644 --- a/opentelemetry-sdk/src/propagation/trace_context.rs +++ b/opentelemetry-sdk/src/propagation/trace_context.rs @@ -1,21 +1,26 @@ //! # W3C Trace Context Propagator //! -use once_cell::sync::Lazy; use opentelemetry::{ propagation::{text_map_propagator::FieldIter, Extractor, Injector, TextMapPropagator}, trace::{SpanContext, SpanId, TraceContextExt, TraceFlags, TraceId, TraceState}, Context, }; use std::str::FromStr; +use std::sync::OnceLock; const SUPPORTED_VERSION: u8 = 0; const MAX_VERSION: u8 = 254; const TRACEPARENT_HEADER: &str = "traceparent"; const TRACESTATE_HEADER: &str = "tracestate"; -static TRACE_CONTEXT_HEADER_FIELDS: Lazy<[String; 2]> = - Lazy::new(|| [TRACEPARENT_HEADER.to_owned(), TRACESTATE_HEADER.to_owned()]); +// TODO Replace this with LazyLock once it is stable. +static TRACE_CONTEXT_HEADER_FIELDS: OnceLock<[String; 2]> = OnceLock::new(); + +fn trace_context_header_fields() -> &'static [String; 2] { + TRACE_CONTEXT_HEADER_FIELDS + .get_or_init(|| [TRACEPARENT_HEADER.to_owned(), TRACESTATE_HEADER.to_owned()]) +} /// Propagates `SpanContext`s in [W3C TraceContext] format under `traceparent` and `tracestate` header. /// @@ -146,7 +151,7 @@ impl TextMapPropagator for TraceContextPropagator { } fn fields(&self) -> FieldIter<'_> { - FieldIter::new(TRACE_CONTEXT_HEADER_FIELDS.as_ref()) + FieldIter::new(trace_context_header_fields()) } } diff --git a/opentelemetry-sdk/src/trace/provider.rs b/opentelemetry-sdk/src/trace/provider.rs index dcb8f55fd8..f8c65d59c9 100644 --- a/opentelemetry-sdk/src/trace/provider.rs +++ b/opentelemetry-sdk/src/trace/provider.rs @@ -68,32 +68,38 @@ use crate::trace::{ }; use crate::Resource; use crate::{export::trace::SpanExporter, trace::SpanProcessor}; -use once_cell::sync::{Lazy, OnceCell}; use opentelemetry::trace::TraceError; use opentelemetry::InstrumentationScope; use opentelemetry::{otel_debug, trace::TraceResult}; use std::borrow::Cow; use std::sync::atomic::{AtomicBool, Ordering}; -use std::sync::Arc; +use std::sync::{Arc, OnceLock}; use super::IdGenerator; -static PROVIDER_RESOURCE: OnceCell = OnceCell::new(); +static PROVIDER_RESOURCE: OnceLock = OnceLock::new(); // a no nop tracer provider used as placeholder when the provider is shutdown -static NOOP_TRACER_PROVIDER: Lazy = Lazy::new(|| TracerProvider { - inner: Arc::new(TracerProviderInner { - processors: Vec::new(), - config: Config { - // cannot use default here as the default resource is not empty - sampler: Box::new(Sampler::ParentBased(Box::new(Sampler::AlwaysOn))), - id_generator: Box::::default(), - span_limits: SpanLimits::default(), - resource: Cow::Owned(Resource::empty()), - }, - is_shutdown: AtomicBool::new(true), - }), -}); +// TODO Replace with LazyLock once it is stable +static NOOP_TRACER_PROVIDER: OnceLock = OnceLock::new(); +#[inline] +fn noop_tracer_provider() -> &'static TracerProvider { + NOOP_TRACER_PROVIDER.get_or_init(|| { + TracerProvider { + inner: Arc::new(TracerProviderInner { + processors: Vec::new(), + config: Config { + // cannot use default here as the default resource is not empty + sampler: Box::new(Sampler::ParentBased(Box::new(Sampler::AlwaysOn))), + id_generator: Box::::default(), + span_limits: SpanLimits::default(), + resource: Cow::Owned(Resource::empty()), + }, + is_shutdown: AtomicBool::new(true), + }), + } + }) +} /// TracerProvider inner type #[derive(Debug)] @@ -269,7 +275,7 @@ impl opentelemetry::trace::TracerProvider for TracerProvider { fn tracer_with_scope(&self, scope: InstrumentationScope) -> Self::Tracer { if self.inner.is_shutdown.load(Ordering::Relaxed) { - return Tracer::new(scope, NOOP_TRACER_PROVIDER.clone()); + return Tracer::new(scope, noop_tracer_provider().clone()); } Tracer::new(scope, self.clone()) } @@ -392,16 +398,13 @@ impl Builder { // For the uncommon case where there are multiple tracer providers with different resource // configurations, users can optionally provide their own borrowed static resource. if matches!(config.resource, Cow::Owned(_)) { - config.resource = match PROVIDER_RESOURCE.try_insert(config.resource.into_owned()) { - Ok(static_resource) => Cow::Borrowed(static_resource), - Err((prev, new)) => { - if prev == &new { - Cow::Borrowed(prev) - } else { - Cow::Owned(new) + config.resource = + match PROVIDER_RESOURCE.get_or_init(|| config.resource.clone().into_owned()) { + static_resource if *static_resource == *config.resource.as_ref() => { + Cow::Borrowed(static_resource) } - } - } + _ => config.resource, // Use the new resource if different + }; } // Create a new vector to hold the modified processors diff --git a/opentelemetry-semantic-conventions/scripts/templates/registry/rust/metric.rs.j2 b/opentelemetry-semantic-conventions/scripts/templates/registry/rust/metric.rs.j2 index 4936198432..55914a2932 100644 --- a/opentelemetry-semantic-conventions/scripts/templates/registry/rust/metric.rs.j2 +++ b/opentelemetry-semantic-conventions/scripts/templates/registry/rust/metric.rs.j2 @@ -26,7 +26,7 @@ //! .u64_histogram(semconv::metric::HTTP_SERVER_REQUEST_DURATION) //! .with_unit("By") //! .with_description("Duration of HTTP server requests.") -//! .init(); +//! .build(); //! ``` {% for root_ns in ctx %} diff --git a/opentelemetry-semantic-conventions/scripts/templates/registry/rust/resource.rs.j2 b/opentelemetry-semantic-conventions/scripts/templates/registry/rust/resource.rs.j2 index a808fa8714..eba1d90965 100644 --- a/opentelemetry-semantic-conventions/scripts/templates/registry/rust/resource.rs.j2 +++ b/opentelemetry-semantic-conventions/scripts/templates/registry/rust/resource.rs.j2 @@ -22,7 +22,6 @@ //! let _tracer = TracerProvider::builder() //! .with_config(config().with_resource(Resource::new(vec![ //! KeyValue::new(semconv::resource::SERVICE_NAME, "my-service"), -//! KeyValue::new(semconv::resource::SERVICE_NAMESPACE, "my-namespace"), //! ]))) //! .build(); //! ``` diff --git a/opentelemetry-semantic-conventions/src/resource.rs b/opentelemetry-semantic-conventions/src/resource.rs index 69d78daf32..6818d0d0e5 100644 --- a/opentelemetry-semantic-conventions/src/resource.rs +++ b/opentelemetry-semantic-conventions/src/resource.rs @@ -21,7 +21,6 @@ //! let _tracer = TracerProvider::builder() //! .with_config(config().with_resource(Resource::new(vec![ //! KeyValue::new(semconv::resource::SERVICE_NAME, "my-service"), -//! KeyValue::new(semconv::resource::SERVICE_NAMESPACE, "my-namespace"), //! ]))) //! .build(); //! ```