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
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
//! OpenTelemetry global `Tracer` and `Meter` singletons.
use crate::api::{self, KeyValue, SpanContext, Tracer};
use std::any::Any;
use std::sync::{Arc, RwLock};
use std::time::SystemTime;

/// Boxed span wraps a generic trait object so that `BoxedTracer`s
/// can return whichever type of span they were configured to use.
#[derive(Debug)]
pub struct BoxedSpan(Box<dyn api::Span>);

impl api::Span for BoxedSpan {
    /// Delegates to inner span.0
    fn add_event_with_timestamp(&mut self, message: String, timestamp: SystemTime) {
        self.0.add_event_with_timestamp(message, timestamp)
    }

    /// Delegates to inner span.
    fn add_link(&mut self, link: api::SpanContext) {
        self.0.add_link(link)
    }

    /// Delegates to inner span.
    fn get_context(&self) -> SpanContext {
        self.0.get_context()
    }

    /// Delegates to inner span.
    fn is_recording(&self) -> bool {
        self.0.is_recording()
    }

    /// Delegates to inner span.
    fn set_attribute(&mut self, attribute: KeyValue) {
        self.0.set_attribute(attribute)
    }

    /// Delegates to inner span.
    fn set_status(&mut self, status: String) {
        self.0.set_status(status)
    }

    /// Delegates to inner span.
    fn update_name(&mut self, new_name: String) {
        self.0.update_name(new_name)
    }

    /// Delegates to inner span.
    fn end(&mut self) {
        self.0.end()
    }

    /// Returns self as any
    fn as_any(&self) -> &dyn Any {
        self
    }
}

/// `GenericTracer` allows `BoxedTracer`'s to contain and use a `Tracer` trait object.
pub trait GenericTracer: Send + Sync {
    /// Create a new invalid span for use in cases where there are no active spans.
    fn invalid_boxed(&self) -> Box<dyn api::Span>;

    /// Returns a trait object so the underlying implementation can be swapped
    /// out at runtime.
    fn start_boxed(&self, name: &str, parent: Option<api::SpanContext>) -> Box<dyn api::Span>;

    /// Returns the currently active span as a BoxedSpan
    fn get_active_span_boxed(&self) -> Box<dyn api::Span>;

    /// Returns the currently active span as a BoxedSpan
    fn mark_span_as_active_boxed(&self, span: &dyn api::Span);

    /// Marks the current span as inactive
    fn mark_span_as_inactive_boxed(&self, span_id: u64);
}

impl<S: api::Span + 'static> GenericTracer for Box<dyn api::Tracer<Span = S>> {
    /// Create a new invalid span for use in cases where there are no active spans.
    fn invalid_boxed(&self) -> Box<dyn api::Span> {
        Box::new(self.invalid())
    }

    /// Returns a trait object so the underlying implementation can be swapped
    /// out at runtime.
    fn start_boxed(&self, name: &str, parent: Option<api::SpanContext>) -> Box<dyn api::Span> {
        Box::new(self.start(name, parent))
    }

    /// Returns the current active span.
    fn get_active_span_boxed(&self) -> Box<dyn api::Span> {
        Box::new(self.get_active_span())
    }

    /// Mark span as active.
    fn mark_span_as_active_boxed(&self, some_span: &dyn api::Span) {
        if let Some(span) = some_span.as_any().downcast_ref::<S>() {
            self.mark_span_as_active(span)
        };
    }

    /// Mark span as inactive.
    fn mark_span_as_inactive_boxed(&self, span_id: u64) {
        self.mark_span_as_inactive(span_id)
    }
}

impl Tracer for dyn GenericTracer {
    /// BoxedTracer returns a BoxedSpan so that it doesn't need a generic type parameter.
    type Span = BoxedSpan;

    /// Returns an invalid boxed span
    fn invalid(&self) -> Self::Span {
        BoxedSpan(self.invalid_boxed())
    }

    /// Starts a new boxed span.
    fn start(&self, name: &str, parent_span: Option<api::SpanContext>) -> Self::Span {
        BoxedSpan(self.start_boxed(name, parent_span))
    }

    /// Returns the current active span.
    fn get_active_span(&self) -> Self::Span {
        BoxedSpan(self.get_active_span_boxed())
    }

    /// Marks a given `Span` as active.
    fn mark_span_as_active(&self, span: &Self::Span) {
        self.mark_span_as_active_boxed(&(*span.0))
    }

    /// Marks a given `Span` as inactive.
    fn mark_span_as_inactive(&self, span_id: u64) {
        self.mark_span_as_inactive_boxed(span_id)
    }
}

