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::Glean;
13use crate::{CommonMetricData, TestGetValue};
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    #[doc(hidden)]
90    pub fn get_value<'a, S: Into<Option<&'a str>>>(
91        &self,
92        glean: &Glean,
93        ping_name: S,
94    ) -> Option<i32> {
95        let queried_ping_name = ping_name
96            .into()
97            .unwrap_or_else(|| &self.meta().inner.send_in_pings[0]);
98
99        match StorageManager.snapshot_metric_for_test(
100            glean.storage(),
101            queried_ping_name,
102            &self.meta().identifier(glean),
103            self.meta().inner.lifetime,
104        ) {
105            Some(Metric::Counter(i)) => Some(i),
106            _ => None,
107        }
108    }
109
110    /// **Exported for test purposes.**
111    ///
112    /// Gets the number of recorded errors for the given metric and error type.
113    ///
114    /// # Arguments
115    ///
116    /// * `error` - The type of error
117    ///
118    /// # Returns
119    ///
120    /// The number of errors reported.
121    pub fn test_get_num_recorded_errors(&self, error: ErrorType) -> i32 {
122        crate::block_on_dispatcher();
123
124        crate::core::with_glean(|glean| {
125            test_get_num_recorded_errors(glean, self.meta(), error).unwrap_or(0)
126        })
127    }
128}
129
130impl TestGetValue<i32> for DenominatorMetric {
131    /// **Test-only API (exported for FFI purposes).**
132    ///
133    /// Gets the currently stored value as an integer.
134    ///
135    /// This doesn't clear the stored value.
136    ///
137    /// # Arguments
138    ///
139    /// * `ping_name` - the optional name of the ping to retrieve the metric
140    ///                 for. Defaults to the first value in `send_in_pings`.
141    ///
142    /// # Returns
143    ///
144    /// The stored value or `None` if nothing stored.
145    fn test_get_value(&self, ping_name: Option<String>) -> Option<i32> {
146        crate::block_on_dispatcher();
147        crate::core::with_glean(|glean| self.get_value(glean, ping_name.as_deref()))
148    }
149}