summaryrefslogtreecommitdiffstats
path: root/third_party/rust/glean/src/private/event.rs
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/rust/glean/src/private/event.rs')
-rw-r--r--third_party/rust/glean/src/private/event.rs173
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);
+ }
+}