opentelemetry/
common.rs

1use std::borrow::{Borrow, Cow};
2use std::sync::Arc;
3use std::{fmt, hash};
4
5/// The key part of attribute [KeyValue] pairs.
6///
7/// See the [attribute naming] spec for guidelines.
8///
9/// [attribute naming]: https://github.com/open-telemetry/semantic-conventions/blob/main/docs/general/attribute-naming.md
10#[non_exhaustive]
11#[derive(Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
12pub struct Key(OtelString);
13
14impl Key {
15    /// Create a new `Key`.
16    ///
17    /// # Examples
18    ///
19    /// ```
20    /// use opentelemetry::Key;
21    /// use std::sync::Arc;
22    ///
23    /// let key1 = Key::new("my_static_str");
24    /// let key2 = Key::new(String::from("my_owned_string"));
25    /// let key3 = Key::new(Arc::from("my_ref_counted_str"));
26    /// ```
27    pub fn new(value: impl Into<Key>) -> Self {
28        value.into()
29    }
30
31    /// Create a new const `Key`.
32    pub const fn from_static_str(value: &'static str) -> Self {
33        Key(OtelString::Static(value))
34    }
35
36    /// Returns a reference to the underlying key name
37    pub fn as_str(&self) -> &str {
38        self.0.as_str()
39    }
40}
41
42impl From<&'static str> for Key {
43    /// Convert a `&str` to a `Key`.
44    fn from(key_str: &'static str) -> Self {
45        Key(OtelString::Static(key_str))
46    }
47}
48
49impl From<String> for Key {
50    /// Convert a `String` to a `Key`.
51    fn from(string: String) -> Self {
52        Key(OtelString::Owned(string.into_boxed_str()))
53    }
54}
55
56impl From<Arc<str>> for Key {
57    /// Convert a `String` to a `Key`.
58    fn from(string: Arc<str>) -> Self {
59        Key(OtelString::RefCounted(string))
60    }
61}
62
63impl From<Cow<'static, str>> for Key {
64    /// Convert a `Cow<'static, str>` to a `Key`
65    fn from(string: Cow<'static, str>) -> Self {
66        match string {
67            Cow::Borrowed(s) => Key(OtelString::Static(s)),
68            Cow::Owned(s) => Key(OtelString::Owned(s.into_boxed_str())),
69        }
70    }
71}
72
73impl fmt::Debug for Key {
74    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
75        self.0.fmt(fmt)
76    }
77}
78
79impl From<Key> for String {
80    fn from(key: Key) -> Self {
81        match key.0 {
82            OtelString::Owned(s) => s.to_string(),
83            OtelString::Static(s) => s.to_string(),
84            OtelString::RefCounted(s) => s.to_string(),
85        }
86    }
87}
88
89impl fmt::Display for Key {
90    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
91        match &self.0 {
92            OtelString::Owned(s) => s.fmt(fmt),
93            OtelString::Static(s) => s.fmt(fmt),
94            OtelString::RefCounted(s) => s.fmt(fmt),
95        }
96    }
97}
98
99impl Borrow<str> for Key {
100    fn borrow(&self) -> &str {
101        self.0.as_str()
102    }
103}
104
105impl AsRef<str> for Key {
106    fn as_ref(&self) -> &str {
107        self.0.as_str()
108    }
109}
110
111#[derive(Clone, Debug, Eq)]
112enum OtelString {
113    Owned(Box<str>),
114    Static(&'static str),
115    RefCounted(Arc<str>),
116}
117
118impl OtelString {
119    fn as_str(&self) -> &str {
120        match self {
121            OtelString::Owned(s) => s.as_ref(),
122            OtelString::Static(s) => s,
123            OtelString::RefCounted(s) => s.as_ref(),
124        }
125    }
126}
127
128impl PartialOrd for OtelString {
129    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
130        Some(self.cmp(other))
131    }
132}
133
134impl Ord for OtelString {
135    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
136        self.as_str().cmp(other.as_str())
137    }
138}
139
140impl PartialEq for OtelString {
141    fn eq(&self, other: &Self) -> bool {
142        self.as_str().eq(other.as_str())
143    }
144}
145
146impl hash::Hash for OtelString {
147    fn hash<H: hash::Hasher>(&self, state: &mut H) {
148        self.as_str().hash(state)
149    }
150}
151
152/// A [Value::Array] containing homogeneous values.
153#[non_exhaustive]
154#[derive(Clone, Debug, PartialEq)]
155pub enum Array {
156    /// Array of bools
157    Bool(Vec<bool>),
158    /// Array of integers
159    I64(Vec<i64>),
160    /// Array of floats
161    F64(Vec<f64>),
162    /// Array of strings
163    String(Vec<StringValue>),
164}
165
166impl fmt::Display for Array {
167    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
168        match self {
169            Array::Bool(values) => display_array_str(values, fmt),
170            Array::I64(values) => display_array_str(values, fmt),
171            Array::F64(values) => display_array_str(values, fmt),
172            Array::String(values) => {
173                write!(fmt, "[")?;
174                for (i, t) in values.iter().enumerate() {
175                    if i > 0 {
176                        write!(fmt, ",")?;
177                    }
178                    write!(fmt, "\"{}\"", t)?;
179                }
180                write!(fmt, "]")
181            }
182        }
183    }
184}
185
186fn display_array_str<T: fmt::Display>(slice: &[T], fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
187    write!(fmt, "[")?;
188    for (i, t) in slice.iter().enumerate() {
189        if i > 0 {
190            write!(fmt, ",")?;
191        }
192        write!(fmt, "{}", t)?;
193    }
194    write!(fmt, "]")
195}
196
197macro_rules! into_array {
198    ($(($t:ty, $val:expr),)+) => {
199        $(
200            impl From<$t> for Array {
201                fn from(t: $t) -> Self {
202                    $val(t)
203                }
204            }
205        )+
206    }
207}
208
209into_array!(
210    (Vec<bool>, Array::Bool),
211    (Vec<i64>, Array::I64),
212    (Vec<f64>, Array::F64),
213    (Vec<StringValue>, Array::String),
214);
215
216/// The value part of attribute [KeyValue] pairs.
217#[non_exhaustive]
218#[derive(Clone, Debug, PartialEq)]
219pub enum Value {
220    /// bool values
221    Bool(bool),
222    /// i64 values
223    I64(i64),
224    /// f64 values
225    F64(f64),
226    /// String values
227    String(StringValue),
228    /// Array of homogeneous values
229    Array(Array),
230}
231
232/// Wrapper for string-like values
233#[non_exhaustive]
234#[derive(Clone, PartialEq, Eq, Hash)]
235pub struct StringValue(OtelString);
236
237impl fmt::Debug for StringValue {
238    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
239        self.0.fmt(f)
240    }
241}
242
243impl fmt::Display for StringValue {
244    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
245        match &self.0 {
246            OtelString::Owned(s) => s.fmt(f),
247            OtelString::Static(s) => s.fmt(f),
248            OtelString::RefCounted(s) => s.fmt(f),
249        }
250    }
251}
252
253impl AsRef<str> for StringValue {
254    fn as_ref(&self) -> &str {
255        self.0.as_str()
256    }
257}
258
259impl StringValue {
260    /// Returns a string slice to this value
261    pub fn as_str(&self) -> &str {
262        self.0.as_str()
263    }
264}
265
266impl From<StringValue> for String {
267    fn from(s: StringValue) -> Self {
268        match s.0 {
269            OtelString::Owned(s) => s.to_string(),
270            OtelString::Static(s) => s.to_string(),
271            OtelString::RefCounted(s) => s.to_string(),
272        }
273    }
274}
275
276impl From<&'static str> for StringValue {
277    fn from(s: &'static str) -> Self {
278        StringValue(OtelString::Static(s))
279    }
280}
281
282impl From<String> for StringValue {
283    fn from(s: String) -> Self {
284        StringValue(OtelString::Owned(s.into_boxed_str()))
285    }
286}
287
288impl From<Arc<str>> for StringValue {
289    fn from(s: Arc<str>) -> Self {
290        StringValue(OtelString::RefCounted(s))
291    }
292}
293
294impl From<Cow<'static, str>> for StringValue {
295    fn from(s: Cow<'static, str>) -> Self {
296        match s {
297            Cow::Owned(s) => StringValue(OtelString::Owned(s.into_boxed_str())),
298            Cow::Borrowed(s) => StringValue(OtelString::Static(s)),
299        }
300    }
301}
302
303impl Value {
304    /// String representation of the `Value`
305    ///
306    /// This will allocate iff the underlying value is not a `String`.
307    pub fn as_str(&self) -> Cow<'_, str> {
308        match self {
309            Value::Bool(v) => format!("{}", v).into(),
310            Value::I64(v) => format!("{}", v).into(),
311            Value::F64(v) => format!("{}", v).into(),
312            Value::String(v) => Cow::Borrowed(v.as_str()),
313            Value::Array(v) => format!("{}", v).into(),
314        }
315    }
316}
317
318macro_rules! from_values {
319   (
320        $(
321            ($t:ty, $val:expr);
322        )+
323    ) => {
324        $(
325            impl From<$t> for Value {
326                fn from(t: $t) -> Self {
327                    $val(t)
328                }
329            }
330        )+
331    }
332}
333
334from_values!(
335    (bool, Value::Bool);
336    (i64, Value::I64);
337    (f64, Value::F64);
338    (StringValue, Value::String);
339);
340
341impl From<&'static str> for Value {
342    fn from(s: &'static str) -> Self {
343        Value::String(s.into())
344    }
345}
346
347impl From<String> for Value {
348    fn from(s: String) -> Self {
349        Value::String(s.into())
350    }
351}
352
353impl From<Arc<str>> for Value {
354    fn from(s: Arc<str>) -> Self {
355        Value::String(s.into())
356    }
357}
358
359impl From<Cow<'static, str>> for Value {
360    fn from(s: Cow<'static, str>) -> Self {
361        Value::String(s.into())
362    }
363}
364
365impl fmt::Display for Value {
366    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
367        match self {
368            Value::Bool(v) => v.fmt(fmt),
369            Value::I64(v) => v.fmt(fmt),
370            Value::F64(v) => v.fmt(fmt),
371            Value::String(v) => fmt.write_str(v.as_str()),
372            Value::Array(v) => v.fmt(fmt),
373        }
374    }
375}
376
377/// A key-value pair describing an attribute.
378#[derive(Clone, Debug, PartialEq)]
379#[non_exhaustive]
380pub struct KeyValue {
381    /// The attribute name
382    pub key: Key,
383
384    /// The attribute value
385    pub value: Value,
386}
387
388impl KeyValue {
389    /// Create a new `KeyValue` pair.
390    pub fn new<K, V>(key: K, value: V) -> Self
391    where
392        K: Into<Key>,
393        V: Into<Value>,
394    {
395        KeyValue {
396            key: key.into(),
397            value: value.into(),
398        }
399    }
400}
401
402/// Information about a library or crate providing instrumentation.
403///
404/// An instrumentation scope should be named to follow any naming conventions
405/// of the instrumented library (e.g. 'middleware' for a web framework).
406///
407/// See the [instrumentation libraries] spec for more information.
408///
409/// [instrumentation libraries]: https://github.com/open-telemetry/opentelemetry-specification/blob/v1.9.0/specification/overview.md#instrumentation-libraries
410#[derive(Debug, Default, Clone)]
411#[non_exhaustive]
412pub struct InstrumentationScope {
413    /// The library name.
414    ///
415    /// This should be the name of the crate providing the instrumentation.
416    name: Cow<'static, str>,
417
418    /// The library version.
419    version: Option<Cow<'static, str>>,
420
421    /// [Schema URL] used by this library.
422    ///
423    /// [Schema URL]: https://github.com/open-telemetry/opentelemetry-specification/blob/v1.9.0/specification/schemas/overview.md#schema-url
424    schema_url: Option<Cow<'static, str>>,
425
426    /// Specifies the instrumentation scope attributes to associate with emitted telemetry.
427    attributes: Vec<KeyValue>,
428}
429
430// Uniqueness for InstrumentationScope does not depend on attributes
431impl Eq for InstrumentationScope {}
432
433impl PartialEq for InstrumentationScope {
434    fn eq(&self, other: &Self) -> bool {
435        self.name == other.name
436            && self.version == other.version
437            && self.schema_url == other.schema_url
438    }
439}
440
441impl hash::Hash for InstrumentationScope {
442    fn hash<H: hash::Hasher>(&self, state: &mut H) {
443        self.name.hash(state);
444        self.version.hash(state);
445        self.schema_url.hash(state);
446    }
447}
448
449impl InstrumentationScope {
450    /// Create a new builder to create an [InstrumentationScope]
451    pub fn builder<T: Into<Cow<'static, str>>>(name: T) -> InstrumentationScopeBuilder {
452        InstrumentationScopeBuilder {
453            name: name.into(),
454            version: None,
455            schema_url: None,
456            attributes: None,
457        }
458    }
459
460    /// Returns the instrumentation library name.
461    #[inline]
462    pub fn name(&self) -> &str {
463        &self.name
464    }
465
466    /// Returns the instrumentation library version.
467    #[inline]
468    pub fn version(&self) -> Option<&str> {
469        self.version.as_deref()
470    }
471
472    /// Returns the [Schema URL] used by this library.
473    ///
474    /// [Schema URL]: https://github.com/open-telemetry/opentelemetry-specification/blob/v1.9.0/specification/schemas/overview.md#schema-url
475    #[inline]
476    pub fn schema_url(&self) -> Option<&str> {
477        self.schema_url.as_deref()
478    }
479
480    /// Returns the instrumentation scope attributes to associate with emitted telemetry.
481    #[inline]
482    pub fn attributes(&self) -> impl Iterator<Item = &KeyValue> {
483        self.attributes.iter()
484    }
485}
486
487/// Configuration options for [InstrumentationScope].
488///
489/// An instrumentation scope is a library or crate providing instrumentation.
490/// It should be named to follow any naming conventions of the instrumented
491/// library (e.g. 'middleware' for a web framework).
492///
493/// Apart from the name, all other fields are optional.
494///
495/// See the [instrumentation libraries] spec for more information.
496///
497/// [instrumentation libraries]: https://github.com/open-telemetry/opentelemetry-specification/blob/v1.9.0/specification/overview.md#instrumentation-libraries
498#[derive(Debug)]
499pub struct InstrumentationScopeBuilder {
500    name: Cow<'static, str>,
501    version: Option<Cow<'static, str>>,
502    schema_url: Option<Cow<'static, str>>,
503    attributes: Option<Vec<KeyValue>>,
504}
505
506impl InstrumentationScopeBuilder {
507    /// Configure the version for the instrumentation scope
508    ///
509    /// # Examples
510    ///
511    /// ```
512    /// let scope = opentelemetry::InstrumentationScope::builder("my-crate")
513    ///     .with_version("v0.1.0")
514    ///     .build();
515    /// ```
516    pub fn with_version(mut self, version: impl Into<Cow<'static, str>>) -> Self {
517        self.version = Some(version.into());
518        self
519    }
520
521    /// Configure the Schema URL for the instrumentation scope
522    ///
523    /// # Examples
524    ///
525    /// ```
526    /// let scope = opentelemetry::InstrumentationScope::builder("my-crate")
527    ///     .with_schema_url("https://opentelemetry.io/schemas/1.17.0")
528    ///     .build();
529    /// ```
530    pub fn with_schema_url(mut self, schema_url: impl Into<Cow<'static, str>>) -> Self {
531        self.schema_url = Some(schema_url.into());
532        self
533    }
534
535    /// Configure the attributes for the instrumentation scope
536    ///
537    /// # Examples
538    ///
539    /// ```
540    /// use opentelemetry::KeyValue;
541    ///
542    /// let scope = opentelemetry::InstrumentationScope::builder("my-crate")
543    ///     .with_attributes([KeyValue::new("k", "v")])
544    ///     .build();
545    /// ```
546    pub fn with_attributes<I>(mut self, attributes: I) -> Self
547    where
548        I: IntoIterator<Item = KeyValue>,
549    {
550        self.attributes = Some(attributes.into_iter().collect());
551        self
552    }
553
554    /// Create a new [InstrumentationScope] from this configuration
555    pub fn build(self) -> InstrumentationScope {
556        InstrumentationScope {
557            name: self.name,
558            version: self.version,
559            schema_url: self.schema_url,
560            attributes: self.attributes.unwrap_or_default(),
561        }
562    }
563}