opentelemetry/metrics/instruments/
mod.rs

1use gauge::{Gauge, ObservableGauge};
2
3use crate::metrics::Meter;
4use crate::KeyValue;
5use core::fmt;
6use std::borrow::Cow;
7use std::marker;
8
9use super::{
10    Counter, Histogram, InstrumentProvider, ObservableCounter, ObservableUpDownCounter,
11    UpDownCounter,
12};
13
14pub(super) mod counter;
15pub(super) mod gauge;
16pub(super) mod histogram;
17pub(super) mod up_down_counter;
18
19/// An SDK implemented instrument that records measurements via callback.
20pub trait AsyncInstrument<T>: Send + Sync {
21    /// Observes the state of the instrument.
22    ///
23    /// It is only valid to call this within a callback.
24    fn observe(&self, measurement: T, attributes: &[KeyValue]);
25}
26
27/// An SDK implemented instrument that records measurements synchronously.
28pub trait SyncInstrument<T>: Send + Sync {
29    /// Records a measurement synchronously.
30    fn measure(&self, measurement: T, attributes: &[KeyValue]);
31}
32
33/// Configuration for building a Histogram.
34#[non_exhaustive] // We expect to add more configuration fields in the future
35pub struct HistogramBuilder<'a, T> {
36    /// Instrument provider is used to create the instrument.
37    pub instrument_provider: &'a dyn InstrumentProvider,
38
39    /// Name of the Histogram.
40    pub name: Cow<'static, str>,
41
42    /// Description of the Histogram.
43    pub description: Option<Cow<'static, str>>,
44
45    /// Unit of the Histogram.
46    pub unit: Option<Cow<'static, str>>,
47
48    /// Bucket boundaries for the histogram.
49    pub boundaries: Option<Vec<f64>>,
50
51    // boundaries: Vec<T>,
52    _marker: marker::PhantomData<T>,
53}
54
55impl<'a, T> HistogramBuilder<'a, T> {
56    /// Create a new instrument builder
57    pub(crate) fn new(meter: &'a Meter, name: Cow<'static, str>) -> Self {
58        HistogramBuilder {
59            instrument_provider: meter.instrument_provider.as_ref(),
60            name,
61            description: None,
62            unit: None,
63            boundaries: None,
64            _marker: marker::PhantomData,
65        }
66    }
67
68    /// Set the description for this instrument
69    pub fn with_description<S: Into<Cow<'static, str>>>(mut self, description: S) -> Self {
70        self.description = Some(description.into());
71        self
72    }
73
74    /// Set the unit for this instrument.
75    ///
76    /// Unit is case sensitive(`kb` is not the same as `kB`).
77    ///
78    /// Unit must be:
79    /// - ASCII string
80    /// - No longer than 63 characters
81    pub fn with_unit<S: Into<Cow<'static, str>>>(mut self, unit: S) -> Self {
82        self.unit = Some(unit.into());
83        self
84    }
85
86    /// Set the boundaries for this histogram.
87    ///
88    /// Setting boundaries is optional. By default, the boundaries are set to:
89    ///
90    /// `[0.0, 5.0, 10.0, 25.0, 50.0, 75.0, 100.0, 250.0, 500.0, 750.0, 1000.0,
91    /// 2500.0, 5000.0, 7500.0, 10000.0]`
92    ///
93    /// # Notes
94    /// - Boundaries must not contain `f64::NAN`, `f64::INFINITY` or
95    ///   `f64::NEG_INFINITY`
96    /// - Values must be in strictly increasing order (e.g., each value must be
97    ///   greater than the previous).
98    /// - Boundaries must not contain duplicate values.
99    ///
100    /// If invalid boundaries are provided, the instrument will not report
101    /// measurements.
102    /// Providing an empty `vec![]` means no bucket information will be
103    /// calculated.
104    ///
105    /// # Warning
106    /// Using more buckets can improve the accuracy of percentile calculations in backends.
107    /// However, this comes at a cost, including increased memory, CPU, and network usage.
108    /// Choose the number of buckets carefully, considering your application's performance
109    /// and resource requirements.
110    pub fn with_boundaries(mut self, boundaries: Vec<f64>) -> Self {
111        self.boundaries = Some(boundaries);
112        self
113    }
114}
115
116impl HistogramBuilder<'_, Histogram<f64>> {
117    /// Creates a new instrument.
118    ///
119    /// Validates the instrument configuration and creates a new instrument. In
120    /// case of invalid configuration, a no-op instrument is returned
121    /// and an error is logged using internal logging.
122    pub fn build(self) -> Histogram<f64> {
123        self.instrument_provider.f64_histogram(self)
124    }
125}
126
127impl HistogramBuilder<'_, Histogram<u64>> {
128    /// Creates a new instrument.
129    ///
130    /// Validates the instrument configuration and creates a new instrument. In
131    /// case of invalid configuration, a no-op instrument is returned
132    /// and an error is logged using internal logging.
133    pub fn build(self) -> Histogram<u64> {
134        self.instrument_provider.u64_histogram(self)
135    }
136}
137
138/// Configuration for building a sync instrument.
139#[non_exhaustive] // We expect to add more configuration fields in the future
140pub struct InstrumentBuilder<'a, T> {
141    /// Instrument provider is used to create the instrument.
142    pub instrument_provider: &'a dyn InstrumentProvider,
143
144    /// Name of the instrument.
145    pub name: Cow<'static, str>,
146
147    /// Description of the instrument.
148    pub description: Option<Cow<'static, str>>,
149
150    /// Unit of the instrument.
151    pub unit: Option<Cow<'static, str>>,
152
153    _marker: marker::PhantomData<T>,
154}
155
156impl<'a, T> InstrumentBuilder<'a, T> {
157    /// Create a new instrument builder
158    pub(crate) fn new(meter: &'a Meter, name: Cow<'static, str>) -> Self {
159        InstrumentBuilder {
160            instrument_provider: meter.instrument_provider.as_ref(),
161            name,
162            description: None,
163            unit: None,
164            _marker: marker::PhantomData,
165        }
166    }
167
168    /// Set the description for this instrument
169    pub fn with_description<S: Into<Cow<'static, str>>>(mut self, description: S) -> Self {
170        self.description = Some(description.into());
171        self
172    }
173
174    /// Set the unit for this instrument.
175    ///
176    /// Unit is case sensitive(`kb` is not the same as `kB`).
177    ///
178    /// Unit must be:
179    /// - ASCII string
180    /// - No longer than 63 characters
181    pub fn with_unit<S: Into<Cow<'static, str>>>(mut self, unit: S) -> Self {
182        self.unit = Some(unit.into());
183        self
184    }
185}
186
187macro_rules! build_instrument {
188    ($name:ident, $inst:ty) => {
189        impl<'a> InstrumentBuilder<'a, $inst> {
190            #[doc = concat!("Validates the instrument configuration and creates a new `",  stringify!($inst), "`.")]
191            /// In case of invalid configuration, a no-op instrument is returned
192            /// and an error is logged using internal logging.
193            pub fn build(self) -> $inst {
194                self.instrument_provider.$name(self)
195            }
196        }
197    };
198}
199
200build_instrument!(u64_counter, Counter<u64>);
201build_instrument!(f64_counter, Counter<f64>);
202build_instrument!(u64_gauge, Gauge<u64>);
203build_instrument!(f64_gauge, Gauge<f64>);
204build_instrument!(i64_gauge, Gauge<i64>);
205build_instrument!(i64_up_down_counter, UpDownCounter<i64>);
206build_instrument!(f64_up_down_counter, UpDownCounter<f64>);
207
208impl<T> fmt::Debug for InstrumentBuilder<'_, T> {
209    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
210        f.debug_struct("InstrumentBuilder")
211            .field("name", &self.name)
212            .field("description", &self.description)
213            .field("unit", &self.unit)
214            .field("kind", &std::any::type_name::<T>())
215            .finish()
216    }
217}
218
219impl<T> fmt::Debug for HistogramBuilder<'_, T> {
220    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
221        f.debug_struct("HistogramBuilder")
222            .field("name", &self.name)
223            .field("description", &self.description)
224            .field("unit", &self.unit)
225            .field("boundaries", &self.boundaries)
226            .field(
227                "kind",
228                &format!("Histogram<{}>", &std::any::type_name::<T>()),
229            )
230            .finish()
231    }
232}
233
234/// A function registered with a [Meter] that makes observations for the
235/// instruments it is registered with.
236///
237/// The async instrument parameter is used to record measurement observations
238/// for these instruments.
239///
240/// The function needs to complete in a finite amount of time.
241pub type Callback<T> = Box<dyn Fn(&dyn AsyncInstrument<T>) + Send + Sync>;
242
243/// Configuration for building an async instrument.
244#[non_exhaustive] // We expect to add more configuration fields in the future
245pub struct AsyncInstrumentBuilder<'a, I, M> {
246    /// Instrument provider is used to create the instrument.
247    pub instrument_provider: &'a dyn InstrumentProvider,
248
249    /// Name of the instrument.
250    pub name: Cow<'static, str>,
251
252    /// Description of the instrument.
253    pub description: Option<Cow<'static, str>>,
254
255    /// Unit of the instrument.
256    pub unit: Option<Cow<'static, str>>,
257
258    /// Callbacks to be called for this instrument.
259    pub callbacks: Vec<Callback<M>>,
260
261    _inst: marker::PhantomData<I>,
262}
263
264impl<'a, I, M> AsyncInstrumentBuilder<'a, I, M> {
265    /// Create a new instrument builder
266    pub(crate) fn new(meter: &'a Meter, name: Cow<'static, str>) -> Self {
267        AsyncInstrumentBuilder {
268            instrument_provider: meter.instrument_provider.as_ref(),
269            name,
270            description: None,
271            unit: None,
272            _inst: marker::PhantomData,
273            callbacks: Vec::new(),
274        }
275    }
276
277    /// Set the description for this instrument
278    pub fn with_description<S: Into<Cow<'static, str>>>(mut self, description: S) -> Self {
279        self.description = Some(description.into());
280        self
281    }
282
283    /// Set the unit for this instrument.
284    ///
285    /// Unit is case sensitive(`kb` is not the same as `kB`).
286    ///
287    /// Unit must be:
288    /// - ASCII string
289    /// - No longer than 63 characters
290    pub fn with_unit<S: Into<Cow<'static, str>>>(mut self, unit: S) -> Self {
291        self.unit = Some(unit.into());
292        self
293    }
294
295    /// Set the callback to be called for this instrument.
296    pub fn with_callback<F>(mut self, callback: F) -> Self
297    where
298        F: Fn(&dyn AsyncInstrument<M>) + Send + Sync + 'static,
299    {
300        self.callbacks.push(Box::new(callback));
301        self
302    }
303}
304
305macro_rules! build_async_instrument {
306    ($name:ident, $inst:ty, $measurement:ty) => {
307        impl<'a> AsyncInstrumentBuilder<'a, $inst, $measurement> {
308            #[doc = concat!("Validates the instrument configuration and creates a new `",  stringify!($inst), "`.")]
309            /// In case of invalid configuration, a no-op instrument is returned
310            /// and an error is logged using internal logging.
311            pub fn build(self) -> $inst {
312                self.instrument_provider.$name(self)
313            }
314        }
315    };
316}
317
318build_async_instrument!(u64_observable_counter, ObservableCounter<u64>, u64);
319build_async_instrument!(f64_observable_counter, ObservableCounter<f64>, f64);
320build_async_instrument!(u64_observable_gauge, ObservableGauge<u64>, u64);
321build_async_instrument!(f64_observable_gauge, ObservableGauge<f64>, f64);
322build_async_instrument!(i64_observable_gauge, ObservableGauge<i64>, i64);
323build_async_instrument!(
324    i64_observable_up_down_counter,
325    ObservableUpDownCounter<i64>,
326    i64
327);
328build_async_instrument!(
329    f64_observable_up_down_counter,
330    ObservableUpDownCounter<f64>,
331    f64
332);
333
334impl<I, M> fmt::Debug for AsyncInstrumentBuilder<'_, I, M>
335where
336    I: AsyncInstrument<M>,
337{
338    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
339        f.debug_struct("InstrumentBuilder")
340            .field("name", &self.name)
341            .field("description", &self.description)
342            .field("unit", &self.unit)
343            .field("kind", &std::any::type_name::<I>())
344            .field("callbacks_len", &self.callbacks.len())
345            .finish()
346    }
347}