Add a Datetime metric type in the RLB

This commit is contained in:
Alessio Placitelli 2020-12-10 17:16:38 +01:00
Родитель 29cd9970a4
Коммит 895fa243e5
5 изменённых файлов: 166 добавлений и 1 удалений

1
Cargo.lock сгенерированный
Просмотреть файл

@ -436,6 +436,7 @@ dependencies = [
name = "glean"
version = "33.8.0"
dependencies = [
"chrono",
"crossbeam-channel 0.4.4",
"env_logger",
"flate2",

Просмотреть файл

@ -33,6 +33,7 @@ thiserror = "1.0.4"
serde_json = "1.0.44"
serde = { version = "1.0.104", features = ["derive"] }
uuid = { version = "0.8.1", features = ["v4"] }
chrono = { version = "0.4.10", features = ["serde"] }
time = "0.1.40"
[dev-dependencies]

Просмотреть файл

@ -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> {
dispatcher::block_on_queue();
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 {
dispatcher::block_on_queue();
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
)
}
}

Просмотреть файл

@ -6,6 +6,7 @@
mod boolean;
mod counter;
mod datetime;
mod event;
mod labeled;
mod memory_distribution;
@ -20,6 +21,7 @@ mod uuid;
pub use self::uuid::UuidMetric;
pub use boolean::BooleanMetric;
pub use counter::CounterMetric;
pub use datetime::{Datetime, DatetimeMetric};
pub use event::EventMetric;
pub use labeled::{AllowLabeled, LabeledMetric};
pub use memory_distribution::MemoryDistributionMetric;

Просмотреть файл

@ -13,7 +13,7 @@ use crate::util::{get_iso_time_string, local_now_with_offset};
use crate::CommonMetricData;
use crate::Glean;
use chrono::{DateTime, FixedOffset, TimeZone};
use chrono::{DateTime, FixedOffset, TimeZone, Timelike};
/// A datetime type.
///
@ -143,6 +143,64 @@ impl DatetimeMetric {
}
}
/// **Test-only API (exported for FFI purposes).**
///
/// Gets the stored datetime value.
///
/// The precision of this value is truncated to the `time_unit` precision.
///
/// # Arguments
///
/// * `glean` - the Glean instance this metric belongs to.
/// * `storage_name` - the storage name to look into.
///
/// # Returns
///
/// The stored value or `None` if nothing stored.
pub fn test_get_value(&self, glean: &Glean, storage_name: &str) -> Option<Datetime> {
match StorageManager.snapshot_metric(
glean.storage(),
storage_name,
&self.meta.identifier(glean),
) {
Some(Metric::Datetime(d, tu)) => {
// The string version of the test function truncates using string
// parsing. Unfortunately `parse_from_str` errors with `NotEnough` if we
// try to truncate with `get_iso_time_string` and then parse it back
// in a `Datetime`. So we need to truncate manually.
let time = d.time();
match tu {
TimeUnit::Nanosecond => d.date().and_hms_nano_opt(
time.hour(),
time.minute(),
time.second(),
time.nanosecond(),
),
TimeUnit::Microsecond => d.date().and_hms_nano_opt(
time.hour(),
time.minute(),
time.second(),
time.nanosecond() / 1000,
),
TimeUnit::Millisecond => d.date().and_hms_nano_opt(
time.hour(),
time.minute(),
time.second(),
time.nanosecond() / 1000000,
),
TimeUnit::Second => {
d.date()
.and_hms_nano_opt(time.hour(), time.minute(), time.second(), 0)
}
TimeUnit::Minute => d.date().and_hms_nano_opt(time.hour(), time.minute(), 0, 0),
TimeUnit::Hour => d.date().and_hms_nano_opt(time.hour(), 0, 0, 0),
TimeUnit::Day => d.date().and_hms_nano_opt(0, 0, 0, 0),
}
}
_ => None,
}
}
/// **Test-only API (exported for FFI purposes).**
///
/// Gets the currently stored value as a String.