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