aws_smithy_observability/
meter.rs

1/*
2 * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 * SPDX-License-Identifier: Apache-2.0
4 */
5
6//! Metrics are used to gain insight into the operational performance and health of a system in
7//! real time.
8
9use crate::instruments::{
10    AsyncInstrumentBuilder, AsyncMeasure, Histogram, InstrumentBuilder, MonotonicCounter,
11    UpDownCounter,
12};
13use crate::{attributes::Attributes, instruments::ProvideInstrument};
14use std::{borrow::Cow, fmt::Debug, sync::Arc};
15
16/// Provides named instances of [Meter].
17pub trait ProvideMeter: Send + Sync + Debug + 'static {
18    /// Get or create a named [Meter].
19    fn get_meter(&self, scope: &'static str, attributes: Option<&Attributes>) -> Meter;
20
21    /// Downcast to `Any` for type inspection.
22    ///
23    /// This method enables runtime type checking via downcasting to concrete types.
24    ///
25    /// Implementations must return `self` to enable proper downcasting:
26    ///
27    /// ```ignore
28    /// impl ProvideMeter for MyProvider {
29    ///     fn as_any(&self) -> &dyn std::any::Any {
30    ///         self
31    ///     }
32    /// }
33    /// ```
34    ///
35    /// # Example Usage
36    ///
37    /// ```ignore
38    /// use aws_smithy_observability::meter::ProvideMeter;
39    /// use aws_smithy_observability_otel::meter::OtelMeterProvider;
40    ///
41    /// fn check_provider_type(provider: &dyn ProvideMeter) {
42    ///     // Downcast to check if this is an OpenTelemetry provider
43    ///     if provider.as_any().downcast_ref::<OtelMeterProvider>().is_some() {
44    ///         println!("This is an OpenTelemetry provider");
45    ///     }
46    /// }
47    /// ```
48    fn as_any(&self) -> &dyn std::any::Any;
49}
50
51/// The entry point to creating instruments. A grouping of related metrics.
52#[derive(Clone)]
53pub struct Meter {
54    pub(crate) instrument_provider: Arc<dyn ProvideInstrument + Send + Sync>,
55}
56
57impl Meter {
58    /// Create a new [Meter] from an [ProvideInstrument]
59    pub fn new(instrument_provider: Arc<dyn ProvideInstrument + Send + Sync>) -> Self {
60        Meter {
61            instrument_provider,
62        }
63    }
64
65    /// Create a new Gauge.
66    #[allow(clippy::type_complexity)]
67    pub fn create_gauge<F>(
68        &self,
69        name: impl Into<Cow<'static, str>>,
70        callback: F,
71    ) -> AsyncInstrumentBuilder<'_, Arc<dyn AsyncMeasure<Value = f64>>, f64>
72    where
73        F: Fn(&dyn AsyncMeasure<Value = f64>) + Send + Sync + 'static,
74    {
75        AsyncInstrumentBuilder::new(self, name.into(), Arc::new(callback))
76    }
77
78    /// Create a new [UpDownCounter].
79    pub fn create_up_down_counter(
80        &self,
81        name: impl Into<Cow<'static, str>>,
82    ) -> InstrumentBuilder<'_, Arc<dyn UpDownCounter>> {
83        InstrumentBuilder::new(self, name.into())
84    }
85
86    /// Create a new AsyncUpDownCounter.
87    #[allow(clippy::type_complexity)]
88    pub fn create_async_up_down_counter<F>(
89        &self,
90        name: impl Into<Cow<'static, str>>,
91        callback: F,
92    ) -> AsyncInstrumentBuilder<'_, Arc<dyn AsyncMeasure<Value = i64>>, i64>
93    where
94        F: Fn(&dyn AsyncMeasure<Value = i64>) + Send + Sync + 'static,
95    {
96        AsyncInstrumentBuilder::new(self, name.into(), Arc::new(callback))
97    }
98
99    /// Create a new [MonotonicCounter].
100    pub fn create_monotonic_counter(
101        &self,
102        name: impl Into<Cow<'static, str>>,
103    ) -> InstrumentBuilder<'_, Arc<dyn MonotonicCounter>> {
104        InstrumentBuilder::new(self, name.into())
105    }
106
107    /// Create a new AsyncMonotonicCounter.
108    #[allow(clippy::type_complexity)]
109    pub fn create_async_monotonic_counter<F>(
110        &self,
111        name: impl Into<Cow<'static, str>>,
112        callback: F,
113    ) -> AsyncInstrumentBuilder<'_, Arc<dyn AsyncMeasure<Value = u64>>, u64>
114    where
115        F: Fn(&dyn AsyncMeasure<Value = u64>) + Send + Sync + 'static,
116    {
117        AsyncInstrumentBuilder::new(self, name.into(), Arc::new(callback))
118    }
119
120    /// Create a new [Histogram].
121    pub fn create_histogram(
122        &self,
123        name: impl Into<Cow<'static, str>>,
124    ) -> InstrumentBuilder<'_, Arc<dyn Histogram>> {
125        InstrumentBuilder::new(self, name.into())
126    }
127}
128
129#[cfg(test)]
130mod tests {
131    use super::*;
132    use crate::noop::NoopMeterProvider;
133
134    #[test]
135    fn test_as_any_downcasting_works() {
136        // Create a noop provider
137        let provider = NoopMeterProvider;
138
139        // Convert to trait object
140        let provider_dyn: &dyn ProvideMeter = &provider;
141
142        // Test that downcasting works when as_any() is properly implemented
143        let downcast_result = provider_dyn.as_any().downcast_ref::<NoopMeterProvider>();
144        assert!(
145            downcast_result.is_some(),
146            "Downcasting should succeed when as_any() returns self"
147        );
148    }
149
150    /// Custom meter provider for testing extensibility.
151    ///
152    /// This demonstrates that users can create their own meter provider implementations
153    /// and that all required types are publicly accessible.
154    #[derive(Debug)]
155    struct CustomMeterProvider;
156
157    impl ProvideMeter for CustomMeterProvider {
158        fn get_meter(&self, _scope: &'static str, _attributes: Option<&Attributes>) -> Meter {
159            // Create a meter using NoopMeterProvider's implementation
160            // This demonstrates that users can compose their own providers
161            let noop_provider = NoopMeterProvider;
162            noop_provider.get_meter(_scope, _attributes)
163        }
164
165        fn as_any(&self) -> &dyn std::any::Any {
166            self
167        }
168    }
169
170    #[test]
171    fn test_custom_provider_extensibility() {
172        // Create a custom provider
173        let custom = CustomMeterProvider;
174        let provider_ref: &dyn ProvideMeter = &custom;
175
176        // Verify custom provider doesn't downcast to NoopMeterProvider
177        assert!(
178            provider_ref
179                .as_any()
180                .downcast_ref::<NoopMeterProvider>()
181                .is_none(),
182            "Custom provider should not downcast to NoopMeterProvider"
183        );
184
185        // Verify custom provider downcasts to its own type
186        assert!(
187            provider_ref
188                .as_any()
189                .downcast_ref::<CustomMeterProvider>()
190                .is_some(),
191            "Custom provider should downcast to CustomMeterProvider"
192        );
193
194        // Verify the provider can create meters (demonstrates all required types are accessible)
195        let meter = custom.get_meter("test_scope", None);
196
197        // Verify we can create instruments from the meter
198        let _counter = meter.create_monotonic_counter("test_counter").build();
199        let _histogram = meter.create_histogram("test_histogram").build();
200        let _up_down = meter.create_up_down_counter("test_up_down").build();
201
202        // If we got here, all required types are publicly accessible
203    }
204}