зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1632162 - Implement the event metric type for Project FOG. r=chutten
Differential Revision: https://phabricator.services.mozilla.com/D73680
This commit is contained in:
Родитель
10f138dc45
Коммит
494496e368
|
@ -0,0 +1,127 @@
|
|||
// 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::collections::HashMap;
|
||||
use std::hash::Hash;
|
||||
use std::marker::PhantomData;
|
||||
|
||||
use super::{CommonMetricData, Instant};
|
||||
|
||||
/// Extra keys for events.
|
||||
///
|
||||
/// Extra keys need to be pre-defined and map to a string representation.
|
||||
///
|
||||
/// For user-defined `EventMetric`s these will be defined as `enums`.
|
||||
/// Each variant will correspond to an entry in the `ALLOWED_KEYS` list.
|
||||
/// The Glean SDK requires the keys as strings for submission in pings,
|
||||
/// whereas in code we want to provide users a type to work with
|
||||
/// (e.g. to avoid typos or misuse of the API).
|
||||
pub trait ExtraKeys: Hash + Eq + PartialEq + Copy {
|
||||
/// List of allowed extra keys as strings.
|
||||
const ALLOWED_KEYS: &'static [&'static str];
|
||||
|
||||
/// The index of the extra key.
|
||||
///
|
||||
/// It corresponds to its position in the associated `ALLOWED_KEYS` list.
|
||||
///
|
||||
/// *Note*: An index of `-1` indicates an invalid / non-existing extra key.
|
||||
/// Invalid / non-existing extra keys will be recorded as an error.
|
||||
/// This cannot happen for generated code.
|
||||
fn index(self) -> i32;
|
||||
}
|
||||
|
||||
/// Default of no extra keys for events.
|
||||
///
|
||||
/// An enum with no values for convenient use as the default set of extra keys
|
||||
/// that an `EventMetric` can accept.
|
||||
///
|
||||
/// *Note*: There exist no values for this enum, it can never exist.
|
||||
/// It its equivalent to the [`never / !` type](https://doc.rust-lang.org/std/primitive.never.html).
|
||||
#[derive(Clone, Copy, Hash, Eq, PartialEq)]
|
||||
pub enum NoExtraKeys {}
|
||||
|
||||
impl ExtraKeys for NoExtraKeys {
|
||||
const ALLOWED_KEYS: &'static [&'static str] = &[];
|
||||
|
||||
fn index(self) -> i32 {
|
||||
// This index will never be used.
|
||||
-1
|
||||
}
|
||||
}
|
||||
|
||||
/// An event metric.
|
||||
///
|
||||
/// Events allow recording of e.g. individual occurences of user actions, say
|
||||
/// every time a view was open and from where. Each time you record an event, it
|
||||
/// records a timestamp, the event's name and a set of custom values.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct EventMetric<K> {
|
||||
inner: glean_core::metrics::EventMetric,
|
||||
extra_keys: PhantomData<K>,
|
||||
}
|
||||
|
||||
impl<K: ExtraKeys> EventMetric<K> {
|
||||
/// Create a new event metric.
|
||||
pub fn new(meta: CommonMetricData) -> Self {
|
||||
let allowed_extra_keys = K::ALLOWED_KEYS.iter().map(|s| s.to_string()).collect();
|
||||
let inner = glean_core::metrics::EventMetric::new(meta, allowed_extra_keys);
|
||||
|
||||
Self {
|
||||
inner,
|
||||
extra_keys: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
/// Record an event.
|
||||
///
|
||||
/// Records an event under this metric's name and attaches a timestamp.
|
||||
/// If given a map of extra values is added to the event log.
|
||||
///
|
||||
/// ## Arguments
|
||||
///
|
||||
/// * `extra` - An (optional) map of (key, value) pairs.
|
||||
pub fn record<M: Into<Option<HashMap<K, String>>>>(&self, extra: M) {
|
||||
let now = Instant::now();
|
||||
|
||||
// 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());
|
||||
|
||||
crate::with_glean(|glean| self.inner.record(glean, now.as_u64(), extra))
|
||||
}
|
||||
|
||||
/// **Test-only API.**
|
||||
///
|
||||
/// Get the currently stored events for this event metric as a JSON-encoded string.
|
||||
/// This doesn't clear the stored value.
|
||||
///
|
||||
/// ## Note
|
||||
///
|
||||
/// This currently returns the value as a JSON encoded string.
|
||||
/// `glean_core` doesn't expose the underlying recorded event type.
|
||||
/// This will eventually change to a proper `RecordedEventData` type.
|
||||
/// See [Bug 1635074](https://bugzilla.mozilla.org/show_bug.cgi?id=1635074).
|
||||
///
|
||||
/// ## Arguments
|
||||
///
|
||||
/// * `storage_name` - the storage name to look into.
|
||||
///
|
||||
/// ## Return value
|
||||
///
|
||||
/// Returns the stored value or `None` if nothing stored.
|
||||
pub fn test_get_value(&self, storage_name: &str) -> Option<String> {
|
||||
crate::with_glean(|glean| {
|
||||
// Unfortunately we need to do this branch ourselves right now,
|
||||
// as `None` is encoded into the JSON `null`.
|
||||
|
||||
let inner = &self.inner;
|
||||
if inner.test_has_value(glean, storage_name) {
|
||||
Some(inner.test_get_value_as_json_string(glean, storage_name))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
|
@ -14,6 +14,7 @@ pub use glean_core::{metrics::TimeUnit, CommonMetricData, ErrorType, Lifetime};
|
|||
|
||||
mod boolean;
|
||||
mod counter;
|
||||
mod event;
|
||||
mod labeled;
|
||||
mod ping;
|
||||
mod string;
|
||||
|
@ -24,6 +25,7 @@ mod uuid;
|
|||
|
||||
pub use self::boolean::BooleanMetric;
|
||||
pub use self::counter::CounterMetric;
|
||||
pub use self::event::{EventMetric, ExtraKeys, NoExtraKeys};
|
||||
pub use self::labeled::LabeledMetric;
|
||||
pub use self::ping::Ping;
|
||||
pub use self::string::StringMetric;
|
||||
|
|
|
@ -0,0 +1,76 @@
|
|||
// 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/.
|
||||
|
||||
mod common;
|
||||
use common::*;
|
||||
|
||||
use std::collections::HashMap;
|
||||
|
||||
use glean::metrics::{CommonMetricData, EventMetric, ExtraKeys, Lifetime, NoExtraKeys};
|
||||
|
||||
#[test]
|
||||
fn smoke_test_event() {
|
||||
let _lock = lock_test();
|
||||
let _t = setup_glean(None);
|
||||
|
||||
let metric = EventMetric::<NoExtraKeys>::new(CommonMetricData {
|
||||
name: "event_metric".into(),
|
||||
category: "telemetry".into(),
|
||||
send_in_pings: vec!["store1".into()],
|
||||
disabled: false,
|
||||
lifetime: Lifetime::Ping,
|
||||
..Default::default()
|
||||
});
|
||||
|
||||
// No extra keys
|
||||
metric.record(None);
|
||||
|
||||
let recorded = metric.test_get_value("store1").unwrap();
|
||||
|
||||
// We roughly inspect the JSON-encoded values only.
|
||||
assert!(recorded.contains("event_metric"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn smoke_test_event_with_extra() {
|
||||
let _lock = lock_test();
|
||||
let _t = setup_glean(None);
|
||||
|
||||
#[derive(Clone, Copy, Hash, Eq, PartialEq)]
|
||||
enum TestKeys {
|
||||
Extra1,
|
||||
}
|
||||
|
||||
impl ExtraKeys for TestKeys {
|
||||
const ALLOWED_KEYS: &'static [&'static str] = &["extra1"];
|
||||
|
||||
fn index(self) -> i32 {
|
||||
self as i32
|
||||
}
|
||||
}
|
||||
|
||||
let metric = EventMetric::<TestKeys>::new(CommonMetricData {
|
||||
name: "event_metric".into(),
|
||||
category: "telemetry".into(),
|
||||
send_in_pings: vec!["store1".into()],
|
||||
disabled: false,
|
||||
lifetime: Lifetime::Ping,
|
||||
..Default::default()
|
||||
});
|
||||
|
||||
// No extra keys
|
||||
metric.record(None);
|
||||
|
||||
// A valid extra key
|
||||
let mut map = HashMap::new();
|
||||
map.insert(TestKeys::Extra1, "a-valid-value".into());
|
||||
metric.record(map);
|
||||
|
||||
let recorded = metric.test_get_value("store1").unwrap();
|
||||
|
||||
// We roughly inspect the JSON-encoded values only.
|
||||
assert!(recorded.contains("event_metric"));
|
||||
assert!(recorded.contains("extra1"));
|
||||
assert!(recorded.contains("a-valid-value"));
|
||||
}
|
Загрузка…
Ссылка в новой задаче