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