Skip to main content

glean_core/metrics/
denominator.rs

1// This Source Code Form is subject to the terms of the Mozilla Public
2// License, v. 2.0. If a copy of the MPL was not distributed with this
3// file, You can obtain one at https://mozilla.org/MPL/2.0/.
4
5use crate::common_metric_data::CommonMetricDataInternal;
6use crate::error_recording::{record_error, test_get_num_recorded_errors, ErrorType};
7use crate::metrics::CounterMetric;
8use crate::metrics::Metric;
9use crate::metrics::MetricType;
10use crate::metrics::RateMetric;
11use crate::Glean;
12use crate::{CommonMetricData, TestGetValue};
13
14/// A Denominator metric (a kind of count shared among Rate metrics).
15///
16/// Used to count things.
17/// The value can only be incremented, not decremented.
18// This is essentially a counter metric,
19// which additionally forwards increments to the denominator to a list of associated rates.
20// The numerator is incremented through the corresponding `NumeratorMetric`.
21#[derive(Clone, Debug)]
22pub struct DenominatorMetric {
23    counter: CounterMetric,
24    numerators: Vec<RateMetric>,
25}
26
27impl MetricType for DenominatorMetric {
28    fn meta(&self) -> &CommonMetricDataInternal {
29        self.counter.meta()
30    }
31}
32
33impl DenominatorMetric {
34    /// Creates a new denominator metric.
35    pub fn new(meta: CommonMetricData, numerators: Vec<CommonMetricData>) -> Self {
36        Self {
37            counter: CounterMetric::new(meta),
38            numerators: numerators.into_iter().map(RateMetric::new).collect(),
39        }
40    }
41
42    /// Increases the denominator by `amount`.
43    ///
44    /// # Arguments
45    ///
46    /// * `glean` - The Glean instance this metric belongs to.
47    /// * `amount` - The amount to increase by. Should be positive.
48    ///
49    /// ## Notes
50    ///
51    /// Logs an error if the `amount` is 0 or negative.
52    pub fn add(&self, amount: i32) {
53        let metric = self.clone();
54        crate::launch_with_glean(move |glean| metric.add_sync(glean, amount))
55    }
56
57    #[doc(hidden)]
58    pub fn add_sync(&self, glean: &Glean, amount: i32) {
59        if !self.should_record(glean) {
60            return;
61        }
62
63        if amount <= 0 {
64            record_error(
65                glean,
66                self.meta(),
67                ErrorType::InvalidValue,
68                format!("Added negative or zero value {}", amount),
69                None,
70            );
71            return;
72        }
73
74        for num in &self.numerators {
75            num.add_to_denominator_sync(glean, amount);
76        }
77
78        glean
79            .storage()
80            .record_with(glean, self.counter.meta(), |old_value| match old_value {
81                Some(Metric::Counter(old_value)) => {
82                    Metric::Counter(old_value.saturating_add(amount))
83                }
84                _ => Metric::Counter(amount),
85            })
86    }
87
88    #[doc(hidden)]
89    pub fn get_value<'a, S: Into<Option<&'a str>>>(
90        &self,
91        glean: &Glean,
92        ping_name: S,
93    ) -> Option<i32> {
94        let queried_ping_name = ping_name
95            .into()
96            .unwrap_or_else(|| &self.meta().inner.send_in_pings[0]);
97
98        match glean.storage().get_metric(self.meta(), queried_ping_name) {
99            Some(Metric::Counter(i)) => Some(i),
100            _ => None,
101        }
102    }
103
104    /// **Exported for test purposes.**
105    ///
106    /// Gets the number of recorded errors for the given metric and error type.
107    ///
108    /// # Arguments
109    ///
110    /// * `error` - The type of error
111    ///
112    /// # Returns
113    ///
114    /// The number of errors reported.
115    pub fn test_get_num_recorded_errors(&self, error: ErrorType) -> i32 {
116        crate::block_on_dispatcher();
117
118        crate::core::with_glean(|glean| {
119            test_get_num_recorded_errors(glean, self.meta(), error).unwrap_or(0)
120        })
121    }
122}
123
124impl TestGetValue for DenominatorMetric {
125    type Output = i32;
126
127    /// **Test-only API (exported for FFI purposes).**
128    ///
129    /// Gets the currently stored value as an integer.
130    ///
131    /// This doesn't clear the stored value.
132    ///
133    /// # Arguments
134    ///
135    /// * `ping_name` - the optional name of the ping to retrieve the metric
136    ///                 for. Defaults to the first value in `send_in_pings`.
137    ///
138    /// # Returns
139    ///
140    /// The stored value or `None` if nothing stored.
141    fn test_get_value(&self, ping_name: Option<String>) -> Option<i32> {
142        crate::block_on_dispatcher();
143        crate::core::with_glean(|glean| self.get_value(glean, ping_name.as_deref()))
144    }
145}