glean_core/
common_metric_data.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 std::sync::atomic::{AtomicU8, Ordering};
6
7use crate::error::{Error, ErrorKind};
8use crate::metrics::labeled::validate_dynamic_label;
9use crate::Glean;
10use serde::{Deserialize, Serialize};
11
12/// The supported metrics' lifetimes.
13///
14/// A metric's lifetime determines when its stored data gets reset.
15#[derive(Copy, Clone, Debug, PartialEq, Eq, Deserialize, Serialize, Default)]
16#[repr(i32)] // Use i32 to be compatible with our JNA definition
17#[serde(rename_all = "lowercase")]
18pub enum Lifetime {
19    /// The metric is reset with each sent ping
20    #[default]
21    Ping,
22    /// The metric is reset on application restart
23    Application,
24    /// The metric is reset with each user profile
25    User,
26}
27
28impl Lifetime {
29    /// String representation of the lifetime.
30    pub fn as_str(self) -> &'static str {
31        match self {
32            Lifetime::Ping => "ping",
33            Lifetime::Application => "app",
34            Lifetime::User => "user",
35        }
36    }
37}
38
39impl TryFrom<i32> for Lifetime {
40    type Error = Error;
41
42    fn try_from(value: i32) -> Result<Lifetime, Self::Error> {
43        match value {
44            0 => Ok(Lifetime::Ping),
45            1 => Ok(Lifetime::Application),
46            2 => Ok(Lifetime::User),
47            e => Err(ErrorKind::Lifetime(e).into()),
48        }
49    }
50}
51
52/// The common set of data shared across all different metric types.
53#[derive(Default, Debug, Clone, Deserialize, Serialize)]
54pub struct CommonMetricData {
55    /// The metric's name.
56    pub name: String,
57    /// The metric's category.
58    pub category: String,
59    /// List of ping names to include this metric in.
60    pub send_in_pings: Vec<String>,
61    /// The metric's lifetime.
62    pub lifetime: Lifetime,
63    /// Whether or not the metric is disabled.
64    ///
65    /// Disabled metrics are never recorded.
66    pub disabled: bool,
67    /// Dynamic label.
68    ///
69    /// When a [`LabeledMetric<T>`](crate::metrics::LabeledMetric) factory creates the specific
70    /// metric to be recorded to, dynamic labels are stored in the specific
71    /// label so that we can validate them when the Glean singleton is
72    /// available.
73    pub dynamic_label: Option<String>,
74}
75
76#[derive(Default, Debug)]
77pub struct CommonMetricDataInternal {
78    pub inner: CommonMetricData,
79    pub disabled: AtomicU8,
80}
81
82impl Clone for CommonMetricDataInternal {
83    fn clone(&self) -> Self {
84        Self {
85            inner: self.inner.clone(),
86            disabled: AtomicU8::new(self.disabled.load(Ordering::Relaxed)),
87        }
88    }
89}
90
91impl From<CommonMetricData> for CommonMetricDataInternal {
92    fn from(input_data: CommonMetricData) -> Self {
93        let disabled = input_data.disabled;
94        Self {
95            inner: input_data,
96            disabled: AtomicU8::new(u8::from(disabled)),
97        }
98    }
99}
100
101impl CommonMetricDataInternal {
102    /// Creates a new metadata object.
103    pub fn new<A: Into<String>, B: Into<String>, C: Into<String>>(
104        category: A,
105        name: B,
106        ping_name: C,
107    ) -> CommonMetricDataInternal {
108        CommonMetricDataInternal {
109            inner: CommonMetricData {
110                name: name.into(),
111                category: category.into(),
112                send_in_pings: vec![ping_name.into()],
113                ..Default::default()
114            },
115            disabled: AtomicU8::new(0),
116        }
117    }
118
119    /// The metric's base identifier, including the category and name, but not the label.
120    ///
121    /// If `category` is empty, it's ommitted.
122    /// Otherwise, it's the combination of the metric's `category` and `name`.
123    pub(crate) fn base_identifier(&self) -> String {
124        if self.inner.category.is_empty() {
125            self.inner.name.clone()
126        } else {
127            format!("{}.{}", self.inner.category, self.inner.name)
128        }
129    }
130
131    /// The metric's unique identifier, including the category, name and label.
132    ///
133    /// If `category` is empty, it's ommitted.
134    /// Otherwise, it's the combination of the metric's `category`, `name` and `label`.
135    pub(crate) fn identifier(&self, glean: &Glean) -> String {
136        let base_identifier = self.base_identifier();
137
138        if let Some(label) = &self.inner.dynamic_label {
139            validate_dynamic_label(glean, self, &base_identifier, label)
140        } else {
141            base_identifier
142        }
143    }
144
145    /// The list of storages this metric should be recorded into.
146    pub fn storage_names(&self) -> &[String] {
147        &self.inner.send_in_pings
148    }
149}