diff options
Diffstat (limited to 'third_party/rust/glean/src/private/event.rs')
-rw-r--r-- | third_party/rust/glean/src/private/event.rs | 173 |
1 files changed, 173 insertions, 0 deletions
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); + } +} |