diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 14:29:10 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 14:29:10 +0000 |
commit | 2aa4a82499d4becd2284cdb482213d541b8804dd (patch) | |
tree | b80bf8bf13c3766139fbacc530efd0dd9d54394c /third_party/rust/glean/src/private | |
parent | Initial commit. (diff) | |
download | firefox-2aa4a82499d4becd2284cdb482213d541b8804dd.tar.xz firefox-2aa4a82499d4becd2284cdb482213d541b8804dd.zip |
Adding upstream version 86.0.1.upstream/86.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'third_party/rust/glean/src/private')
-rw-r--r-- | third_party/rust/glean/src/private/boolean.rs | 49 | ||||
-rw-r--r-- | third_party/rust/glean/src/private/counter.rs | 62 | ||||
-rw-r--r-- | third_party/rust/glean/src/private/custom_distribution.rs | 81 | ||||
-rw-r--r-- | third_party/rust/glean/src/private/datetime.rs | 103 | ||||
-rw-r--r-- | third_party/rust/glean/src/private/event.rs | 173 | ||||
-rw-r--r-- | third_party/rust/glean/src/private/labeled.rs | 355 | ||||
-rw-r--r-- | third_party/rust/glean/src/private/memory_distribution.rs | 67 | ||||
-rw-r--r-- | third_party/rust/glean/src/private/mod.rs | 37 | ||||
-rw-r--r-- | third_party/rust/glean/src/private/ping.rs | 48 | ||||
-rw-r--r-- | third_party/rust/glean/src/private/quantity.rs | 63 | ||||
-rw-r--r-- | third_party/rust/glean/src/private/recorded_experiment_data.rs | 15 | ||||
-rw-r--r-- | third_party/rust/glean/src/private/string.rs | 72 | ||||
-rw-r--r-- | third_party/rust/glean/src/private/string_list.rs | 108 | ||||
-rw-r--r-- | third_party/rust/glean/src/private/timespan.rs | 163 | ||||
-rw-r--r-- | third_party/rust/glean/src/private/timing_distribution.rs | 99 | ||||
-rw-r--r-- | third_party/rust/glean/src/private/uuid.rs | 69 |
16 files changed, 1564 insertions, 0 deletions
diff --git a/third_party/rust/glean/src/private/boolean.rs b/third_party/rust/glean/src/private/boolean.rs new file mode 100644 index 0000000000..c9bc87535d --- /dev/null +++ b/third_party/rust/glean/src/private/boolean.rs @@ -0,0 +1,49 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. + +use inherent::inherent; +use std::sync::Arc; + +use glean_core::metrics::MetricType; + +use crate::dispatcher; + +// We need to wrap the glean-core type: otherwise if we try to implement +// the trait for the metric in `glean_core::metrics` we hit error[E0117]: +// only traits defined in the current crate can be implemented for arbitrary +// types. + +/// This implements the developer facing API for recording boolean metrics. +/// +/// Instances of this class type are automatically generated by the parsers +/// at build time, allowing developers to record values that were previously +/// registered in the metrics.yaml file. +#[derive(Clone)] +pub struct BooleanMetric(pub(crate) Arc<glean_core::metrics::BooleanMetric>); + +impl BooleanMetric { + /// The public constructor used by automatically generated metrics. + pub fn new(meta: glean_core::CommonMetricData) -> Self { + Self(Arc::new(glean_core::metrics::BooleanMetric::new(meta))) + } +} + +#[inherent(pub)] +impl glean_core::traits::Boolean for BooleanMetric { + fn set(&self, value: bool) { + let metric = Arc::clone(&self.0); + dispatcher::launch(move || crate::with_glean(|glean| metric.set(glean, value))); + } + + fn test_get_value<'a, S: Into<Option<&'a str>>>(&self, ping_name: S) -> Option<bool> { + crate::block_on_dispatcher(); + + let queried_ping_name = match ping_name.into() { + Some(name) => name, + None => self.0.meta().send_in_pings.first().unwrap(), + }; + + crate::with_glean(|glean| self.0.test_get_value(glean, queried_ping_name)) + } +} diff --git a/third_party/rust/glean/src/private/counter.rs b/third_party/rust/glean/src/private/counter.rs new file mode 100644 index 0000000000..61e4cc3242 --- /dev/null +++ b/third_party/rust/glean/src/private/counter.rs @@ -0,0 +1,62 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. + +use inherent::inherent; +use std::sync::Arc; + +use glean_core::metrics::MetricType; +use glean_core::ErrorType; + +use crate::dispatcher; + +// We need to wrap the glean-core type: otherwise if we try to implement +// the trait for the metric in `glean_core::metrics` we hit error[E0117]: +// only traits defined in the current crate can be implemented for arbitrary +// types. + +/// This implements the developer facing API for recording counter metrics. +/// +/// Instances of this class type are automatically generated by the parsers +/// at build time, allowing developers to record values that were previously +/// registered in the metrics.yaml file. +#[derive(Clone)] +pub struct CounterMetric(pub(crate) Arc<glean_core::metrics::CounterMetric>); + +impl CounterMetric { + /// The public constructor used by automatically generated metrics. + pub fn new(meta: glean_core::CommonMetricData) -> Self { + Self(Arc::new(glean_core::metrics::CounterMetric::new(meta))) + } +} + +#[inherent(pub)] +impl glean_core::traits::Counter for CounterMetric { + fn add(&self, amount: i32) { + let metric = Arc::clone(&self.0); + dispatcher::launch(move || crate::with_glean(|glean| metric.add(glean, amount))); + } + + fn test_get_value<'a, S: Into<Option<&'a str>>>(&self, ping_name: S) -> Option<i32> { + crate::block_on_dispatcher(); + + let queried_ping_name = ping_name + .into() + .unwrap_or_else(|| &self.0.meta().send_in_pings[0]); + + crate::with_glean(|glean| self.0.test_get_value(glean, queried_ping_name)) + } + + fn test_get_num_recorded_errors<'a, S: Into<Option<&'a str>>>( + &self, + error: ErrorType, + ping_name: S, + ) -> i32 { + crate::block_on_dispatcher(); + + crate::with_glean_mut(|glean| { + glean_core::test_get_num_recorded_errors(&glean, self.0.meta(), error, ping_name.into()) + .unwrap_or(0) + }) + } +} diff --git a/third_party/rust/glean/src/private/custom_distribution.rs b/third_party/rust/glean/src/private/custom_distribution.rs new file mode 100644 index 0000000000..790850c8d7 --- /dev/null +++ b/third_party/rust/glean/src/private/custom_distribution.rs @@ -0,0 +1,81 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. + +use inherent::inherent; +use std::sync::Arc; + +use glean_core::metrics::{DistributionData, MetricType}; +use glean_core::{CommonMetricData, ErrorType, HistogramType}; + +use crate::dispatcher; + +// We need to wrap the glean-core type: otherwise if we try to implement +// the trait for the metric in `glean_core::metrics` we hit error[E0117]: +// only traits defined in the current crate can be implemented for arbitrary +// types. + +/// This implements the developer-facing API for recording custom distribution metrics. +/// +/// Instances of this class type are automatically generated by the parsers +/// at build time, allowing developers to record values that were previously +/// registered in the metrics.yaml file. +#[derive(Clone)] +pub struct CustomDistributionMetric(pub(crate) Arc<glean_core::metrics::CustomDistributionMetric>); + +impl CustomDistributionMetric { + /// The public constructor used by automatically generated metrics. + pub fn new( + meta: CommonMetricData, + range_min: u64, + range_max: u64, + bucket_count: u64, + histogram_type: HistogramType, + ) -> Self { + Self(Arc::new( + glean_core::metrics::CustomDistributionMetric::new( + meta, + range_min, + range_max, + bucket_count, + histogram_type, + ), + )) + } +} + +#[inherent(pub)] +impl glean_core::traits::CustomDistribution for CustomDistributionMetric { + fn accumulate_samples_signed(&self, samples: Vec<i64>) { + let metric = Arc::clone(&self.0); + dispatcher::launch(move || { + crate::with_glean(|glean| metric.accumulate_samples_signed(glean, samples)) + }); + } + + fn test_get_value<'a, S: Into<Option<&'a str>>>( + &self, + ping_name: S, + ) -> Option<DistributionData> { + crate::block_on_dispatcher(); + + let queried_ping_name = ping_name + .into() + .unwrap_or_else(|| &self.0.meta().send_in_pings[0]); + + crate::with_glean(|glean| self.0.test_get_value(glean, queried_ping_name)) + } + + fn test_get_num_recorded_errors<'a, S: Into<Option<&'a str>>>( + &self, + error: ErrorType, + ping_name: S, + ) -> i32 { + crate::block_on_dispatcher(); + + crate::with_glean_mut(|glean| { + glean_core::test_get_num_recorded_errors(&glean, self.0.meta(), error, ping_name.into()) + .unwrap_or(0) + }) + } +} diff --git a/third_party/rust/glean/src/private/datetime.rs b/third_party/rust/glean/src/private/datetime.rs new file mode 100644 index 0000000000..fcb6376022 --- /dev/null +++ b/third_party/rust/glean/src/private/datetime.rs @@ -0,0 +1,103 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. + +use inherent::inherent; +use std::sync::Arc; + +use glean_core::metrics::MetricType; +pub use glean_core::metrics::{Datetime, TimeUnit}; +use glean_core::ErrorType; + +use crate::dispatcher; + +// We need to wrap the glean-core type: otherwise if we try to implement +// the trait for the metric in `glean_core::metrics` we hit error[E0117]: +// only traits defined in the current crate can be implemented for arbitrary +// types. + +/// This implements the developer facing API for recording Datetime metrics. +/// +/// Instances of this class type are automatically generated by the parsers +/// at build time, allowing developers to record values that were previously +/// registered in the metrics.yaml file. +#[derive(Clone)] +pub struct DatetimeMetric(pub(crate) Arc<glean_core::metrics::DatetimeMetric>); + +impl DatetimeMetric { + /// The public constructor used by automatically generated metrics. + pub fn new(meta: glean_core::CommonMetricData, time_unit: TimeUnit) -> Self { + Self(Arc::new(glean_core::metrics::DatetimeMetric::new( + meta, time_unit, + ))) + } +} + +#[inherent(pub)] +impl glean_core::traits::Datetime for DatetimeMetric { + fn set(&self, value: Option<Datetime>) { + let metric = Arc::clone(&self.0); + dispatcher::launch(move || crate::with_glean(|glean| metric.set(glean, value))); + } + + fn test_get_value<'a, S: Into<Option<&'a str>>>(&self, ping_name: S) -> Option<Datetime> { + crate::block_on_dispatcher(); + + let queried_ping_name = ping_name + .into() + .unwrap_or_else(|| &self.0.meta().send_in_pings[0]); + + crate::with_glean(|glean| self.0.test_get_value(glean, queried_ping_name)) + } + + fn test_get_num_recorded_errors<'a, S: Into<Option<&'a str>>>( + &self, + error: ErrorType, + ping_name: S, + ) -> i32 { + crate::block_on_dispatcher(); + + crate::with_glean_mut(|glean| { + glean_core::test_get_num_recorded_errors(&glean, self.0.meta(), error, ping_name.into()) + .unwrap_or(0) + }) + } +} + +#[cfg(test)] +mod test { + use super::*; + use crate::common_test::{lock_test, new_glean}; + use crate::CommonMetricData; + use chrono::prelude::*; + + #[test] + fn datetime_convenient_api() { + let _lock = lock_test(); + let _t = new_glean(None, true); + + let metric: DatetimeMetric = DatetimeMetric::new( + CommonMetricData { + name: "datetime".into(), + category: "test".into(), + send_in_pings: vec!["test1".into()], + ..Default::default() + }, + TimeUnit::Day, + ); + + // Record a date: it will get truncated to Day resolution. + let sample_date = FixedOffset::east(0).ymd(2018, 2, 25).and_hms(11, 5, 0); + metric.set(Some(sample_date)); + + // Check that the value has the correct resolution. + let date = metric.test_get_value(None).unwrap(); + assert_eq!(date, FixedOffset::east(0).ymd(2018, 2, 25).and_hms(0, 0, 0)); + + // Ensure no error was recorded. + assert_eq!( + metric.test_get_num_recorded_errors(ErrorType::InvalidValue, None), + 0 + ) + } +} diff --git a/third_party/rust/glean/src/private/event.rs b/third_party/rust/glean/src/private/event.rs new file mode 100644 index 0000000000..7bc6fe1017 --- /dev/null +++ b/third_party/rust/glean/src/private/event.rs @@ -0,0 +1,173 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. + +use inherent::inherent; +use std::{collections::HashMap, marker::PhantomData, sync::Arc}; + +use glean_core::metrics::MetricType; +use glean_core::traits; + +use crate::{dispatcher, ErrorType, RecordedEvent}; + +pub use glean_core::traits::NoExtraKeys; + +// We need to wrap the glean-core type: otherwise if we try to implement +// the trait for the metric in `glean_core::metrics` we hit error[E0117]: +// only traits defined in the current crate can be implemented for arbitrary +// types. + +/// This implements the developer facing API for recording event metrics. +/// +/// Instances of this class type are automatically generated by the parsers +/// at build time, allowing developers to record values that were previously +/// registered in the metrics.yaml file. +#[derive(Clone)] +pub struct EventMetric<K> { + pub(crate) inner: Arc<glean_core::metrics::EventMetric>, + extra_keys: PhantomData<K>, +} + +impl<K: traits::ExtraKeys> EventMetric<K> { + /// The public constructor used by automatically generated metrics. + pub fn new(meta: glean_core::CommonMetricData) -> Self { + let allowed_extra_keys = K::ALLOWED_KEYS.iter().map(|s| s.to_string()).collect(); + let inner = Arc::new(glean_core::metrics::EventMetric::new( + meta, + allowed_extra_keys, + )); + Self { + inner, + extra_keys: PhantomData, + } + } +} + +#[inherent(pub)] +impl<K: traits::ExtraKeys> traits::Event for EventMetric<K> { + type Extra = K; + + fn record<M: Into<Option<HashMap<<Self as traits::Event>::Extra, String>>>>(&self, extra: M) { + const NANOS_PER_MILLI: u64 = 1_000_000; + let now = time::precise_time_ns() / NANOS_PER_MILLI; + + // Translate from [ExtraKey -> String] to a [Int -> String] map + let extra = extra + .into() + .map(|h| h.into_iter().map(|(k, v)| (k.index(), v)).collect()); + let metric = Arc::clone(&self.inner); + dispatcher::launch(move || crate::with_glean(|glean| metric.record(glean, now, extra))); + } + + fn test_get_value<'a, S: Into<Option<&'a str>>>( + &self, + ping_name: S, + ) -> Option<Vec<RecordedEvent>> { + crate::block_on_dispatcher(); + + let queried_ping_name = ping_name + .into() + .unwrap_or_else(|| &self.inner.meta().send_in_pings[0]); + + crate::with_glean(|glean| self.inner.test_get_value(glean, queried_ping_name)) + } + + fn test_get_num_recorded_errors<'a, S: Into<Option<&'a str>>>( + &self, + error: ErrorType, + ping_name: S, + ) -> i32 { + crate::block_on_dispatcher(); + + crate::with_glean_mut(|glean| { + glean_core::test_get_num_recorded_errors( + &glean, + self.inner.meta(), + error, + ping_name.into(), + ) + .unwrap_or(0) + }) + } +} + +#[cfg(test)] +mod test { + use super::*; + use crate::common_test::{lock_test, new_glean}; + use crate::CommonMetricData; + + #[test] + fn no_extra_keys() { + let _lock = lock_test(); + let _t = new_glean(None, true); + + let metric: EventMetric<NoExtraKeys> = EventMetric::new(CommonMetricData { + name: "event".into(), + category: "test".into(), + send_in_pings: vec!["test1".into()], + ..Default::default() + }); + + metric.record(None); + metric.record(None); + + let data = metric.test_get_value(None).expect("no event recorded"); + assert_eq!(2, data.len()); + assert!(data[0].timestamp <= data[1].timestamp); + } + + #[test] + fn with_extra_keys() { + let _lock = lock_test(); + let _t = new_glean(None, true); + + #[derive(Debug, Clone, Copy, Hash, Eq, PartialEq)] + enum SomeExtra { + Key1, + Key2, + } + + impl glean_core::traits::ExtraKeys for SomeExtra { + const ALLOWED_KEYS: &'static [&'static str] = &["key1", "key2"]; + + fn index(self) -> i32 { + self as i32 + } + } + + let metric: EventMetric<SomeExtra> = EventMetric::new(CommonMetricData { + name: "event".into(), + category: "test".into(), + send_in_pings: vec!["test1".into()], + ..Default::default() + }); + + let mut map1 = HashMap::new(); + map1.insert(SomeExtra::Key1, "1".into()); + metric.record(map1); + + let mut map2 = HashMap::new(); + map2.insert(SomeExtra::Key1, "1".into()); + map2.insert(SomeExtra::Key2, "2".into()); + metric.record(map2); + + metric.record(None); + + let data = metric.test_get_value(None).expect("no event recorded"); + assert_eq!(3, data.len()); + assert!(data[0].timestamp <= data[1].timestamp); + assert!(data[1].timestamp <= data[2].timestamp); + + let mut map = HashMap::new(); + map.insert("key1".into(), "1".into()); + assert_eq!(Some(map), data[0].extra); + + let mut map = HashMap::new(); + map.insert("key1".into(), "1".into()); + map.insert("key2".into(), "2".into()); + assert_eq!(Some(map), data[1].extra); + + assert_eq!(None, data[2].extra); + } +} diff --git a/third_party/rust/glean/src/private/labeled.rs b/third_party/rust/glean/src/private/labeled.rs new file mode 100644 index 0000000000..af4f1411a3 --- /dev/null +++ b/third_party/rust/glean/src/private/labeled.rs @@ -0,0 +1,355 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. + +use inherent::inherent; +use std::sync::Arc; + +use glean_core::metrics::MetricType; +use glean_core::ErrorType; + +/// Sealed traits protect against downstream implementations. +/// +/// We wrap it in a private module that is inaccessible outside of this module. +mod private { + use crate::{ + private::BooleanMetric, private::CounterMetric, private::StringMetric, CommonMetricData, + }; + use std::sync::Arc; + + /// The sealed labeled trait. + /// + /// This also allows us to hide methods, that are only used internally + /// and should not be visible to users of the object implementing the + /// `Labeled<T>` trait. + pub trait Sealed { + /// The `glean_core` metric type representing the labeled metric. + type Inner: glean_core::metrics::MetricType + Clone; + + /// Create a new metric object implementing this trait from the inner type. + fn from_inner(metric: Self::Inner) -> Self; + + /// Create a new `glean_core` metric from the metadata. + fn new_inner(meta: crate::CommonMetricData) -> Self::Inner; + } + + // `LabeledMetric<BooleanMetric>` is possible. + // + // See [Labeled Booleans](https://mozilla.github.io/glean/book/user/metrics/labeled_booleans.html). + impl Sealed for BooleanMetric { + type Inner = glean_core::metrics::BooleanMetric; + + fn from_inner(metric: Self::Inner) -> Self { + BooleanMetric(Arc::new(metric)) + } + + fn new_inner(meta: CommonMetricData) -> Self::Inner { + glean_core::metrics::BooleanMetric::new(meta) + } + } + + // `LabeledMetric<StringMetric>` is possible. + // + // See [Labeled Strings](https://mozilla.github.io/glean/book/user/metrics/labeled_strings.html). + impl Sealed for StringMetric { + type Inner = glean_core::metrics::StringMetric; + + fn from_inner(metric: Self::Inner) -> Self { + StringMetric(Arc::new(metric)) + } + + fn new_inner(meta: CommonMetricData) -> Self::Inner { + glean_core::metrics::StringMetric::new(meta) + } + } + + // `LabeledMetric<CounterMetric>` is possible. + // + // See [Labeled Counters](https://mozilla.github.io/glean/book/user/metrics/labeled_counters.html). + impl Sealed for CounterMetric { + type Inner = glean_core::metrics::CounterMetric; + + fn from_inner(metric: Self::Inner) -> Self { + CounterMetric(Arc::new(metric)) + } + + fn new_inner(meta: CommonMetricData) -> Self::Inner { + glean_core::metrics::CounterMetric::new(meta) + } + } +} + +/// Marker trait for metrics that can be nested inside a labeled metric. +/// +/// This trait is sealed and cannot be implemented for types outside this crate. +pub trait AllowLabeled: private::Sealed {} + +// Implement the trait for everything we marked as allowed. +impl<T> AllowLabeled for T where T: private::Sealed {} + +// We need to wrap the glean-core type: otherwise if we try to implement +// the trait for the metric in `glean_core::metrics` we hit error[E0117]: +// only traits defined in the current crate can be implemented for arbitrary +// types. + +/// This implements the specific facing API for recording labeled metrics. +/// +/// Instances of this type are automatically generated by the parser +/// at build time, allowing developers to record values that were previously +/// registered in the metrics.yaml file. +/// Unlike most metric types, [`LabeledMetric`] does not have its own corresponding +/// storage, but records metrics for the underlying metric type `T` in the storage +/// for that type. +#[derive(Clone)] +pub struct LabeledMetric<T: AllowLabeled>( + pub(crate) Arc<glean_core::metrics::LabeledMetric<T::Inner>>, +); + +impl<T> LabeledMetric<T> +where + T: AllowLabeled, +{ + /// The public constructor used by automatically generated metrics. + pub fn new(meta: glean_core::CommonMetricData, labels: Option<Vec<String>>) -> Self { + let submetric = T::new_inner(meta); + let core = glean_core::metrics::LabeledMetric::new(submetric, labels); + Self(Arc::new(core)) + } +} + +#[inherent(pub)] +impl<T> glean_core::traits::Labeled<T> for LabeledMetric<T> +where + T: AllowLabeled + Clone, +{ + fn get(&self, label: &str) -> T { + let inner = self.0.get(label); + T::from_inner(inner) + } + + fn test_get_num_recorded_errors<'a, S: Into<Option<&'a str>>>( + &self, + error: ErrorType, + ping_name: S, + ) -> i32 { + crate::block_on_dispatcher(); + + crate::with_glean_mut(|glean| { + glean_core::test_get_num_recorded_errors( + &glean, + self.0.get_submetric().meta(), + error, + ping_name.into(), + ) + .unwrap_or(0) + }) + } +} + +#[cfg(test)] +mod test { + use super::ErrorType; + use crate::common_test::{lock_test, new_glean}; + use crate::destroy_glean; + use crate::private::{BooleanMetric, CounterMetric, LabeledMetric, StringMetric}; + use crate::CommonMetricData; + + #[test] + fn test_labeled_counter_type() { + let _lock = lock_test(); + + let _t = new_glean(None, true); + + let metric: LabeledMetric<CounterMetric> = LabeledMetric::new( + CommonMetricData { + name: "labeled_counter".into(), + category: "labeled".into(), + send_in_pings: vec!["test1".into()], + ..Default::default() + }, + None, + ); + + metric.get("label1").add(1); + metric.get("label2").add(2); + assert_eq!(1, metric.get("label1").test_get_value("test1").unwrap()); + assert_eq!(2, metric.get("label2").test_get_value("test1").unwrap()); + } + + #[test] + fn test_other_label_with_predefined_labels() { + let _lock = lock_test(); + + let _t = new_glean(None, true); + + let metric: LabeledMetric<CounterMetric> = LabeledMetric::new( + CommonMetricData { + name: "labeled_counter".into(), + category: "labeled".into(), + send_in_pings: vec!["test1".into()], + ..Default::default() + }, + Some(vec!["foo".into(), "bar".into(), "baz".into()]), + ); + + metric.get("foo").add(1); + metric.get("foo").add(2); + metric.get("bar").add(1); + metric.get("not_there").add(1); + metric.get("also_not_there").add(1); + metric.get("not_me").add(1); + + assert_eq!(3, metric.get("foo").test_get_value(None).unwrap()); + assert_eq!(1, metric.get("bar").test_get_value(None).unwrap()); + assert!(metric.get("baz").test_get_value(None).is_none()); + // The rest all lands in the __other__ bucket. + assert_eq!(3, metric.get("__other__").test_get_value(None).unwrap()); + } + + #[test] + fn test_other_label_without_predefined_labels() { + let _lock = lock_test(); + + let _t = new_glean(None, true); + + let metric: LabeledMetric<CounterMetric> = LabeledMetric::new( + CommonMetricData { + name: "labeled_counter".into(), + category: "labeled".into(), + send_in_pings: vec!["test1".into()], + ..Default::default() + }, + None, + ); + + // Record in 20 labels: it will go over the maximum number of supported + // dynamic labels. + for i in 0..=20 { + metric.get(format!("label_{}", i).as_str()).add(1); + } + // Record in a label once again. + metric.get("label_0").add(1); + + assert_eq!(2, metric.get("label_0").test_get_value(None).unwrap()); + for i in 1..15 { + assert_eq!( + 1, + metric + .get(format!("label_{}", i).as_str()) + .test_get_value(None) + .unwrap() + ); + } + assert_eq!(5, metric.get("__other__").test_get_value(None).unwrap()); + } + + #[test] + fn test_other_label_without_predefined_labels_before_glean_init() { + let _lock = lock_test(); + + // We explicitly want Glean to not be initialized. + destroy_glean(true); + + let metric: LabeledMetric<CounterMetric> = LabeledMetric::new( + CommonMetricData { + name: "labeled_counter".into(), + category: "labeled".into(), + send_in_pings: vec!["test1".into()], + ..Default::default() + }, + None, + ); + + // Record in 20 labels: it will go over the maximum number of supported + // dynamic labels. + for i in 0..=20 { + metric.get(format!("label_{}", i).as_str()).add(1); + } + // Record in a label once again. + metric.get("label_0").add(1); + + // Initialize Glean. + let _t = new_glean(None, false); + + assert_eq!(2, metric.get("label_0").test_get_value(None).unwrap()); + for i in 1..15 { + assert_eq!( + 1, + metric + .get(format!("label_{}", i).as_str()) + .test_get_value(None) + .unwrap() + ); + } + assert_eq!(5, metric.get("__other__").test_get_value(None).unwrap()); + } + + #[test] + fn test_labeled_string_type() { + let _lock = lock_test(); + + let _t = new_glean(None, true); + + let metric: LabeledMetric<StringMetric> = LabeledMetric::new( + CommonMetricData { + name: "labeled_string".into(), + category: "labeled".into(), + send_in_pings: vec!["test1".into()], + ..Default::default() + }, + None, + ); + + metric.get("label1").set("foo"); + metric.get("label2").set("bar"); + assert_eq!("foo", metric.get("label1").test_get_value("test1").unwrap()); + assert_eq!("bar", metric.get("label2").test_get_value("test1").unwrap()); + } + + #[test] + fn test_labeled_boolean_type() { + let _lock = lock_test(); + + let _t = new_glean(None, true); + + let metric: LabeledMetric<BooleanMetric> = LabeledMetric::new( + CommonMetricData { + name: "labeled_boolean".into(), + category: "labeled".into(), + send_in_pings: vec!["test1".into()], + ..Default::default() + }, + None, + ); + + metric.get("label1").set(false); + metric.get("label2").set(true); + assert!(!metric.get("label1").test_get_value("test1").unwrap()); + assert!(metric.get("label2").test_get_value("test1").unwrap()); + } + + #[test] + fn test_invalid_labels_record_errors() { + let _lock = lock_test(); + + let _t = new_glean(None, true); + + let metric: LabeledMetric<BooleanMetric> = LabeledMetric::new( + CommonMetricData { + name: "labeled_boolean".into(), + category: "labeled".into(), + send_in_pings: vec!["test1".into()], + ..Default::default() + }, + None, + ); + + let invalid_label = "!#I'm invalid#--_"; + metric.get(invalid_label).set(true); + assert_eq!(true, metric.get("__other__").test_get_value(None).unwrap()); + assert_eq!( + 1, + metric.test_get_num_recorded_errors(ErrorType::InvalidLabel, None) + ); + } +} diff --git a/third_party/rust/glean/src/private/memory_distribution.rs b/third_party/rust/glean/src/private/memory_distribution.rs new file mode 100644 index 0000000000..584d86c9d1 --- /dev/null +++ b/third_party/rust/glean/src/private/memory_distribution.rs @@ -0,0 +1,67 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. + +use inherent::inherent; +use std::sync::Arc; + +use glean_core::metrics::{DistributionData, MemoryUnit, MetricType}; +use glean_core::ErrorType; + +use crate::dispatcher; + +// We need to wrap the glean-core type: otherwise if we try to implement +// the trait for the metric in `glean_core::metrics` we hit error[E0117]: +// only traits defined in the current crate can be implemented for arbitrary +// types. + +/// This implements the developer-facing API for recording memory distribution metrics. +/// +/// Instances of this class type are automatically generated by the parsers +/// at build time, allowing developers to record values that were previously +/// registered in the metrics.yaml file. +#[derive(Clone)] +pub struct MemoryDistributionMetric(pub(crate) Arc<glean_core::metrics::MemoryDistributionMetric>); + +impl MemoryDistributionMetric { + /// The public constructor used by automatically generated metrics. + pub fn new(meta: glean_core::CommonMetricData, memory_unit: MemoryUnit) -> Self { + Self(Arc::new( + glean_core::metrics::MemoryDistributionMetric::new(meta, memory_unit), + )) + } +} + +#[inherent(pub)] +impl glean_core::traits::MemoryDistribution for MemoryDistributionMetric { + fn accumulate(&self, sample: u64) { + let metric = Arc::clone(&self.0); + dispatcher::launch(move || crate::with_glean(|glean| metric.accumulate(glean, sample))); + } + + fn test_get_value<'a, S: Into<Option<&'a str>>>( + &self, + ping_name: S, + ) -> Option<DistributionData> { + crate::block_on_dispatcher(); + + let queried_ping_name = ping_name + .into() + .unwrap_or_else(|| &self.0.meta().send_in_pings[0]); + + crate::with_glean(|glean| self.0.test_get_value(glean, queried_ping_name)) + } + + fn test_get_num_recorded_errors<'a, S: Into<Option<&'a str>>>( + &self, + error: ErrorType, + ping_name: S, + ) -> i32 { + crate::block_on_dispatcher(); + + crate::with_glean_mut(|glean| { + glean_core::test_get_num_recorded_errors(&glean, self.0.meta(), error, ping_name.into()) + .unwrap_or(0) + }) + } +} diff --git a/third_party/rust/glean/src/private/mod.rs b/third_party/rust/glean/src/private/mod.rs new file mode 100644 index 0000000000..c4b692072b --- /dev/null +++ b/third_party/rust/glean/src/private/mod.rs @@ -0,0 +1,37 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. + +//! The different metric types supported by the Glean SDK to handle data. + +mod boolean; +mod counter; +mod custom_distribution; +mod datetime; +mod event; +mod labeled; +mod memory_distribution; +mod ping; +mod quantity; +mod recorded_experiment_data; +mod string; +mod string_list; +mod timespan; +mod timing_distribution; +mod uuid; + +pub use self::uuid::UuidMetric; +pub use boolean::BooleanMetric; +pub use counter::CounterMetric; +pub use custom_distribution::CustomDistributionMetric; +pub use datetime::{Datetime, DatetimeMetric}; +pub use event::EventMetric; +pub use labeled::{AllowLabeled, LabeledMetric}; +pub use memory_distribution::MemoryDistributionMetric; +pub use ping::PingType; +pub use quantity::QuantityMetric; +pub use recorded_experiment_data::RecordedExperimentData; +pub use string::StringMetric; +pub use string_list::StringListMetric; +pub use timespan::TimespanMetric; +pub use timing_distribution::TimingDistributionMetric; diff --git a/third_party/rust/glean/src/private/ping.rs b/third_party/rust/glean/src/private/ping.rs new file mode 100644 index 0000000000..c9452b9d35 --- /dev/null +++ b/third_party/rust/glean/src/private/ping.rs @@ -0,0 +1,48 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. + +use inherent::inherent; + +/// A Glean ping. +#[derive(Clone, Debug)] +pub struct PingType { + pub(crate) name: String, + pub(crate) ping_type: glean_core::metrics::PingType, +} + +impl PingType { + /// Creates a new ping type. + /// + /// # Arguments + /// + /// * `name` - The name of the ping. + /// * `include_client_id` - Whether to include the client ID in the assembled ping when. + /// * `send_if_empty` - Whether the ping should be sent empty or not. + /// * `reason_codes` - The valid reason codes for this ping. + pub fn new<A: Into<String>>( + name: A, + include_client_id: bool, + send_if_empty: bool, + reason_codes: Vec<String>, + ) -> Self { + let name = name.into(); + let ping_type = glean_core::metrics::PingType::new( + name.clone(), + include_client_id, + send_if_empty, + reason_codes, + ); + + let me = Self { name, ping_type }; + crate::register_ping_type(&me); + me + } +} + +#[inherent(pub)] +impl glean_core::traits::Ping for PingType { + fn submit(&self, reason: Option<&str>) { + crate::submit_ping(self, reason) + } +} diff --git a/third_party/rust/glean/src/private/quantity.rs b/third_party/rust/glean/src/private/quantity.rs new file mode 100644 index 0000000000..716ce5147c --- /dev/null +++ b/third_party/rust/glean/src/private/quantity.rs @@ -0,0 +1,63 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. + +use inherent::inherent; +use std::sync::Arc; + +use glean_core::metrics::MetricType; +use glean_core::ErrorType; + +use crate::dispatcher; + +// We need to wrap the glean-core type, otherwise if we try to implement +// the trait for the metric in `glean_core::metrics` we hit error[E0117]: +// only traits defined in the current crate can be implemented for arbitrary +// types. + +/// This implements the developer facing API for recording Quantity metrics. +/// +/// Instances of this class type are automatically generated by the parsers +/// at build time, allowing developers to record values that were previously +/// registered in the metrics.yaml file. +#[derive(Clone)] +pub struct QuantityMetric(pub(crate) Arc<glean_core::metrics::QuantityMetric>); + +impl QuantityMetric { + /// The public constructor used by automatically generated metrics. + pub fn new(meta: glean_core::CommonMetricData) -> Self { + Self(Arc::new(glean_core::metrics::QuantityMetric::new(meta))) + } +} + +#[inherent(pub)] +impl glean_core::traits::Quantity for QuantityMetric { + fn set(&self, value: i64) { + let metric = Arc::clone(&self.0); + dispatcher::launch(move || crate::with_glean(|glean| metric.set(glean, value))); + } + + fn test_get_value<'a, S: Into<Option<&'a str>>>(&self, ping_name: S) -> Option<i64> { + crate::block_on_dispatcher(); + + let queried_ping_name = ping_name + .into() + .unwrap_or_else(|| &self.0.meta().send_in_pings[0]); + + crate::with_glean(|glean| self.0.test_get_value(glean, queried_ping_name)) + } + + #[allow(dead_code)] // Remove after mozilla/glean#1328 + fn test_get_num_recorded_errors<'a, S: Into<Option<&'a str>>>( + &self, + error: ErrorType, + ping_name: S, + ) -> i32 { + crate::block_on_dispatcher(); + + crate::with_glean_mut(|glean| { + glean_core::test_get_num_recorded_errors(&glean, self.0.meta(), error, ping_name.into()) + .unwrap_or(0) + }) + } +} diff --git a/third_party/rust/glean/src/private/recorded_experiment_data.rs b/third_party/rust/glean/src/private/recorded_experiment_data.rs new file mode 100644 index 0000000000..0550b536a7 --- /dev/null +++ b/third_party/rust/glean/src/private/recorded_experiment_data.rs @@ -0,0 +1,15 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. + +use crate::HashMap; +use serde::Deserialize; + +/// Deserialized experiment data. +#[derive(Clone, Deserialize, Debug)] +pub struct RecordedExperimentData { + /// The experiment's branch as set through [`set_experiment_active`](crate::set_experiment_active). + pub branch: String, + /// Any extra data associated with this experiment through [`set_experiment_active`](crate::set_experiment_active). + pub extra: Option<HashMap<String, String>>, +} diff --git a/third_party/rust/glean/src/private/string.rs b/third_party/rust/glean/src/private/string.rs new file mode 100644 index 0000000000..0f11a016a9 --- /dev/null +++ b/third_party/rust/glean/src/private/string.rs @@ -0,0 +1,72 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. + +use glean_core::Glean; +use inherent::inherent; +use std::sync::Arc; + +use glean_core::metrics::MetricType; +use glean_core::ErrorType; + +use crate::dispatcher; + +// We need to wrap the glean-core type, otherwise if we try to implement +// the trait for the metric in `glean_core::metrics` we hit error[E0117]: +// only traits defined in the current crate can be implemented for arbitrary +// types. + +/// This implements the developer facing API for recording string metrics. +/// +/// Instances of this class type are automatically generated by the parsers +/// at build time, allowing developers to record values that were previously +/// registered in the metrics.yaml file. +#[derive(Clone)] +pub struct StringMetric(pub(crate) Arc<glean_core::metrics::StringMetric>); + +impl StringMetric { + /// The public constructor used by automatically generated metrics. + pub fn new(meta: glean_core::CommonMetricData) -> Self { + Self(Arc::new(glean_core::metrics::StringMetric::new(meta))) + } + + /// Internal only, synchronous API for setting a string value. + pub(crate) fn set_sync<S: Into<std::string::String>>(&self, glean: &Glean, value: S) { + self.0.set(glean, value); + } +} + +#[inherent(pub)] +impl glean_core::traits::String for StringMetric { + fn set<S: Into<std::string::String>>(&self, value: S) { + let metric = Arc::clone(&self.0); + let new_value = value.into(); + dispatcher::launch(move || crate::with_glean(|glean| metric.set(glean, new_value))); + } + + fn test_get_value<'a, S: Into<Option<&'a str>>>( + &self, + ping_name: S, + ) -> Option<std::string::String> { + crate::block_on_dispatcher(); + + let queried_ping_name = ping_name + .into() + .unwrap_or_else(|| &self.0.meta().send_in_pings[0]); + + crate::with_glean(|glean| self.0.test_get_value(glean, queried_ping_name)) + } + + fn test_get_num_recorded_errors<'a, S: Into<Option<&'a str>>>( + &self, + error: ErrorType, + ping_name: S, + ) -> i32 { + crate::block_on_dispatcher(); + + crate::with_glean_mut(|glean| { + glean_core::test_get_num_recorded_errors(&glean, self.0.meta(), error, ping_name.into()) + .unwrap_or(0) + }) + } +} diff --git a/third_party/rust/glean/src/private/string_list.rs b/third_party/rust/glean/src/private/string_list.rs new file mode 100644 index 0000000000..e37bec4fa6 --- /dev/null +++ b/third_party/rust/glean/src/private/string_list.rs @@ -0,0 +1,108 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. + +use inherent::inherent; +use std::sync::Arc; + +use glean_core::metrics::MetricType; +use glean_core::ErrorType; + +use crate::dispatcher; + +// We need to wrap the glean-core type: otherwise if we try to implement +// the trait for the metric in `glean_core::metrics` we hit error[E0117]: +// only traits defined in the current crate can be implemented for arbitrary +// types. + +/// This implements the developer-facing API for recording string list metrics. +/// +/// Instances of this class type are automatically generated by the parsers +/// at build time, allowing developers to record values that were previously +/// registered in the metrics.yaml file. +#[derive(Clone)] +pub struct StringListMetric(pub(crate) Arc<glean_core::metrics::StringListMetric>); + +impl StringListMetric { + /// The public constructor used by automatically generated metrics. + pub fn new(meta: glean_core::CommonMetricData) -> Self { + Self(Arc::new(glean_core::metrics::StringListMetric::new(meta))) + } +} + +#[inherent(pub)] +impl glean_core::traits::StringList for StringListMetric { + fn add<S: Into<String>>(&self, value: S) { + let metric = Arc::clone(&self.0); + let new_value = value.into(); + dispatcher::launch(move || crate::with_glean(|glean| metric.add(glean, new_value))); + } + + fn set(&self, value: Vec<String>) { + let metric = Arc::clone(&self.0); + dispatcher::launch(move || crate::with_glean(|glean| metric.set(glean, value))); + } + + fn test_get_value<'a, S: Into<Option<&'a str>>>(&self, ping_name: S) -> Option<Vec<String>> { + crate::block_on_dispatcher(); + + let queried_ping_name = ping_name + .into() + .unwrap_or_else(|| &self.0.meta().send_in_pings[0]); + + crate::with_glean(|glean| self.0.test_get_value(glean, queried_ping_name)) + } + + fn test_get_num_recorded_errors<'a, S: Into<Option<&'a str>>>( + &self, + error: ErrorType, + ping_name: S, + ) -> i32 { + crate::block_on_dispatcher(); + + crate::with_glean_mut(|glean| { + glean_core::test_get_num_recorded_errors(&glean, self.0.meta(), error, ping_name.into()) + .unwrap_or(0) + }) + } +} + +#[cfg(test)] +mod test { + use super::*; + use crate::common_test::{lock_test, new_glean}; + use crate::{CommonMetricData, ErrorType}; + + #[test] + fn string_list_metric_docs() { + let _lock = lock_test(); + let _t = new_glean(None, true); + + let engine_metric: StringListMetric = StringListMetric::new(CommonMetricData { + name: "event".into(), + category: "test".into(), + send_in_pings: vec!["test1".into()], + ..Default::default() + }); + + let engines: Vec<String> = vec!["Google".to_string(), "DuckDuckGo".to_string()]; + + // Add them one at a time + engines.iter().for_each(|x| engine_metric.add(x)); + + // Set them in one go + engine_metric.set(engines); + + assert!(engine_metric.test_get_value(None).is_some()); + + assert_eq!( + vec!["Google".to_string(), "DuckDuckGo".to_string()], + engine_metric.test_get_value(None).unwrap() + ); + + assert_eq!( + 0, + engine_metric.test_get_num_recorded_errors(ErrorType::InvalidValue, None) + ); + } +} diff --git a/third_party/rust/glean/src/private/timespan.rs b/third_party/rust/glean/src/private/timespan.rs new file mode 100644 index 0000000000..111d3b2d4a --- /dev/null +++ b/third_party/rust/glean/src/private/timespan.rs @@ -0,0 +1,163 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. + +use std::sync::{Arc, RwLock}; + +use inherent::inherent; + +use glean_core::metrics::{MetricType, TimeUnit}; +use glean_core::ErrorType; + +use crate::dispatcher; + +// We need to wrap the glean-core type: otherwise if we try to implement +// the trait for the metric in `glean_core::metrics` we hit error[E0117]: +// only traits defined in the current crate can be implemented for arbitrary +// types. + +/// This implements the developer facing API for recording timespan metrics. +/// +/// Instances of this class type are automatically generated by the parsers +/// at build time, allowing developers to record values that were previously +/// registered in the metrics.yaml file. +#[derive(Clone)] +pub struct TimespanMetric(pub(crate) Arc<RwLock<glean_core::metrics::TimespanMetric>>); + +impl TimespanMetric { + /// The public constructor used by automatically generated metrics. + pub fn new(meta: glean_core::CommonMetricData, time_unit: TimeUnit) -> Self { + let timespan = glean_core::metrics::TimespanMetric::new(meta, time_unit); + Self(Arc::new(RwLock::new(timespan))) + } +} + +#[inherent(pub)] +impl glean_core::traits::Timespan for TimespanMetric { + fn start(&self) { + let start_time = time::precise_time_ns(); + + let metric = Arc::clone(&self.0); + dispatcher::launch(move || { + crate::with_glean(|glean| { + let mut lock = metric + .write() + .expect("Lock poisoned for timespan metric on start."); + lock.set_start(glean, start_time) + }) + }); + } + + fn stop(&self) { + let stop_time = time::precise_time_ns(); + + let metric = Arc::clone(&self.0); + dispatcher::launch(move || { + crate::with_glean(|glean| { + let mut lock = metric + .write() + .expect("Lock poisoned for timespan metric on stop."); + lock.set_stop(glean, stop_time) + }) + }); + } + + fn cancel(&self) { + let metric = Arc::clone(&self.0); + dispatcher::launch(move || { + let mut lock = metric + .write() + .expect("Lock poisoned for timespan metric on cancel."); + lock.cancel() + }); + } + + fn test_get_value<'a, S: Into<Option<&'a str>>>(&self, ping_name: S) -> Option<u64> { + crate::block_on_dispatcher(); + + crate::with_glean(|glean| { + // Note: The order of operations is important here to avoid potential deadlocks because + // of `lock-order-inversion`. + // `with_glean` takes a lock on the global Glean object, + // then we take a lock on the metric itself here. + // + // Other parts do it in the same order, see for example `start`. + let metric = self + .0 + .read() + .expect("Lock poisoned for timespan metric on test_get_value."); + + let queried_ping_name = ping_name + .into() + .unwrap_or_else(|| &metric.meta().send_in_pings[0]); + metric.test_get_value(glean, queried_ping_name) + }) + } + + fn test_get_num_recorded_errors<'a, S: Into<Option<&'a str>>>( + &self, + error: ErrorType, + ping_name: S, + ) -> i32 { + crate::block_on_dispatcher(); + + let metric = self + .0 + .read() + .expect("Lock poisoned for timespan metric on test_get_value."); + + crate::with_glean_mut(|glean| { + glean_core::test_get_num_recorded_errors(&glean, metric.meta(), error, ping_name.into()) + .unwrap_or(0) + }) + } +} + +#[cfg(test)] +mod test { + use std::{thread, time::Duration}; + + use super::*; + use crate::common_test::{lock_test, new_glean}; + use crate::CommonMetricData; + + #[test] + fn timespan_convenient_api() { + let _lock = lock_test(); + let _t = new_glean(None, true); + + let metric: TimespanMetric = TimespanMetric::new( + CommonMetricData { + name: "timespan".into(), + category: "test".into(), + send_in_pings: vec!["test1".into()], + ..Default::default() + }, + TimeUnit::Millisecond, + ); + + // Canceling doesn't store data. + metric.start(); + metric.cancel(); + assert!(metric.test_get_value(None).is_none()); + + // Starting and stopping measures time. + metric.start(); + thread::sleep(Duration::from_millis(10)); + metric.stop(); + assert!(10 <= metric.test_get_value(None).unwrap()); + + // No errors + assert_eq!( + metric.test_get_num_recorded_errors(ErrorType::InvalidState, None), + 0 + ); + + // Stopping without starting is an error + metric.stop(); + assert_eq!( + metric.test_get_num_recorded_errors(ErrorType::InvalidState, None), + 1 + ) + } +} diff --git a/third_party/rust/glean/src/private/timing_distribution.rs b/third_party/rust/glean/src/private/timing_distribution.rs new file mode 100644 index 0000000000..5e1a9f930f --- /dev/null +++ b/third_party/rust/glean/src/private/timing_distribution.rs @@ -0,0 +1,99 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. + +use inherent::inherent; +use std::sync::{Arc, RwLock}; + +use glean_core::metrics::{DistributionData, MetricType, TimeUnit, TimerId}; +use glean_core::ErrorType; + +use crate::dispatcher; + +// We need to wrap the glean-core type: otherwise if we try to implement +// the trait for the metric in `glean_core::metrics` we hit error[E0117]: +// only traits defined in the current crate can be implemented for arbitrary +// types. + +/// This implements the developer-facing API for recording timing distribution metrics. +/// +/// Instances of this class type are automatically generated by the parsers +/// at build time, allowing developers to record values that were previously +/// registered in the metrics.yaml file. +#[derive(Clone)] +pub struct TimingDistributionMetric( + pub(crate) Arc<RwLock<glean_core::metrics::TimingDistributionMetric>>, +); + +impl TimingDistributionMetric { + /// The public constructor used by automatically generated metrics. + pub fn new(meta: glean_core::CommonMetricData, time_unit: TimeUnit) -> Self { + Self(Arc::new(RwLock::new( + glean_core::metrics::TimingDistributionMetric::new(meta, time_unit), + ))) + } +} + +#[inherent(pub)] +impl glean_core::traits::TimingDistribution for TimingDistributionMetric { + fn start(&self) -> TimerId { + let start_time = time::precise_time_ns(); + self.0.write().unwrap().set_start(start_time) + } + + fn stop_and_accumulate(&self, id: TimerId) { + let stop_time = time::precise_time_ns(); + let metric = Arc::clone(&self.0); + dispatcher::launch(move || { + crate::with_glean(|glean| { + metric + .write() + .unwrap() + .set_stop_and_accumulate(glean, id, stop_time) + }) + }); + } + + fn cancel(&self, id: TimerId) { + let metric = Arc::clone(&self.0); + dispatcher::launch(move || metric.write().unwrap().cancel(id)); + } + + fn test_get_value<'a, S: Into<Option<&'a str>>>( + &self, + ping_name: S, + ) -> Option<DistributionData> { + crate::block_on_dispatcher(); + + crate::with_glean(|glean| { + // The order of taking these locks matter. Glean must be first. + let inner = self + .0 + .read() + .expect("Lock poisoned for timing distribution metric on test_get_value."); + let queried_ping_name = ping_name + .into() + .unwrap_or_else(|| &inner.meta().send_in_pings[0]); + + inner.test_get_value(glean, queried_ping_name) + }) + } + + fn test_get_num_recorded_errors<'a, S: Into<Option<&'a str>>>( + &self, + error: ErrorType, + ping_name: S, + ) -> i32 { + crate::block_on_dispatcher(); + + crate::with_glean_mut(|glean| { + glean_core::test_get_num_recorded_errors( + &glean, + self.0.read().unwrap().meta(), + error, + ping_name.into(), + ) + .unwrap_or(0) + }) + } +} diff --git a/third_party/rust/glean/src/private/uuid.rs b/third_party/rust/glean/src/private/uuid.rs new file mode 100644 index 0000000000..fc82b67b8c --- /dev/null +++ b/third_party/rust/glean/src/private/uuid.rs @@ -0,0 +1,69 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. + +use inherent::inherent; +use std::sync::Arc; + +use glean_core::metrics::MetricType; +use glean_core::ErrorType; + +use crate::dispatcher; + +// We need to wrap the glean-core type, otherwise if we try to implement +// the trait for the metric in `glean_core::metrics` we hit error[E0117]: +// only traits defined in the current crate can be implemented for arbitrary +// types. + +/// This implements the developer facing API for recording UUID metrics. +/// +/// Instances of this class type are automatically generated by the parsers +/// at build time, allowing developers to record values that were previously +/// registered in the metrics.yaml file. +#[derive(Clone)] +pub struct UuidMetric(pub(crate) Arc<glean_core::metrics::UuidMetric>); + +impl UuidMetric { + /// The public constructor used by automatically generated metrics. + pub fn new(meta: glean_core::CommonMetricData) -> Self { + Self(Arc::new(glean_core::metrics::UuidMetric::new(meta))) + } +} + +#[inherent(pub)] +impl glean_core::traits::Uuid for UuidMetric { + fn set(&self, value: uuid::Uuid) { + let metric = Arc::clone(&self.0); + dispatcher::launch(move || crate::with_glean(|glean| metric.set(glean, value))); + } + + fn generate_and_set(&self) -> uuid::Uuid { + // TODO: We can use glean-core's generate_and_set after bug 1673017. + let uuid = uuid::Uuid::new_v4(); + self.set(uuid); + uuid + } + + fn test_get_value<'a, S: Into<Option<&'a str>>>(&self, ping_name: S) -> Option<uuid::Uuid> { + crate::block_on_dispatcher(); + + let queried_ping_name = ping_name + .into() + .unwrap_or_else(|| &self.0.meta().send_in_pings[0]); + + crate::with_glean(|glean| self.0.test_get_value(glean, queried_ping_name)) + } + + fn test_get_num_recorded_errors<'a, S: Into<Option<&'a str>>>( + &self, + error: ErrorType, + ping_name: S, + ) -> i32 { + crate::block_on_dispatcher(); + + crate::with_glean_mut(|glean| { + glean_core::test_get_num_recorded_errors(&glean, self.0.meta(), error, ping_name.into()) + .unwrap_or(0) + }) + } +} |