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