diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5b5d791054..4779b29d8d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -68,7 +68,7 @@ jobs: - uses: actions-rs/toolchain@v1 with: profile: minimal - toolchain: 1.49.0 + toolchain: 1.55.0 override: true - name: Run tests run: cargo --version && diff --git a/opentelemetry-api/Cargo.toml b/opentelemetry-api/Cargo.toml index 257ad09a47..1406d9f5a5 100644 --- a/opentelemetry-api/Cargo.toml +++ b/opentelemetry-api/Cargo.toml @@ -11,6 +11,7 @@ lazy_static = "1.4" pin-project = { version = "1.0.2", optional = true } thiserror = "1" tokio-stream = { version = "0.1", optional = true } +indexmap = "1" [package.metadata.docs.rs] all-features = true diff --git a/opentelemetry-api/src/trace/mod.rs b/opentelemetry-api/src/trace/mod.rs index ef377fa5ee..a2825df241 100644 --- a/opentelemetry-api/src/trace/mod.rs +++ b/opentelemetry-api/src/trace/mod.rs @@ -168,6 +168,7 @@ use thiserror::Error; mod context; pub mod noop; +mod order_map; mod span; mod span_context; mod tracer; @@ -175,6 +176,7 @@ mod tracer_provider; pub use self::{ context::{get_active_span, mark_span_as_active, FutureExt, SpanRef, TraceContextExt}, + order_map::OrderMap, span::{Span, SpanKind, Status}, span_context::{SpanContext, SpanId, TraceFlags, TraceId, TraceState}, tracer::{SamplingDecision, SamplingResult, SpanBuilder, Tracer}, diff --git a/opentelemetry-api/src/trace/order_map.rs b/opentelemetry-api/src/trace/order_map.rs new file mode 100644 index 0000000000..bd24e8f941 --- /dev/null +++ b/opentelemetry-api/src/trace/order_map.rs @@ -0,0 +1,668 @@ +use crate::{Key, KeyValue, Value}; +use indexmap::map::{ + Drain, Entry, IntoIter, IntoKeys, IntoValues, Iter, IterMut, Keys, Values, ValuesMut, +}; +use indexmap::{Equivalent, IndexMap}; +use std::collections::hash_map::RandomState; +use std::hash::{BuildHasher, Hash}; +use std::iter::FromIterator; +use std::ops::{Index, IndexMut, RangeBounds}; + +/// A hash table implementation that preserves insertion order across all operations. +/// +/// Entries will be returned according to their insertion order when iterating over the collection. +#[derive(Clone, Debug)] +pub struct OrderMap(IndexMap); + +impl OrderMap { + /// Create a new map. (Does not allocate) + #[inline] + pub fn new() -> Self { + Self(IndexMap::new()) + } + + /// Create a new map with capacity for `n` key-value pairs. (Does not + /// allocate if `n` is zero.) + /// + /// Computes in **O(n)** time. + #[inline] + pub fn with_capacity(n: usize) -> Self { + Self(IndexMap::with_capacity(n)) + } +} + +impl OrderMap { + /// Create a new map with capacity for `n` key-value pairs. (Does not + /// allocate if `n` is zero.) + /// + /// Computes in **O(n)** time. + #[inline] + pub fn with_capacity_and_hasher(n: usize, hash_builder: S) -> Self { + Self(IndexMap::with_capacity_and_hasher(n, hash_builder)) + } + + /// Create a new map with `hash_builder`. + /// + /// This function is `const`, so it + /// can be called in `static` contexts. + pub const fn with_hasher(hash_builder: S) -> Self { + Self(IndexMap::with_hasher(hash_builder)) + } + + /// Computes in **O(1)** time. + pub fn capacity(&self) -> usize { + self.0.capacity() + } + + /// Return a reference to the map's `BuildHasher`. + pub fn hasher(&self) -> &S { + self.0.hasher() + } + + /// Return the number of key-value pairs in the map. + /// + /// Computes in **O(1)** time. + #[inline] + pub fn len(&self) -> usize { + self.0.len() + } + + /// Returns true if the map contains no elements. + /// + /// Computes in **O(1)** time. + #[inline] + pub fn is_empty(&self) -> bool { + self.0.is_empty() + } + + /// Return an iterator over the key-value pairs of the map, in their order + pub fn iter(&self) -> Iter<'_, K, V> { + self.0.iter() + } + + /// Return an iterator over the key-value pairs of the map, in their order + pub fn iter_mut(&mut self) -> IterMut<'_, K, V> { + self.0.iter_mut() + } + + /// Return an iterator over the keys of the map, in their order + pub fn keys(&self) -> Keys<'_, K, V> { + self.0.keys() + } + + /// Return an owning iterator over the keys of the map, in their order + pub fn into_keys(self) -> IntoKeys { + self.0.into_keys() + } + + /// Return an iterator over the values of the map, in their order + pub fn values(&self) -> Values<'_, K, V> { + self.0.values() + } + + /// Return an iterator over mutable references to the values of the map, + /// in their order + pub fn values_mut(&mut self) -> ValuesMut<'_, K, V> { + self.0.values_mut() + } + + /// Return an owning iterator over the values of the map, in their order + pub fn into_values(self) -> IntoValues { + self.0.into_values() + } + + /// Remove all key-value pairs in the map, while preserving its capacity. + /// + /// Computes in **O(n)** time. + pub fn clear(&mut self) { + self.0.clear(); + } + + /// Shortens the map, keeping the first `len` elements and dropping the rest. + /// + /// If `len` is greater than the map's current length, this has no effect. + pub fn truncate(&mut self, len: usize) { + self.0.truncate(len); + } + + /// Clears the `IndexMap` in the given index range, returning those + /// key-value pairs as a drain iterator. + /// + /// The range may be any type that implements `RangeBounds`, + /// including all of the `std::ops::Range*` types, or even a tuple pair of + /// `Bound` start and end values. To drain the map entirely, use `RangeFull` + /// like `map.drain(..)`. + /// + /// This shifts down all entries following the drained range to fill the + /// gap, and keeps the allocated memory for reuse. + /// + /// ***Panics*** if the starting point is greater than the end point or if + /// the end point is greater than the length of the map. + pub fn drain(&mut self, range: R) -> Drain<'_, K, V> + where + R: RangeBounds, + { + self.0.drain(range) + } + + /// Splits the collection into two at the given index. + /// + /// Returns a newly allocated map containing the elements in the range + /// `[at, len)`. After the call, the original map will be left containing + /// the elements `[0, at)` with its previous capacity unchanged. + /// + /// ***Panics*** if `at > len`. + pub fn split_off(&mut self, at: usize) -> Self + where + S: Clone, + { + Self(self.0.split_off(at)) + } +} + +impl OrderMap +where + K: Hash + Eq, + S: BuildHasher, +{ + /// Reserve capacity for `additional` more key-value pairs. + /// + /// Computes in **O(n)** time. + pub fn reserve(&mut self, additional: usize) { + self.0.reserve(additional) + } + + /// Shrink the capacity of the map as much as possible. + /// + /// Computes in **O(n)** time. + pub fn shrink_to_fit(&mut self) { + self.0.shrink_to_fit() + } + + /// Insert a key-value pair in the map. + /// + /// If an equivalent key already exists in the map: the key remains and + /// retains in its place in the order, its corresponding value is updated + /// with `value` and the older value is returned inside `Some(_)`. + /// + /// If no equivalent key existed in the map: the new key-value pair is + /// inserted, last in order, and `None` is returned. + /// + /// Computes in **O(1)** time (amortized average). + /// + /// See also [`entry`](#method.entry) if you you want to insert *or* modify + /// or if you need to get the index of the corresponding key-value pair. + pub fn insert(&mut self, key: K, value: V) -> Option { + self.0.insert(key, value) + } + + /// Insert a key-value pair in the map, and get their index. + /// + /// If an equivalent key already exists in the map: the key remains and + /// retains in its place in the order, its corresponding value is updated + /// with `value` and the older value is returned inside `(index, Some(_))`. + /// + /// If no equivalent key existed in the map: the new key-value pair is + /// inserted, last in order, and `(index, None)` is returned. + /// + /// Computes in **O(1)** time (amortized average). + /// + /// See also [`entry`](#method.entry) if you you want to insert *or* modify + /// or if you need to get the index of the corresponding key-value pair. + pub fn insert_full(&mut self, key: K, value: V) -> (usize, Option) { + self.0.insert_full(key, value) + } + + /// Get the given key’s corresponding entry in the map for insertion and/or + /// in-place manipulation. + /// + /// Computes in **O(1)** time (amortized average). + pub fn entry(&mut self, key: K) -> Entry<'_, K, V> { + self.0.entry(key) + } + + /// Return `true` if an equivalent to `key` exists in the map. + /// + /// Computes in **O(1)** time (average). + pub fn contains_key(&self, key: &Q) -> bool + where + Q: Hash + Equivalent, + { + self.0.contains_key(key) + } + + /// Return a reference to the value stored for `key`, if it is present, + /// else `None`. + /// + /// Computes in **O(1)** time (average). + pub fn get(&self, key: &Q) -> Option<&V> + where + Q: Hash + Equivalent, + { + self.0.get(key) + } + + /// Return references to the key-value pair stored for `key`, + /// if it is present, else `None`. + /// + /// Computes in **O(1)** time (average). + pub fn get_key_value(&self, key: &Q) -> Option<(&K, &V)> + where + Q: Hash + Equivalent, + { + self.0.get_key_value(key) + } + + /// Return item index, key and value + pub fn get_full(&self, key: &Q) -> Option<(usize, &K, &V)> + where + Q: Hash + Equivalent, + { + self.0.get_full(key) + } + + /// Return item index, if it exists in the map + /// + /// Computes in **O(1)** time (average). + pub fn get_index_of(&self, key: &Q) -> Option + where + Q: Hash + Equivalent, + { + self.0.get_index_of(key) + } + + /// Return a mutable reference to the element pointed at by `key`, if it exists. + pub fn get_mut(&mut self, key: &Q) -> Option<&mut V> + where + Q: Hash + Equivalent, + { + self.0.get_mut(key) + } + + /// Return a mutable reference to the element pointed at by `key`, if it exists. + /// It also returns the element's index and its key. + pub fn get_full_mut(&mut self, key: &Q) -> Option<(usize, &K, &mut V)> + where + Q: Hash + Equivalent, + { + self.0.get_full_mut(key) + } + + /// Remove the key-value pair equivalent to `key` and return + /// its value. + /// + /// Like `Vec::remove`, the pair is removed by shifting all of the + /// elements that follow it, preserving their relative order. + /// **This perturbs the index of all of those elements!** + /// + /// Return `None` if `key` is not in map. + /// + /// Computes in **O(n)** time (average). + pub fn shift_remove(&mut self, key: &Q) -> Option + where + Q: Hash + Equivalent, + { + self.0.shift_remove(key) + } + + /// Remove and return the key-value pair equivalent to `key`. + /// + /// Like `Vec::remove`, the pair is removed by shifting all of the + /// elements that follow it, preserving their relative order. + /// **This perturbs the index of all of those elements!** + /// + /// Return `None` if `key` is not in map. + /// + /// Computes in **O(n)** time (average). + pub fn shift_remove_entry(&mut self, key: &Q) -> Option<(K, V)> + where + Q: Hash + Equivalent, + { + self.0.shift_remove_entry(key) + } + + /// Remove the key-value pair equivalent to `key` and return it and + /// the index it had. + /// + /// Like `Vec::remove`, the pair is removed by shifting all of the + /// elements that follow it, preserving their relative order. + /// **This perturbs the index of all of those elements!** + /// + /// Return `None` if `key` is not in map. + /// + /// Computes in **O(n)** time (average). + pub fn shift_remove_full(&mut self, key: &Q) -> Option<(usize, K, V)> + where + Q: Hash + Equivalent, + { + self.0.shift_remove_full(key) + } + + /// Remove the last key-value pair + /// + /// This preserves the order of the remaining elements. + /// + /// Computes in **O(1)** time (average). + pub fn pop(&mut self) -> Option<(K, V)> { + self.0.pop() + } + + /// Scan through each key-value pair in the map and keep those where the + /// closure `keep` returns `true`. + /// + /// The elements are visited in order, and remaining elements keep their + /// order. + /// + /// Computes in **O(n)** time (average). + pub fn retain(&mut self, keep: F) + where + F: FnMut(&K, &mut V) -> bool, + { + self.0.retain(keep); + } +} + +impl OrderMap { + /// Get a key-value pair by index + /// + /// Valid indices are *0 <= index < self.len()* + /// + /// Computes in **O(1)** time. + pub fn get_index(&self, index: usize) -> Option<(&K, &V)> { + self.0.get_index(index) + } + + /// Get a key-value pair by index + /// + /// Valid indices are *0 <= index < self.len()* + /// + /// Computes in **O(1)** time. + pub fn get_index_mut(&mut self, index: usize) -> Option<(&mut K, &mut V)> { + self.0.get_index_mut(index) + } + + /// Get the first key-value pair + /// + /// Computes in **O(1)** time. + pub fn first(&self) -> Option<(&K, &V)> { + self.0.first() + } + + /// Get the first key-value pair, with mutable access to the value + /// + /// Computes in **O(1)** time. + pub fn first_mut(&mut self) -> Option<(&K, &mut V)> { + self.0.first_mut() + } + + /// Get the last key-value pair + /// + /// Computes in **O(1)** time. + pub fn last(&self) -> Option<(&K, &V)> { + self.0.last() + } + + /// Get the last key-value pair, with mutable access to the value + /// + /// Computes in **O(1)** time. + pub fn last_mut(&mut self) -> Option<(&K, &mut V)> { + self.0.last_mut() + } + + /// Remove the key-value pair by index + /// + /// Valid indices are *0 <= index < self.len()* + /// + /// Like `Vec::remove`, the pair is removed by shifting all of the + /// elements that follow it, preserving their relative order. + /// **This perturbs the index of all of those elements!** + /// + /// Computes in **O(n)** time (average). + pub fn shift_remove_index(&mut self, index: usize) -> Option<(K, V)> { + self.0.shift_remove_index(index) + } +} + +impl<'a, K, V, S> IntoIterator for &'a OrderMap { + type Item = (&'a K, &'a V); + type IntoIter = Iter<'a, K, V>; + fn into_iter(self) -> Self::IntoIter { + self.0.iter() + } +} + +impl<'a, K, V, S> IntoIterator for &'a mut OrderMap { + type Item = (&'a K, &'a mut V); + type IntoIter = IterMut<'a, K, V>; + fn into_iter(self) -> Self::IntoIter { + self.0.iter_mut() + } +} + +impl IntoIterator for OrderMap { + type Item = (K, V); + type IntoIter = IntoIter; + fn into_iter(self) -> Self::IntoIter { + self.0.into_iter() + } +} + +/// Access `OrderMap` values corresponding to a key. +/// +/// Panics if the value is missing. +impl Index<&Q> for OrderMap +where + Q: Hash + Equivalent, + K: Hash + Eq, + S: BuildHasher, +{ + type Output = V; + + /// Returns a reference to the value corresponding to the supplied `key`. + /// + /// ***Panics*** if `key` is not present in the map. + fn index(&self, key: &Q) -> &V { + self.0.index(key) + } +} + +/// Access `Ordermap` values corresponding to a key. +/// +/// Mutable indexing allows changing / updating values of key-value +/// pairs that are already present. +/// +/// You can **not** insert new pairs with index syntax, use `.insert()`. +impl IndexMut<&Q> for OrderMap +where + Q: Hash + Equivalent, + K: Hash + Eq, + S: BuildHasher, +{ + /// Returns a mutable reference to the value corresponding to the supplied `key`. + /// + /// ***Panics*** if `key` is not present in the map. + fn index_mut(&mut self, key: &Q) -> &mut V { + self.0.index_mut(key) + } +} + +/// Access `IndexMap` values at indexed positions. +/// +/// It panics if the index is out of bounds. +impl Index for OrderMap { + type Output = V; + + /// Returns a reference to the value at the supplied `index`. + /// + /// ***Panics*** if `index` is out of bounds. + fn index(&self, index: usize) -> &V { + self.0.index(index) + } +} + +/// Access `IndexMap` values at indexed positions. +/// +/// Mutable indexing allows changing / updating indexed values +/// that are already present. +/// +/// You can **not** insert new values with index syntax, use `.insert()`. +/// +/// # Examples +/// +/// ``` +/// use indexmap::IndexMap; +/// +/// let mut map = IndexMap::new(); +/// for word in "Lorem ipsum dolor sit amet".split_whitespace() { +/// map.insert(word.to_lowercase(), word.to_string()); +/// } +/// let lorem = &mut map[0]; +/// assert_eq!(lorem, "Lorem"); +/// lorem.retain(char::is_lowercase); +/// assert_eq!(map["lorem"], "orem"); +/// ``` +/// +/// ```should_panic +/// use indexmap::IndexMap; +/// +/// let mut map = IndexMap::new(); +/// map.insert("foo", 1); +/// map[10] = 1; // panics! +/// ``` +impl IndexMut for OrderMap { + /// Returns a mutable reference to the value at the supplied `index`. + /// + /// ***Panics*** if `index` is out of bounds. + fn index_mut(&mut self, index: usize) -> &mut V { + self.0.index_mut(index) + } +} + +impl FromIterator<(K, V)> for OrderMap +where + K: Hash + Eq, + S: BuildHasher + Default, +{ + /// Create an `OrderMap` from the sequence of key-value pairs in the + /// iterable. + /// + /// `from_iter` uses the same logic as `extend`. See + /// [`extend`](#method.extend) for more details. + fn from_iter>(iterable: I) -> Self { + Self(IndexMap::from_iter(iterable)) + } +} + +impl From<[(K, V); N]> for OrderMap +where + K: Hash + Eq, +{ + fn from(arr: [(K, V); N]) -> Self { + Self(IndexMap::from(arr)) + } +} + +impl Extend<(K, V)> for OrderMap +where + K: Hash + Eq, + S: BuildHasher, +{ + /// Extend the map with all key-value pairs in the iterable. + /// + /// This is equivalent to calling [`insert`](#method.insert) for each of + /// them in order, which means that for keys that already existed + /// in the map, their value is updated but it keeps the existing order. + /// + /// New keys are inserted in the order they appear in the sequence. If + /// equivalents of a key occur more than once, the last corresponding value + /// prevails. + fn extend>(&mut self, iterable: I) { + self.0.extend(iterable) + } +} + +impl<'a, K, V, S> Extend<(&'a K, &'a V)> for OrderMap +where + K: 'a + Hash + Eq + Copy, + V: 'a + Copy, + S: BuildHasher, +{ + /// Extend the map with all key-value pairs in the iterable. + /// + /// See the first extend method for more details. + fn extend>(&mut self, iterable: I) { + self.0.extend(iterable) + } +} + +impl Default for OrderMap +where + S: Default, +{ + /// Return an empty `OrderMap` + fn default() -> Self { + Self(IndexMap::default()) + } +} + +impl PartialEq> for OrderMap +where + K: Hash + Eq, + V1: PartialEq, + S1: BuildHasher, + S2: BuildHasher, +{ + fn eq(&self, other: &OrderMap) -> bool { + self.0.eq(&other.0) + } +} + +impl Eq for OrderMap +where + K: Eq + Hash, + V: Eq, + S: BuildHasher, +{ +} + +impl FromIterator for OrderMap +where + S: BuildHasher + Default, +{ + /// Create an `OrderMap` from the sequence of key-value pairs in the + /// iterable. + /// + /// `from_iter` uses the same logic as `extend`. See + /// [`extend`](#method.extend) for more details. + fn from_iter>(iterable: I) -> Self { + Self(IndexMap::from_iter( + iterable.into_iter().map(|kv| (kv.key, kv.value)), + )) + } +} + +impl From<[KeyValue; N]> for OrderMap { + fn from(arr: [KeyValue; N]) -> Self { + let arr = arr.map(|kv| (kv.key, kv.value)); + Self(IndexMap::from(arr)) + } +} + +impl Extend for OrderMap +where + S: BuildHasher, +{ + /// Extend the map with all key-value pairs in the iterable. + /// + /// This is equivalent to calling [`insert`](#method.insert) for each of + /// them in order, which means that for keys that already existed + /// in the map, their value is updated but it keeps the existing order. + /// + /// New keys are inserted in the order they appear in the sequence. If + /// equivalents of a key occur more than once, the last corresponding value + /// prevails. + fn extend>(&mut self, iterable: I) { + self.0 + .extend(iterable.into_iter().map(|kv| (kv.key, kv.value))) + } +} diff --git a/opentelemetry-api/src/trace/tracer.rs b/opentelemetry-api/src/trace/tracer.rs index 1596d62b37..e36d32c5c5 100644 --- a/opentelemetry-api/src/trace/tracer.rs +++ b/opentelemetry-api/src/trace/tracer.rs @@ -1,8 +1,10 @@ +use crate::trace::OrderMap; use crate::{ trace::{Event, Link, Span, SpanId, SpanKind, Status, TraceContextExt, TraceId, TraceState}, - Context, KeyValue, + Context, Key, KeyValue, Value, }; use std::borrow::Cow; +use std::iter::FromIterator; use std::time::SystemTime; /// The interface for constructing [`Span`]s. @@ -259,7 +261,7 @@ pub struct SpanBuilder { pub end_time: Option, /// Span attributes - pub attributes: Option>, + pub attributes: Option>, /// Span events pub events: Option>, @@ -324,8 +326,25 @@ impl SpanBuilder { } } - /// Assign span attributes - pub fn with_attributes(self, attributes: Vec) -> Self { + /// Assign span attributes from an iterable. + /// + /// Check out [`SpanBuilder::with_attributes_map`] to assign span attributes + /// via an [`OrderMap`] instance. + pub fn with_attributes(self, attributes: I) -> Self + where + I: IntoIterator, + { + SpanBuilder { + attributes: Some(OrderMap::from_iter(attributes.into_iter())), + ..self + } + } + + /// Assign span attributes. + /// + /// Check out [`SpanBuilder::with_attributes`] to assign span attributes + /// from an iterable of [`KeyValue`]s. + pub fn with_attributes_map(self, attributes: OrderMap) -> Self { SpanBuilder { attributes: Some(attributes), ..self diff --git a/opentelemetry-sdk/src/trace/sampler.rs b/opentelemetry-sdk/src/trace/sampler.rs index 8928632584..6d3e4942d6 100644 --- a/opentelemetry-sdk/src/trace/sampler.rs +++ b/opentelemetry-sdk/src/trace/sampler.rs @@ -38,11 +38,12 @@ //! MUST NOT allow this combination. use crate::InstrumentationLibrary; +use opentelemetry_api::trace::OrderMap; use opentelemetry_api::{ trace::{ Link, SamplingDecision, SamplingResult, SpanKind, TraceContextExt, TraceId, TraceState, }, - Context, KeyValue, + Context, Key, Value, }; use std::convert::TryInto; @@ -58,7 +59,7 @@ pub trait ShouldSample: Send + Sync + std::fmt::Debug { trace_id: TraceId, name: &str, span_kind: &SpanKind, - attributes: &[KeyValue], + attributes: &OrderMap, links: &[Link], instrumentation_library: &InstrumentationLibrary, ) -> SamplingResult; @@ -86,7 +87,7 @@ impl ShouldSample for Sampler { trace_id: TraceId, name: &str, span_kind: &SpanKind, - attributes: &[KeyValue], + attributes: &OrderMap, links: &[Link], instrumentation_library: &InstrumentationLibrary, ) -> SamplingResult { @@ -242,7 +243,7 @@ mod tests { trace_id, name, &SpanKind::Internal, - &[], + &Default::default(), &[], &InstrumentationLibrary::default(), ) @@ -284,7 +285,7 @@ mod tests { TraceId::from_u128(1), "should sample", &SpanKind::Internal, - &[], + &Default::default(), &[], &instrumentation_library, ); diff --git a/opentelemetry-sdk/src/trace/tracer.rs b/opentelemetry-sdk/src/trace/tracer.rs index ace67a34e9..2852d621d7 100644 --- a/opentelemetry-sdk/src/trace/tracer.rs +++ b/opentelemetry-sdk/src/trace/tracer.rs @@ -17,10 +17,10 @@ use crate::{ InstrumentationLibrary, }; use opentelemetry_api::trace::{ - Link, SamplingDecision, SamplingResult, SpanBuilder, SpanContext, SpanId, SpanKind, + Link, OrderMap, SamplingDecision, SamplingResult, SpanBuilder, SpanContext, SpanId, SpanKind, TraceContextExt, TraceFlags, TraceId, TraceState, }; -use opentelemetry_api::{Context, KeyValue}; +use opentelemetry_api::{Context, Key, KeyValue, Value}; use std::fmt; use std::sync::Weak; @@ -72,7 +72,7 @@ impl Tracer { trace_id: TraceId, name: &str, span_kind: &SpanKind, - attributes: &[KeyValue], + attributes: &OrderMap, links: &[Link], config: &Config, instrumentation_library: &InstrumentationLibrary, @@ -218,14 +218,14 @@ impl opentelemetry_api::trace::Tracer for Tracer { } = builder; // Build optional inner context, `None` if not recording. - let mut span = if let Some((flags, mut extra_attrs, trace_state)) = sampling_decision { - if !extra_attrs.is_empty() { - attribute_options.append(&mut extra_attrs); + let mut span = if let Some((flags, extra_attrs, trace_state)) = sampling_decision { + for extra_attr in extra_attrs { + attribute_options.insert(extra_attr.key, extra_attr.value); } let mut attributes = EvictedHashMap::new(span_limits.max_attributes_per_span, attribute_options.len()); - for attribute in attribute_options { - attributes.insert(attribute); + for (key, value) in attribute_options { + attributes.insert(KeyValue::new(key, value)); } let mut links = EvictedQueue::new(span_limits.max_links_per_span); if let Some(link_options) = &mut link_options { @@ -300,10 +300,10 @@ mod tests { }; use opentelemetry_api::{ trace::{ - Link, SamplingDecision, SamplingResult, Span, SpanContext, SpanId, SpanKind, + Link, OrderMap, SamplingDecision, SamplingResult, Span, SpanContext, SpanId, SpanKind, TraceContextExt, TraceFlags, TraceId, TraceState, Tracer, TracerProvider, }, - Context, KeyValue, + Context, Key, Value, }; #[derive(Debug)] @@ -316,7 +316,7 @@ mod tests { _trace_id: TraceId, _name: &str, _span_kind: &SpanKind, - _attributes: &[KeyValue], + _attributes: &OrderMap, _links: &[Link], _instrumentation_library: &InstrumentationLibrary, ) -> SamplingResult { diff --git a/opentelemetry-semantic-conventions/src/trace.rs b/opentelemetry-semantic-conventions/src/trace.rs index f6371d0763..01448e16d8 100644 --- a/opentelemetry-semantic-conventions/src/trace.rs +++ b/opentelemetry-semantic-conventions/src/trace.rs @@ -14,13 +14,13 @@ //! ## Usage //! //! ```rust -//! use opentelemetry::{global, trace::Tracer as _}; +//! use opentelemetry::{global, trace::Tracer as _, trace::OrderMap}; //! use opentelemetry_semantic_conventions as semcov; //! //! let tracer = global::tracer("my-component"); //! let _span = tracer //! .span_builder("span-name") -//! .with_attributes(vec![ +//! .with_attributes([ //! semcov::trace::NET_PEER_IP.string("10.0.0.1"), //! semcov::trace::NET_PEER_PORT.i64(80), //! ])