glean_core/
core_metrics.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::metrics::{
6    Datetime, DatetimeMetric, QuantityMetric, StringMetric, TimeUnit, TimespanMetric,
7};
8use crate::{CommonMetricData, Lifetime};
9
10use malloc_size_of_derive::MallocSizeOf;
11use once_cell::sync::Lazy;
12
13/// Metrics included in every ping as `client_info`.
14#[derive(Debug, Default, MallocSizeOf)]
15pub struct ClientInfoMetrics {
16    /// The build identifier generated by the CI system (e.g. "1234/A").
17    pub app_build: String,
18    /// The user visible version string (e.g. "1.0.3").
19    pub app_display_version: String,
20    /// The app's build date
21    pub app_build_date: Datetime,
22
23    /// The architecture of the device (e.g. "arm", "x86").
24    pub architecture: String,
25    /// The name of the operating system (e.g. "Linux", "Android", "iOS").
26    pub os_version: String,
27
28    /// The product-provided release channel (e.g. "beta").
29    pub channel: Option<String>,
30    /// The Android specific SDK version of the software running on this hardware device (e.g. "23").
31    pub android_sdk_version: Option<String>,
32    /// The Windows specific OS build version (e.g. 19043)
33    pub windows_build_number: Option<i64>,
34    /// The manufacturer of the device the application is running on.
35    /// Not set if the device manufacturer can't be determined (e.g. on Desktop).
36    pub device_manufacturer: Option<String>,
37    /// The model of the device the application is running on.
38    /// On Android, this is Build.MODEL, the user-visible marketing name, like "Pixel 2 XL".
39    /// Not set if the device model can't be determined (e.g. on Desktop).
40    pub device_model: Option<String>,
41    /// The locale of the application during initialization (e.g. "es-ES").
42    /// If the locale can't be determined on the system, the value is "und", to indicate "undetermined".
43    pub locale: Option<String>,
44}
45
46/// Optional product attribution metrics carried in `client_info.attribution`.
47#[derive(Clone, Debug, Default, PartialEq)]
48pub struct AttributionMetrics {
49    /// The attribution source (e.g. "google-play").
50    pub source: Option<String>,
51    /// The attribution medium (e.g. "organic" for a search engine).
52    pub medium: Option<String>,
53    /// The attribution campaign (e.g. "mozilla-org").
54    pub campaign: Option<String>,
55    /// The attribution term (e.g. "browser with developer tools for android").
56    pub term: Option<String>,
57    /// The attribution content (e.g. "firefoxview").
58    pub content: Option<String>,
59}
60
61impl AttributionMetrics {
62    /// Update self with any non-`None` fields from `other`.
63    pub fn update(&mut self, other: AttributionMetrics) {
64        if let Some(source) = other.source {
65            self.source = Some(source);
66        }
67        if let Some(medium) = other.medium {
68            self.medium = Some(medium);
69        }
70        if let Some(campaign) = other.campaign {
71            self.campaign = Some(campaign);
72        }
73        if let Some(term) = other.term {
74            self.term = Some(term);
75        }
76        if let Some(content) = other.content {
77            self.content = Some(content);
78        }
79    }
80}
81
82/// Optional product distribution metrics carried in `client_info.distribution`.
83#[derive(Clone, Debug, Default, PartialEq)]
84pub struct DistributionMetrics {
85    /// The distribution name (e.g. "MozillaOnline").
86    pub name: Option<String>,
87}
88
89impl DistributionMetrics {
90    /// Update self with any non-`None` fields from `other`.
91    pub fn update(&mut self, other: DistributionMetrics) {
92        if let Some(name) = other.name {
93            self.name = Some(name);
94        }
95    }
96}
97
98/// Metrics included in every ping as `client_info`.
99impl ClientInfoMetrics {
100    /// Creates the client info with dummy values for all.
101    pub fn unknown() -> Self {
102        ClientInfoMetrics {
103            app_build: "Unknown".to_string(),
104            app_display_version: "Unknown".to_string(),
105            app_build_date: Datetime::default(),
106            architecture: "Unknown".to_string(),
107            os_version: "Unknown".to_string(),
108            channel: Some("Unknown".to_string()),
109            android_sdk_version: None,
110            windows_build_number: None,
111            device_manufacturer: None,
112            device_model: None,
113            locale: None,
114        }
115    }
116}
117
118#[allow(non_upper_case_globals)]
119pub mod internal_metrics {
120    use super::*;
121
122    pub static app_build: Lazy<StringMetric> = Lazy::new(|| {
123        StringMetric::new(CommonMetricData {
124            name: "app_build".into(),
125            category: "".into(),
126            send_in_pings: vec!["glean_client_info".into()],
127            lifetime: Lifetime::Application,
128            disabled: false,
129            ..Default::default()
130        })
131    });
132
133    pub static app_display_version: Lazy<StringMetric> = Lazy::new(|| {
134        StringMetric::new(CommonMetricData {
135            name: "app_display_version".into(),
136            category: "".into(),
137            send_in_pings: vec!["glean_client_info".into()],
138            lifetime: Lifetime::Application,
139            disabled: false,
140            ..Default::default()
141        })
142    });
143
144    pub static app_build_date: Lazy<DatetimeMetric> = Lazy::new(|| {
145        DatetimeMetric::new(
146            CommonMetricData {
147                name: "build_date".into(),
148                category: "".into(),
149                send_in_pings: vec!["glean_client_info".into()],
150                lifetime: Lifetime::Application,
151                disabled: false,
152                ..Default::default()
153            },
154            TimeUnit::Second,
155        )
156    });
157
158    pub static app_channel: Lazy<StringMetric> = Lazy::new(|| {
159        StringMetric::new(CommonMetricData {
160            name: "app_channel".into(),
161            category: "".into(),
162            send_in_pings: vec!["glean_client_info".into()],
163            lifetime: Lifetime::Application,
164            disabled: false,
165            ..Default::default()
166        })
167    });
168
169    pub static os_version: Lazy<StringMetric> = Lazy::new(|| {
170        StringMetric::new(CommonMetricData {
171            name: "os_version".into(),
172            category: "".into(),
173            send_in_pings: vec!["glean_client_info".into()],
174            lifetime: Lifetime::Application,
175            disabled: false,
176            ..Default::default()
177        })
178    });
179
180    pub static architecture: Lazy<StringMetric> = Lazy::new(|| {
181        StringMetric::new(CommonMetricData {
182            name: "architecture".into(),
183            category: "".into(),
184            send_in_pings: vec!["glean_client_info".into()],
185            lifetime: Lifetime::Application,
186            disabled: false,
187            ..Default::default()
188        })
189    });
190
191    pub static android_sdk_version: Lazy<StringMetric> = Lazy::new(|| {
192        StringMetric::new(CommonMetricData {
193            name: "android_sdk_version".into(),
194            category: "".into(),
195            send_in_pings: vec!["glean_client_info".into()],
196            lifetime: Lifetime::Application,
197            disabled: false,
198            ..Default::default()
199        })
200    });
201
202    pub static windows_build_number: Lazy<QuantityMetric> = Lazy::new(|| {
203        QuantityMetric::new(CommonMetricData {
204            name: "windows_build_number".into(),
205            category: "".into(),
206            send_in_pings: vec!["glean_client_info".into()],
207            lifetime: Lifetime::Application,
208            disabled: false,
209            ..Default::default()
210        })
211    });
212
213    pub static device_manufacturer: Lazy<StringMetric> = Lazy::new(|| {
214        StringMetric::new(CommonMetricData {
215            name: "device_manufacturer".into(),
216            category: "".into(),
217            send_in_pings: vec!["glean_client_info".into()],
218            lifetime: Lifetime::Application,
219            disabled: false,
220            ..Default::default()
221        })
222    });
223
224    pub static device_model: Lazy<StringMetric> = Lazy::new(|| {
225        StringMetric::new(CommonMetricData {
226            name: "device_model".into(),
227            category: "".into(),
228            send_in_pings: vec!["glean_client_info".into()],
229            lifetime: Lifetime::Application,
230            disabled: false,
231            ..Default::default()
232        })
233    });
234
235    pub static locale: Lazy<StringMetric> = Lazy::new(|| {
236        StringMetric::new(CommonMetricData {
237            name: "locale".into(),
238            category: "".into(),
239            send_in_pings: vec!["glean_client_info".into()],
240            lifetime: Lifetime::Application,
241            disabled: false,
242            ..Default::default()
243        })
244    });
245
246    pub static baseline_duration: Lazy<TimespanMetric> = Lazy::new(|| {
247        TimespanMetric::new(
248            CommonMetricData {
249                name: "duration".into(),
250                category: "glean.baseline".into(),
251                send_in_pings: vec!["baseline".into()],
252                lifetime: Lifetime::Ping,
253                disabled: false,
254                ..Default::default()
255            },
256            TimeUnit::Second,
257        )
258    });
259}
260
261#[cfg(test)]
262mod test {
263    use super::*;
264
265    #[test]
266    fn update_attribution() {
267        let mut attr: AttributionMetrics = Default::default();
268        let empty: AttributionMetrics = Default::default();
269
270        // Ensure the identity operation works.
271        attr.update(empty.clone());
272        assert_eq!(None, attr.source);
273
274        // Ensure simple updates work.
275        attr.update(AttributionMetrics {
276            source: Some("a source".into()),
277            ..Default::default()
278        });
279        assert_eq!(Some("a source".into()), attr.source);
280
281        // Ensure None doesn't overwrite.
282        attr.update(empty);
283        assert_eq!(Some("a source".into()), attr.source);
284
285        // Ensure updates of Some work.
286        attr.update(AttributionMetrics {
287            source: Some("another source".into()),
288            ..Default::default()
289        });
290        assert_eq!(Some("another source".into()), attr.source);
291    }
292
293    #[test]
294    fn update_distribution() {
295        let mut dist: DistributionMetrics = Default::default();
296        let empty: DistributionMetrics = Default::default();
297
298        // Ensure the identity operation works.
299        dist.update(empty.clone());
300        assert_eq!(None, dist.name);
301
302        // Ensure simple updates work.
303        dist.update(DistributionMetrics {
304            name: Some("a name".into()),
305        });
306        assert_eq!(Some("a name".into()), dist.name);
307
308        // Ensure None doesn't overwrite.
309        dist.update(empty);
310        assert_eq!(Some("a name".into()), dist.name);
311
312        // Ensure updates of Some work.
313        dist.update(DistributionMetrics {
314            name: Some("another name".into()),
315        });
316        assert_eq!(Some("another name".into()), dist.name);
317    }
318}