use std::collections::HashMap;
use crate::error_recording::{record_error, test_get_num_recorded_errors, ErrorType};
use crate::event_database::RecordedEvent;
use crate::metrics::MetricType;
use crate::util::truncate_string_at_boundary_with_error;
use crate::CommonMetricData;
use crate::Glean;
const MAX_LENGTH_EXTRA_KEY_VALUE: usize = 100;
#[derive(Clone, Debug)]
pub struct EventMetric {
meta: CommonMetricData,
allowed_extra_keys: Vec<String>,
}
impl MetricType for EventMetric {
fn meta(&self) -> &CommonMetricData {
&self.meta
}
}
impl EventMetric {
pub fn new(meta: CommonMetricData, allowed_extra_keys: Vec<String>) -> Self {
Self {
meta,
allowed_extra_keys,
}
}
pub fn record(&self, extra: HashMap<String, String>) {
let timestamp = crate::get_timestamp_ms();
self.record_with_time(timestamp, extra);
}
pub fn record_with_time(&self, timestamp: u64, extra: HashMap<String, String>) {
let metric = self.clone();
crate::launch_with_glean(move |glean| metric.record_sync(glean, timestamp, extra));
}
fn validate_extra(
&self,
glean: &Glean,
extra: HashMap<String, String>,
) -> Result<Option<HashMap<String, String>>, ()> {
if extra.is_empty() {
return Ok(None);
}
let mut extra_strings = HashMap::new();
for (k, v) in extra.into_iter() {
if !self.allowed_extra_keys.contains(&k) {
let msg = format!("Invalid key index {}", k);
record_error(glean, &self.meta, ErrorType::InvalidValue, msg, None);
return Err(());
}
let value = truncate_string_at_boundary_with_error(
glean,
&self.meta,
v,
MAX_LENGTH_EXTRA_KEY_VALUE,
);
extra_strings.insert(k, value);
}
Ok(Some(extra_strings))
}
#[doc(hidden)]
pub fn record_sync(&self, glean: &Glean, timestamp: u64, extra: HashMap<String, String>) {
if !self.should_record(glean) {
return;
}
let extra_strings = match self.validate_extra(glean, extra) {
Ok(extra) => extra,
Err(()) => return,
};
glean
.event_storage()
.record(glean, &self.meta, timestamp, extra_strings);
}
#[doc(hidden)]
pub fn get_value<'a, S: Into<Option<&'a str>>>(
&self,
glean: &Glean,
ping_name: S,
) -> Option<Vec<RecordedEvent>> {
let queried_ping_name = ping_name
.into()
.unwrap_or_else(|| &self.meta().send_in_pings[0]);
glean
.event_storage()
.test_get_value(&self.meta, queried_ping_name)
}
pub fn test_get_value(&self, ping_name: Option<String>) -> Option<Vec<RecordedEvent>> {
crate::block_on_dispatcher();
crate::core::with_glean(|glean| self.get_value(glean, ping_name.as_deref()))
}
pub fn test_get_num_recorded_errors(&self, error: ErrorType, ping_name: Option<String>) -> i32 {
crate::block_on_dispatcher();
crate::core::with_glean(|glean| {
test_get_num_recorded_errors(glean, self.meta(), error, ping_name.as_deref())
.unwrap_or(0)
})
}
}