glean_core/metrics/
mod.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
5//! The different metric types supported by the Glean SDK to handle data.
6
7use std::collections::HashMap;
8use std::sync::atomic::Ordering;
9
10use chrono::{DateTime, FixedOffset};
11use malloc_size_of::MallocSizeOf;
12use serde::{Deserialize, Serialize};
13use serde_json::json;
14pub use serde_json::Value as JsonValue;
15
16mod boolean;
17mod counter;
18mod custom_distribution;
19mod datetime;
20mod denominator;
21pub(crate) mod dual_labeled_counter;
22mod event;
23mod experiment;
24pub(crate) mod labeled;
25mod memory_distribution;
26mod memory_unit;
27mod numerator;
28mod object;
29mod ping;
30mod quantity;
31mod rate;
32mod recorded_experiment;
33mod remote_settings_config;
34mod string;
35mod string_list;
36mod text;
37mod time_unit;
38mod timespan;
39mod timing_distribution;
40mod url;
41mod uuid;
42
43use crate::common_metric_data::CommonMetricDataInternal;
44pub use crate::common_metric_data::DynamicLabelType;
45pub use crate::event_database::RecordedEvent;
46use crate::histogram::{Functional, Histogram, PrecomputedExponential, PrecomputedLinear};
47pub use crate::metrics::datetime::Datetime;
48use crate::util::get_iso_time_string;
49use crate::Glean;
50
51pub use self::boolean::BooleanMetric;
52pub use self::counter::CounterMetric;
53pub use self::custom_distribution::{CustomDistributionMetric, LocalCustomDistribution};
54pub use self::datetime::DatetimeMetric;
55pub use self::denominator::DenominatorMetric;
56pub use self::dual_labeled_counter::DualLabeledCounterMetric;
57pub use self::event::EventMetric;
58pub(crate) use self::experiment::ExperimentMetric;
59pub use self::labeled::{
60    LabeledBoolean, LabeledCounter, LabeledCustomDistribution, LabeledMemoryDistribution,
61    LabeledMetric, LabeledMetricData, LabeledQuantity, LabeledString, LabeledTimingDistribution,
62};
63pub use self::memory_distribution::{LocalMemoryDistribution, MemoryDistributionMetric};
64pub use self::memory_unit::MemoryUnit;
65pub use self::numerator::NumeratorMetric;
66pub use self::object::ObjectMetric;
67pub use self::ping::PingType;
68pub use self::quantity::QuantityMetric;
69pub use self::rate::{Rate, RateMetric};
70pub use self::string::StringMetric;
71pub use self::string_list::StringListMetric;
72pub use self::text::TextMetric;
73pub use self::time_unit::TimeUnit;
74pub use self::timespan::TimespanMetric;
75pub use self::timing_distribution::LocalTimingDistribution;
76pub use self::timing_distribution::TimerId;
77pub use self::timing_distribution::TimingDistributionMetric;
78pub use self::url::UrlMetric;
79pub use self::uuid::UuidMetric;
80pub use crate::histogram::HistogramType;
81pub use recorded_experiment::RecordedExperiment;
82
83pub use self::remote_settings_config::RemoteSettingsConfig;
84
85/// A snapshot of all buckets and the accumulated sum of a distribution.
86//
87// Note: Be careful when changing this structure.
88// The serialized form ends up in the ping payload.
89// New fields might require to be skipped on serialization.
90#[derive(Debug, Serialize, PartialEq)]
91pub struct DistributionData {
92    /// A map containig the bucket index mapped to the accumulated count.
93    ///
94    /// This can contain buckets with a count of `0`.
95    pub values: HashMap<i64, i64>,
96
97    /// The accumulated sum of all the samples in the distribution.
98    pub sum: i64,
99
100    /// The total number of entries in the distribution.
101    #[serde(skip)]
102    pub count: i64,
103}
104
105/// The available metrics.
106///
107/// This is the in-memory and persisted layout of a metric.
108///
109/// ## Note
110///
111/// The order of metrics in this enum is important, as it is used for serialization.
112/// Do not reorder the variants.
113///
114/// **Any new metric must be added at the end.**
115#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
116pub enum Metric {
117    /// A boolean metric. See [`BooleanMetric`] for more information.
118    Boolean(bool),
119    /// A counter metric. See [`CounterMetric`] for more information.
120    Counter(i32),
121    /// A custom distribution with precomputed exponential bucketing.
122    /// See [`CustomDistributionMetric`] for more information.
123    CustomDistributionExponential(Histogram<PrecomputedExponential>),
124    /// A custom distribution with precomputed linear bucketing.
125    /// See [`CustomDistributionMetric`] for more information.
126    CustomDistributionLinear(Histogram<PrecomputedLinear>),
127    /// A datetime metric. See [`DatetimeMetric`] for more information.
128    Datetime(DateTime<FixedOffset>, TimeUnit),
129    /// An experiment metric. See `ExperimentMetric` for more information.
130    Experiment(recorded_experiment::RecordedExperiment),
131    /// A quantity metric. See [`QuantityMetric`] for more information.
132    Quantity(i64),
133    /// A string metric. See [`StringMetric`] for more information.
134    String(String),
135    /// A string list metric. See [`StringListMetric`] for more information.
136    StringList(Vec<String>),
137    /// A UUID metric. See [`UuidMetric`] for more information.
138    Uuid(String),
139    /// A timespan metric. See [`TimespanMetric`] for more information.
140    Timespan(std::time::Duration, TimeUnit),
141    /// A timing distribution. See [`TimingDistributionMetric`] for more information.
142    TimingDistribution(Histogram<Functional>),
143    /// A memory distribution. See [`MemoryDistributionMetric`] for more information.
144    MemoryDistribution(Histogram<Functional>),
145    /// **DEPRECATED**: A JWE metric..
146    /// Note: This variant MUST NOT be removed to avoid backwards-incompatible changes to the
147    /// serialization. This type has no underlying implementation anymore.
148    Jwe(String),
149    /// A rate metric. See [`RateMetric`] for more information.
150    Rate(i32, i32),
151    /// A URL metric. See [`UrlMetric`] for more information.
152    Url(String),
153    /// A Text metric. See [`TextMetric`] for more information.
154    Text(String),
155    /// An Object metric. See [`ObjectMetric`] for more information.
156    Object(String),
157}
158
159impl MallocSizeOf for Metric {
160    fn size_of(&self, ops: &mut malloc_size_of::MallocSizeOfOps) -> usize {
161        match self {
162            Metric::Boolean(m) => m.size_of(ops),
163            Metric::Counter(m) => m.size_of(ops),
164            // Custom distributions are in the same section, no matter what bucketing.
165            Metric::CustomDistributionExponential(m) => m.size_of(ops),
166            Metric::CustomDistributionLinear(m) => m.size_of(ops),
167            Metric::Datetime(_a, b) => b.size_of(ops),
168            Metric::Experiment(m) => m.size_of(ops),
169            Metric::Quantity(m) => m.size_of(ops),
170            Metric::Rate(a, b) => a.size_of(ops) + b.size_of(ops),
171            Metric::String(m) => m.size_of(ops),
172            Metric::StringList(m) => m.size_of(ops),
173            Metric::Timespan(a, b) => a.size_of(ops) + b.size_of(ops),
174            Metric::TimingDistribution(m) => m.size_of(ops),
175            Metric::Url(m) => m.size_of(ops),
176            Metric::Uuid(m) => m.size_of(ops),
177            Metric::MemoryDistribution(m) => m.size_of(ops),
178            Metric::Jwe(m) => m.size_of(ops),
179            Metric::Text(m) => m.size_of(ops),
180            Metric::Object(m) => m.size_of(ops),
181        }
182    }
183}
184
185/// A [`MetricType`] describes common behavior across all metrics.
186pub trait MetricType {
187    /// Access the stored metadata
188    fn meta(&self) -> &CommonMetricDataInternal;
189
190    /// Create a new metric from this with a new name.
191    fn with_name(&self, _name: String) -> Self
192    where
193        Self: Sized,
194    {
195        unimplemented!()
196    }
197
198    /// Create a new metric from this with a specific label.
199    fn with_dynamic_label(&self, _label: DynamicLabelType) -> Self
200    where
201        Self: Sized,
202    {
203        unimplemented!()
204    }
205
206    /// Whether this metric should currently be recorded
207    ///
208    /// This depends on the metrics own state, as determined by its metadata,
209    /// and whether upload is enabled on the Glean object.
210    fn should_record(&self, glean: &Glean) -> bool {
211        // Technically nothing prevents multiple calls to should_record() to run in parallel,
212        // meaning both are reading self.meta().disabled and later writing it. In between it can
213        // also read remote_settings_config, which also could be modified in between those 2 reads.
214        // This means we could write the wrong remote_settings_epoch | current_disabled value. All in all
215        // at worst we would see that metric enabled/disabled wrongly once.
216        // But since everything is tunneled through the dispatcher, this should never ever happen.
217
218        // Get the current disabled field from the metric metadata, including
219        // the encoded remote_settings epoch
220        let disabled_field = self.meta().disabled.load(Ordering::Relaxed);
221        // Grab the epoch from the upper nibble
222        let epoch = disabled_field >> 4;
223        // Get the disabled flag from the lower nibble
224        let disabled = disabled_field & 0xF;
225        // Get the current remote_settings epoch to see if we need to bother with the
226        // more expensive HashMap lookup
227        let remote_settings_epoch = glean.remote_settings_epoch.load(Ordering::Acquire);
228        if epoch == remote_settings_epoch {
229            return disabled == 0;
230        }
231        // The epoch's didn't match so we need to look up the disabled flag
232        // by the base_identifier from the in-memory HashMap
233        let remote_settings_config = &glean.remote_settings_config.lock().unwrap();
234        // Get the value from the remote configuration if it is there, otherwise return the default value.
235        let current_disabled = {
236            let base_id = self.meta().base_identifier();
237            let identifier = base_id
238                .split_once('/')
239                .map(|split| split.0)
240                .unwrap_or(&base_id);
241            // NOTE: The `!` preceding the `*is_enabled` is important for inverting the logic since the
242            // underlying property in the metrics.yaml is `disabled` and the outward API is treating it as
243            // if it were `enabled` to make it easier to understand.
244
245            if !remote_settings_config.metrics_enabled.is_empty() {
246                if let Some(is_enabled) = remote_settings_config.metrics_enabled.get(identifier) {
247                    u8::from(!*is_enabled)
248                } else {
249                    u8::from(self.meta().inner.disabled)
250                }
251            } else {
252                u8::from(self.meta().inner.disabled)
253            }
254        };
255
256        // Re-encode the epoch and enabled status and update the metadata
257        let new_disabled = (remote_settings_epoch << 4) | (current_disabled & 0xF);
258        self.meta().disabled.store(new_disabled, Ordering::Relaxed);
259
260        // Return a boolean indicating whether or not the metric should be recorded
261        current_disabled == 0
262    }
263}
264
265/// A [`MetricIdentifier`] describes an interface for retrieving an
266/// identifier (category, name, label) for a metric
267pub trait MetricIdentifier<'a> {
268    /// Retrieve the category, name and (maybe) label of the metric
269    fn get_identifiers(&'a self) -> (&'a str, &'a str, Option<&'a str>);
270}
271
272/// [`TestGetValue`] describes an interface for retrieving the value for a given metric
273pub trait TestGetValue<T> {
274    /// **Test-only API (exported for FFI purposes).**
275    ///
276    /// Returns the currently stored value of the appropriate type for the given metric.
277    ///
278    /// This doesn't clear the stored value.
279    ///
280    /// # Arguments
281    ///
282    /// * `ping_name` - the optional name of the ping to retrieve the metric
283    ///                 for. Defaults to the first value in `send_in_pings`.
284    ///
285    /// # Returns
286    ///
287    /// The stored value or `None` if nothing stored.
288    fn test_get_value(&self, ping_name: Option<String>) -> Option<T>;
289}
290
291// Provide a blanket implementation for MetricIdentifier for all the types
292// that implement MetricType.
293impl<'a, T> MetricIdentifier<'a> for T
294where
295    T: MetricType,
296{
297    fn get_identifiers(&'a self) -> (&'a str, &'a str, Option<&'a str>) {
298        let meta = &self.meta().inner;
299        (&meta.category, &meta.name, meta.dynamic_label.as_deref())
300    }
301}
302
303impl Metric {
304    /// Gets the ping section the metric fits into.
305    ///
306    /// This determines the section of the ping to place the metric data in when
307    /// assembling the ping payload.
308    pub fn ping_section(&self) -> &'static str {
309        match self {
310            Metric::Boolean(_) => "boolean",
311            Metric::Counter(_) => "counter",
312            // Custom distributions are in the same section, no matter what bucketing.
313            Metric::CustomDistributionExponential(_) => "custom_distribution",
314            Metric::CustomDistributionLinear(_) => "custom_distribution",
315            Metric::Datetime(_, _) => "datetime",
316            Metric::Experiment(_) => panic!("Experiments should not be serialized through this"),
317            Metric::Quantity(_) => "quantity",
318            Metric::Rate(..) => "rate",
319            Metric::String(_) => "string",
320            Metric::StringList(_) => "string_list",
321            Metric::Timespan(..) => "timespan",
322            Metric::TimingDistribution(_) => "timing_distribution",
323            Metric::Url(_) => "url",
324            Metric::Uuid(_) => "uuid",
325            Metric::MemoryDistribution(_) => "memory_distribution",
326            Metric::Jwe(_) => "jwe",
327            Metric::Text(_) => "text",
328            Metric::Object(_) => "object",
329        }
330    }
331
332    /// The JSON representation of the metric's data
333    pub fn as_json(&self) -> JsonValue {
334        match self {
335            Metric::Boolean(b) => json!(b),
336            Metric::Counter(c) => json!(c),
337            Metric::CustomDistributionExponential(hist) => {
338                json!(custom_distribution::snapshot(hist))
339            }
340            Metric::CustomDistributionLinear(hist) => json!(custom_distribution::snapshot(hist)),
341            Metric::Datetime(d, time_unit) => json!(get_iso_time_string(*d, *time_unit)),
342            Metric::Experiment(e) => e.as_json(),
343            Metric::Quantity(q) => json!(q),
344            Metric::Rate(num, den) => {
345                json!({"numerator": num, "denominator": den})
346            }
347            Metric::String(s) => json!(s),
348            Metric::StringList(v) => json!(v),
349            Metric::Timespan(time, time_unit) => {
350                json!({"value": time_unit.duration_convert(*time), "time_unit": time_unit})
351            }
352            Metric::TimingDistribution(hist) => json!(timing_distribution::snapshot(hist)),
353            Metric::Url(s) => json!(s),
354            Metric::Uuid(s) => json!(s),
355            Metric::MemoryDistribution(hist) => json!(memory_distribution::snapshot(hist)),
356            Metric::Jwe(s) => json!(s),
357            Metric::Text(s) => json!(s),
358            Metric::Object(s) => {
359                serde_json::from_str(s).expect("object storage should have been json")
360            }
361        }
362    }
363}
364
365macro_rules! impl_malloc_size_of_for_metric {
366    ($ty:ident) => {
367        impl ::malloc_size_of::MallocSizeOf for $ty {
368            fn size_of(&self, ops: &mut malloc_size_of::MallocSizeOfOps) -> usize {
369                // Note: `meta` is likely s behind an `Arc`.
370                // `size_of` should only be called from a single thread to avoid double-counting.
371                self.meta().size_of(ops)
372            }
373        }
374    };
375}
376
377impl_malloc_size_of_for_metric!(BooleanMetric);
378impl_malloc_size_of_for_metric!(CounterMetric);
379impl_malloc_size_of_for_metric!(CustomDistributionMetric);
380impl_malloc_size_of_for_metric!(DatetimeMetric);
381impl_malloc_size_of_for_metric!(DenominatorMetric);
382impl_malloc_size_of_for_metric!(EventMetric);
383impl_malloc_size_of_for_metric!(ExperimentMetric);
384impl_malloc_size_of_for_metric!(MemoryDistributionMetric);
385impl_malloc_size_of_for_metric!(NumeratorMetric);
386impl_malloc_size_of_for_metric!(ObjectMetric);
387impl_malloc_size_of_for_metric!(QuantityMetric);
388impl_malloc_size_of_for_metric!(RateMetric);
389impl_malloc_size_of_for_metric!(StringMetric);
390impl_malloc_size_of_for_metric!(StringListMetric);
391impl_malloc_size_of_for_metric!(TextMetric);
392impl_malloc_size_of_for_metric!(TimespanMetric);
393impl_malloc_size_of_for_metric!(UrlMetric);
394impl_malloc_size_of_for_metric!(UuidMetric);