diff --git a/opentelemetry/CHANGELOG.md b/opentelemetry/CHANGELOG.md index 80e1c3249c..ed6a4a473b 100644 --- a/opentelemetry/CHANGELOG.md +++ b/opentelemetry/CHANGELOG.md @@ -4,6 +4,7 @@ - *Breaking* Change return type of `opentelemetry::global::set_tracer_provider` to Unit to align with metrics counterpart - Add `get_all` method to `opentelemetry::propagation::Extractor` to return all values of the given propagation key and provide a default implementation. +- Adds `opentelemetry::AsKeyValues` trait and supporting `opentelemetry::KeyValueCollector` struct to convert types into `KeyValue`s for use as attributes. ## 0.30.0 diff --git a/opentelemetry/src/common.rs b/opentelemetry/src/common.rs index 55a9e1dd67..d9302eefe2 100644 --- a/opentelemetry/src/common.rs +++ b/opentelemetry/src/common.rs @@ -413,6 +413,55 @@ impl KeyValue { } } +/// Create a new `KeyValue` from a key and value tuple. +impl From<(K, V)> for KeyValue +where + K: Into, + V: Into, +{ + fn from(tuple: (K, V)) -> Self { + KeyValue::new(tuple.0, tuple.1) + } +} + +/// Represents a collector for `KeyValue` attributes. +#[derive(Default, Debug)] +pub struct KeyValueCollector { + vec: Vec, +} + +impl KeyValueCollector { + /// Adds a key-value pair to the collector. + pub fn add(&mut self, key: K, value: V) + where + K: Into, + V: Into, + { + self.add_kv((key, value)); + } + + /// Adds a `KeyValue` to the collector. + pub fn add_kv(&mut self, kv: KV) + where + KV: Into, + { + self.vec.push(kv.into()); + } +} + +/// A trait for types that can be converted to a set of `KeyValue` attributes. +pub trait AsKeyValues { + /// Returns a vector of `KeyValue` attributes. + fn as_key_values(&self) -> Vec { + let mut collector = KeyValueCollector::default(); + self.collect_key_values(&mut collector); + collector.vec + } + + /// Collects key-value pairs into the provided `KeyValueCollector`. + fn collect_key_values(&self, collector: &mut KeyValueCollector); +} + struct F64Hashable(f64); impl PartialEq for F64Hashable { @@ -628,7 +677,7 @@ impl InstrumentationScopeBuilder { mod tests { use std::hash::{Hash, Hasher}; - use crate::{InstrumentationScope, KeyValue}; + use crate::{AsKeyValues, InstrumentationScope, KeyValue, KeyValueCollector}; use rand::random; use std::collections::hash_map::DefaultHasher; @@ -701,6 +750,33 @@ mod tests { hasher.finish() } + #[test] + fn kv_from_tuple() { + let kv: KeyValue = ("key", "value").into(); + assert_eq!(kv.key.as_str(), "key"); + assert_eq!(kv.value.as_str(), "value"); + } + + struct HelperStruct; + + impl AsKeyValues for HelperStruct { + fn collect_key_values(&self, collector: &mut KeyValueCollector) { + collector.add("key1", "value1"); + collector.add_kv(("key2", "value2")); + } + } + + #[test] + fn kv_collector() { + let helper = HelperStruct; + let kvs = helper.as_key_values(); + assert_eq!(kvs.len(), 2); + assert_eq!(kvs[0].key.as_str(), "key1"); + assert_eq!(kvs[0].value.as_str(), "value1"); + assert_eq!(kvs[1].key.as_str(), "key2"); + assert_eq!(kvs[1].value.as_str(), "value2"); + } + #[test] fn instrumentation_scope_equality() { let scope1 = InstrumentationScope::builder("my-crate") diff --git a/opentelemetry/src/lib.rs b/opentelemetry/src/lib.rs index ebed7960ea..0610536e23 100644 --- a/opentelemetry/src/lib.rs +++ b/opentelemetry/src/lib.rs @@ -260,7 +260,8 @@ mod common; pub mod testing; pub use common::{ - Array, InstrumentationScope, InstrumentationScopeBuilder, Key, KeyValue, StringValue, Value, + Array, AsKeyValues, InstrumentationScope, InstrumentationScopeBuilder, Key, KeyValue, + KeyValueCollector, StringValue, Value, }; #[cfg(feature = "metrics")]