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
/*
 * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
 * SPDX-License-Identifier: Apache-2.0
 */

//! Utilities for interacting with the currently set `GlobalTelemetryProvider`

use once_cell::sync::Lazy;
use std::{
    mem,
    sync::{Arc, RwLock},
};

use crate::provider::{GlobalTelemetryProvider, TelemetryProvider};

// Statically store the global provider
static GLOBAL_TELEMETRY_PROVIDER: Lazy<RwLock<GlobalTelemetryProvider>> =
    Lazy::new(|| RwLock::new(GlobalTelemetryProvider::new(TelemetryProvider::default())));

/// Set the current global [TelemetryProvider].
///
/// This is meant to be run once at the beginning of an application. It will panic if two threads
/// attempt to call it at the same time.
pub fn set_telemetry_provider(new_provider: TelemetryProvider) {
    // TODO(smithyObservability): would probably be nicer to return a Result here, but the Guard held by the error from
    // .try_write is not Send so I struggled to build an ObservabilityError from it
    let mut old_provider = GLOBAL_TELEMETRY_PROVIDER
        .try_write()
        .expect("GLOBAL_TELEMETRY_PROVIDER RwLock Poisoned");

    let new_global_provider = GlobalTelemetryProvider::new(new_provider);

    let _ = mem::replace(&mut *old_provider, new_global_provider);
}

/// Get an [Arc] reference to the current global [TelemetryProvider].
///
/// This can panic if called when another thread is calling [set_telemetry_provider].
pub fn get_telemetry_provider() -> Arc<TelemetryProvider> {
    // TODO(smithyObservability): would probably be nicer to return a Result here, but the Guard held by the error from
    // .try_read is not Send so I struggled to build an ObservabilityError from it
    GLOBAL_TELEMETRY_PROVIDER
        .try_read()
        .expect("GLOBAL_TELEMETRY_PROVIDER RwLock Poisoned")
        .telemetry_provider()
        .clone()
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::provider::TelemetryProvider;
    use serial_test::serial;

    // Note: the tests in this module are run serially to prevent them from stepping on each other and poisoning the
    // RwLock holding the GlobalTelemetryProvider
    #[test]
    #[serial]
    fn can_set_global_telemetry_provider() {
        let my_provider = TelemetryProvider::default();

        // Set the new counter and get a reference to the old one
        set_telemetry_provider(my_provider);
    }

    #[test]
    #[serial]
    fn can_get_global_telemetry_provider() {
        let curr_provider = get_telemetry_provider();

        // Use the global provider to create an instrument and record a value with it
        let curr_mp = curr_provider.meter_provider();
        let curr_meter = curr_mp.get_meter("TestMeter", None);
        let instrument = curr_meter.create_monotonic_counter("TestMonoCounter".into(), None, None);
        instrument.add(4, None, None);
    }
}