/// BoxedTracer is an instance of a generic tracer that can be returned by the
/// global provider to represent.
#[allow(missing_debug_implementations)]
pub struct BoxedTracer(Box<dyn GenericTracer>);

impl api::Tracer for BoxedTracer {
    /// Global tracer uses `BoxedSpan`s so that it can be a global singleton,
    /// which is not possible if it takes generic type parameters.
    type Span = BoxedSpan;

    /// Returns a span with an invalid `SpanContext`.
    fn invalid(&self) -> Self::Span {
        self.0.invalid()
    }

    /// Starts a new `Span`.
    fn start(&self, name: &str, parent_span: Option<api::SpanContext>) -> Self::Span {
        self.0.start(name, parent_span)
    }

    /// Returns the current active span.
    fn get_active_span(&self) -> Self::Span {
        self.0.get_active_span()
    }

    /// Mark a given `Span` as active.
    fn mark_span_as_active(&self, span: &Self::Span) {
        self.0.mark_span_as_active(span)
    }

    /// Mark a given `Span` as inactive.
    fn mark_span_as_inactive(&self, span_id: u64) {
        self.0.mark_span_as_inactive(span_id)
    }
}

/// `GenericProvider` allows `GlobalProvider`'s to contain and use a `Provider` trait object.
pub trait GenericProvider: Send + Sync {
    /// Creates a named tracer instance that is a trait object through the underlying `Provider`.
    fn get_tracer_boxed(&self, name: &'static str) -> Box<dyn GenericTracer>;
}

impl api::Provider for dyn GenericProvider {
    /// Tracer is a boxed tracer so it can wrap any implementation of `Tracer`.
    type Tracer = BoxedTracer;

    /// Find or create a named instance of `BoxedTracer`.
    fn get_tracer(&self, name: &'static str) -> Self::Tracer {
        BoxedTracer(self.get_tracer_boxed(name))
    }
}

impl<T, S> GenericProvider for Box<dyn api::Provider<Tracer = T>>
where
    S: api::Span + 'static,
    T: api::Tracer<Span = S> + 'static,
{
    /// Return a boxed generic tracer, used
    fn get_tracer_boxed(&self, name: &'static str) -> Box<dyn GenericTracer> {
        // Has to first be boxed to impl `GenericTracer`
        let generic_tracer: Box<dyn api::Tracer<Span = S>> = Box::new(self.get_tracer(name));
        // Then boxed again to impl `Box<dyn GenericTracer>`.
        Box::new(generic_tracer)
    }
}

/// GlobalProvider maintains a global singleton that allows any thread to access
/// the same generic `Provider` implementation.
#[allow(missing_debug_implementations)]
pub struct GlobalProvider {
    provider: Box<dyn GenericProvider>,
}

impl GlobalProvider {
    /// Create a new GlobalProvider instance from a struct that implements `Provider`.
    fn new<P, T, S>(provider: P) -> Self
    where
        S: api::Span + 'static,
        T: api::Tracer<Span = S> + 'static,
        P: api::Provider<Tracer = T> + 'static,
    {
        // Has to first be boxed to impl `GenericProvider`.
        let generic_provider: Box<dyn api::Provider<Tracer = T>> = Box::new(provider);

        // Then boxed again to for `Box<dyn GenericProvider>`.
        GlobalProvider {
            provider: Box::new(generic_provider),
        }
    }
}

impl api::Provider for GlobalProvider {
    type Tracer = BoxedTracer;

    /// Find or create a named tracer using the global provider.
    fn get_tracer(&self, name: &'static str) -> Self::Tracer {
        self.provider.get_tracer(name)
    }
}

lazy_static::lazy_static! {
    /// The global `Tracer` singleton.
    static ref GLOBAL_TRACER_PROVIDER: RwLock<Arc<GlobalProvider>> = RwLock::new(Arc::new(GlobalProvider::new(api::NoopProvider {})));
}

/// Returns a reference to the global `Provider`
pub fn trace_provider() -> Arc<GlobalProvider> {
    GLOBAL_TRACER_PROVIDER
        .read()
        .expect("GLOBAL_TRACER_PROVIDER RwLock poisoned")
        .clone()
}

/// Assigns the global `Tracer`
pub fn set_provider<P, T, S>(new_provider: P)
where
    S: api::Span + 'static,
    T: api::Tracer<Span = S> + 'static,
    P: api::Provider<Tracer = T> + 'static,
{
    let mut global_provider = GLOBAL_TRACER_PROVIDER
        .write()
        .expect("GLOBAL_TRACER_PROVIDER RwLock poisoned");
    *global_provider = Arc::new(GlobalProvider::new(new_provider));
}

/// Returns `NoopMeter` for now
pub fn global_meter() -> crate::api::NoopMeter {
    crate::api::NoopMeter {}
}