1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
use crate::metrics::{self, Meter, MeterProvider};
use crate::KeyValue;
use core::fmt;
use once_cell::sync::Lazy;
use std::{
    borrow::Cow,
    sync::{Arc, RwLock},
};

/// The global `Meter` provider singleton.
static GLOBAL_METER_PROVIDER: Lazy<RwLock<GlobalMeterProvider>> = Lazy::new(|| {
    RwLock::new(GlobalMeterProvider::new(
        metrics::noop::NoopMeterProvider::new(),
    ))
});

/// Allows a specific [MeterProvider] to be used generically by the
/// [GlobalMeterProvider] by mirroring the interface and boxing the return types.
pub trait ObjectSafeMeterProvider {
    /// Creates a versioned named meter instance that is a trait object through the underlying
    /// [MeterProvider].
    fn versioned_meter_cow(
        &self,
        name: Cow<'static, str>,
        version: Option<Cow<'static, str>>,
        schema_url: Option<Cow<'static, str>>,
        attributes: Option<Vec<KeyValue>>,
    ) -> Meter;
}

impl<P> ObjectSafeMeterProvider for P
where
    P: MeterProvider,
{
    /// Return a versioned boxed tracer
    fn versioned_meter_cow(
        &self,
        name: Cow<'static, str>,
        version: Option<Cow<'static, str>>,
        schema_url: Option<Cow<'static, str>>,
        attributes: Option<Vec<KeyValue>>,
    ) -> Meter {
        self.versioned_meter(name, version, schema_url, attributes)
    }
}

/// Represents the globally configured [`MeterProvider`] instance for this
/// application.
#[derive(Clone)]
pub struct GlobalMeterProvider {
    provider: Arc<dyn ObjectSafeMeterProvider + Send + Sync>,
}

impl fmt::Debug for GlobalMeterProvider {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_struct("GlobalMeterProvider").finish()
    }
}

impl MeterProvider for GlobalMeterProvider {
    fn versioned_meter(
        &self,
        name: impl Into<Cow<'static, str>>,
        version: Option<impl Into<Cow<'static, str>>>,
        schema_url: Option<impl Into<Cow<'static, str>>>,
        attributes: Option<Vec<KeyValue>>,
    ) -> Meter {
        self.provider.versioned_meter_cow(
            name.into(),
            version.map(Into::into),
            schema_url.map(Into::into),
            attributes,
        )
    }
}

impl GlobalMeterProvider {
    /// Create a new global meter provider
    pub fn new<P>(provider: P) -> Self
    where
        P: MeterProvider + Send + Sync + 'static,
    {
        GlobalMeterProvider {
            provider: Arc::new(provider),
        }
    }
}

/// Sets the given [`MeterProvider`] instance as the current global meter
/// provider.
pub fn set_meter_provider<P>(new_provider: P)
where
    P: metrics::MeterProvider + Send + Sync + 'static,
{
    let mut global_provider = GLOBAL_METER_PROVIDER
        .write()
        .expect("GLOBAL_METER_PROVIDER RwLock poisoned");
    *global_provider = GlobalMeterProvider::new(new_provider);
}

/// Returns an instance of the currently configured global [`MeterProvider`]
/// through [`GlobalMeterProvider`].
pub fn meter_provider() -> GlobalMeterProvider {
    GLOBAL_METER_PROVIDER
        .read()
        .expect("GLOBAL_METER_PROVIDER RwLock poisoned")
        .clone()
}

/// Creates a named [`Meter`] via the configured [`GlobalMeterProvider`].
///
/// If the name is an empty string, the provider will use a default name.
///
/// This is a more convenient way of expressing `global::meter_provider().versioned_meter(name, None, None, None)`.
pub fn meter(name: impl Into<Cow<'static, str>>) -> Meter {
    meter_provider().meter(name.into())
}

/// Creates a [`Meter`] with the name, version and schema url.
///
/// - name SHOULD uniquely identify the instrumentation scope, such as the instrumentation library (e.g. io.opentelemetry.contrib.mongodb), package, module or class name.
/// - version specifies the version of the instrumentation scope if the scope has a version
/// - schema url specifies the Schema URL that should be recorded in the emitted telemetry.
///
/// This is a convenient way of `global::meter_provider().versioned_meter(...)`
///
/// # Example
///
/// ```
/// use opentelemetry_api::global::meter_with_version;
/// use opentelemetry_api::KeyValue;
///
/// let meter = meter_with_version(
///     "io.opentelemetry",
///     Some("0.17"),
///     Some("https://opentelemetry.io/schemas/1.2.0"),
///     Some(vec![KeyValue::new("key", "value")]),
/// );
/// ```
pub fn meter_with_version(
    name: impl Into<Cow<'static, str>>,
    version: Option<impl Into<Cow<'static, str>>>,
    schema_url: Option<impl Into<Cow<'static, str>>>,
    attributes: Option<Vec<KeyValue>>,
) -> Meter {
    meter_provider().versioned_meter(
        name.into(),
        version.map(Into::into),
        schema_url.map(Into::into),
        attributes,
    )
}