aws_smithy_observability/
instruments.rs

1/*
2 * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 * SPDX-License-Identifier: Apache-2.0
4 */
5
6//! Instruments are used to record values for metrics.
7
8use std::{borrow::Cow, fmt::Debug, marker::PhantomData, sync::Arc};
9
10use crate::{meter::Meter, Attributes, Context};
11
12/// Configuration for building a sync instrument.
13#[non_exhaustive]
14pub struct InstrumentBuilder<'a, T> {
15    instrument_provider: &'a dyn ProvideInstrument,
16    name: Cow<'static, str>,
17    description: Option<Cow<'static, str>>,
18    units: Option<Cow<'static, str>>,
19    _phantom: PhantomData<T>,
20}
21
22impl<'a, T> InstrumentBuilder<'a, T> {
23    /// Create a new instrument builder
24    pub(crate) fn new(meter: &'a Meter, name: Cow<'static, str>) -> Self {
25        InstrumentBuilder {
26            instrument_provider: meter.instrument_provider.as_ref(),
27            name,
28            description: None,
29            units: None,
30            _phantom: PhantomData::<T>,
31        }
32    }
33
34    /// Get the name.
35    pub fn get_name(&self) -> &Cow<'static, str> {
36        &self.name
37    }
38
39    /// Set the description.
40    pub fn set_description(mut self, description: impl Into<Cow<'static, str>>) -> Self {
41        self.description = Some(description.into());
42        self
43    }
44
45    /// Get the description.
46    pub fn get_description(&self) -> &Option<Cow<'static, str>> {
47        &self.description
48    }
49
50    /// Set the units.
51    pub fn set_units(mut self, units: impl Into<Cow<'static, str>>) -> Self {
52        self.units = Some(units.into());
53        self
54    }
55
56    /// Get the units.
57    pub fn get_units(&self) -> &Option<Cow<'static, str>> {
58        &self.units
59    }
60}
61
62/// Takes in the name of function from [ProvideInstrument] and the type of instrument being created
63/// (ex: [Histogram]) and adds a `build` function for it.
64macro_rules! build_instrument {
65    ($name:ident, $instrument:ty) => {
66        impl<'a> InstrumentBuilder<'a, $instrument> {
67            #[doc = concat!("Create a new `",  stringify!($instrument), "`.")]
68            pub fn build(self) -> $instrument {
69                self.instrument_provider.$name(self)
70            }
71        }
72    };
73}
74
75build_instrument!(create_histogram, Arc<dyn Histogram>);
76build_instrument!(create_monotonic_counter, Arc<dyn MonotonicCounter>);
77build_instrument!(create_up_down_counter, Arc<dyn UpDownCounter>);
78
79/// Configuration for building an async instrument.
80#[non_exhaustive]
81pub struct AsyncInstrumentBuilder<'a, T, M> {
82    instrument_provider: &'a dyn ProvideInstrument,
83    name: Cow<'static, str>,
84    // Implementation note: I could not make the lifetimes work out in the impl ProvideInstrument
85    // in aws-smithy-observability-otel without making this field pub
86    /// The callback function for this AsyncInstrumentBuilder.
87    #[allow(clippy::type_complexity)]
88    pub callback: Arc<dyn Fn(&dyn AsyncMeasure<Value = M>) + Send + Sync>,
89    description: Option<Cow<'static, str>>,
90    units: Option<Cow<'static, str>>,
91    _phantom: PhantomData<T>,
92}
93
94#[allow(clippy::type_complexity)]
95impl<'a, T, M> AsyncInstrumentBuilder<'a, T, M> {
96    /// Create a new async instrument builder
97    pub(crate) fn new(
98        meter: &'a Meter,
99        name: Cow<'static, str>,
100        callback: Arc<dyn Fn(&dyn AsyncMeasure<Value = M>) + Send + Sync>,
101    ) -> Self {
102        AsyncInstrumentBuilder {
103            instrument_provider: meter.instrument_provider.as_ref(),
104            name,
105            callback,
106            description: None,
107            units: None,
108            _phantom: PhantomData::<T>,
109        }
110    }
111
112    /// Get the name.
113    pub fn get_name(&self) -> &Cow<'static, str> {
114        &self.name
115    }
116
117    /// Get the callback function.
118    pub fn get_callback(&self) -> Arc<dyn Fn(&dyn AsyncMeasure<Value = M>) + Send + Sync> {
119        self.callback.clone()
120    }
121
122    /// Set the description.
123    pub fn set_description(mut self, description: impl Into<Cow<'static, str>>) -> Self {
124        self.description = Some(description.into());
125        self
126    }
127
128    /// Get the description.
129    pub fn get_description(&self) -> &Option<Cow<'static, str>> {
130        &self.description
131    }
132
133    /// Set the units.
134    pub fn set_units(mut self, units: impl Into<Cow<'static, str>>) -> Self {
135        self.units = Some(units.into());
136        self
137    }
138
139    /// Get the units.
140    pub fn get_units(&self) -> &Option<Cow<'static, str>> {
141        &self.units
142    }
143}
144
145/// Takes in the name of function from [ProvideInstrument] and the type of instrument being created
146/// (ex: [AsyncMeasure]) and adds a `build` function for it.
147//TODO(observability): Can I derive the measurement from the Value of the instrument type or vice versa?
148macro_rules! build_async_instrument {
149    ($name:ident, $instrument:ty, $measurement:ty) => {
150        impl<'a> AsyncInstrumentBuilder<'a, $instrument, $measurement> {
151            #[doc = concat!("Create a new `",  stringify!($instrument), "`.")]
152            pub fn build(self) -> $instrument {
153                self.instrument_provider.$name(self)
154            }
155        }
156    };
157}
158
159build_async_instrument!(create_gauge, Arc<dyn AsyncMeasure<Value = f64>>, f64);
160build_async_instrument!(
161    create_async_up_down_counter,
162    Arc<dyn AsyncMeasure<Value = i64>>,
163    i64
164);
165build_async_instrument!(
166    create_async_monotonic_counter,
167    Arc<dyn AsyncMeasure<Value = u64>>,
168    u64
169);
170
171/// The entry point to creating instruments. A grouping of related metrics.
172pub trait ProvideInstrument: Send + Sync + Debug {
173    /// Create a new Gauge.
174    #[allow(clippy::type_complexity)]
175    fn create_gauge(
176        &self,
177        builder: AsyncInstrumentBuilder<'_, Arc<dyn AsyncMeasure<Value = f64>>, f64>,
178    ) -> Arc<dyn AsyncMeasure<Value = f64>>;
179
180    /// Create a new [UpDownCounter].
181    fn create_up_down_counter(
182        &self,
183        builder: InstrumentBuilder<'_, Arc<dyn UpDownCounter>>,
184    ) -> Arc<dyn UpDownCounter>;
185
186    /// Create a new AsyncUpDownCounter.
187    #[allow(clippy::type_complexity)]
188    fn create_async_up_down_counter(
189        &self,
190        builder: AsyncInstrumentBuilder<'_, Arc<dyn AsyncMeasure<Value = i64>>, i64>,
191    ) -> Arc<dyn AsyncMeasure<Value = i64>>;
192
193    /// Create a new [MonotonicCounter].
194    fn create_monotonic_counter(
195        &self,
196        builder: InstrumentBuilder<'_, Arc<dyn MonotonicCounter>>,
197    ) -> Arc<dyn MonotonicCounter>;
198
199    /// Create a new AsyncMonotonicCounter.
200    #[allow(clippy::type_complexity)]
201    fn create_async_monotonic_counter(
202        &self,
203        builder: AsyncInstrumentBuilder<'_, Arc<dyn AsyncMeasure<Value = u64>>, u64>,
204    ) -> Arc<dyn AsyncMeasure<Value = u64>>;
205
206    /// Create a new [Histogram].
207    fn create_histogram(
208        &self,
209        builder: InstrumentBuilder<'_, Arc<dyn Histogram>>,
210    ) -> Arc<dyn Histogram>;
211}
212
213/// Collects a set of events with an event count and sum for all events.
214pub trait Histogram: Send + Sync + Debug {
215    /// Record a value.
216    fn record(&self, value: f64, attributes: Option<&Attributes>, context: Option<&dyn Context>);
217}
218
219/// A counter that monotonically increases.
220pub trait MonotonicCounter: Send + Sync + Debug {
221    /// Increment a counter by a fixed amount.
222    fn add(&self, value: u64, attributes: Option<&Attributes>, context: Option<&dyn Context>);
223}
224
225/// A counter that can increase or decrease.
226pub trait UpDownCounter: Send + Sync + Debug {
227    /// Increment or decrement a counter by a fixed amount.
228    fn add(&self, value: i64, attributes: Option<&Attributes>, context: Option<&dyn Context>);
229}
230
231/// A measurement that can be taken asynchronously.
232pub trait AsyncMeasure: Send + Sync + Debug {
233    /// The type recorded by the measurement.
234    type Value;
235
236    /// Record a value
237    fn record(
238        &self,
239        value: Self::Value,
240        attributes: Option<&Attributes>,
241        context: Option<&dyn Context>,
242    );
243
244    /// Stop recording, unregister callback.
245    fn stop(&self);
246}