use std::collections::HashMap;
use std::sync::atomic::Ordering;
use chrono::{DateTime, FixedOffset};
use serde::{Deserialize, Serialize};
use serde_json::{json, Value as JsonValue};
mod boolean;
mod counter;
mod custom_distribution;
mod datetime;
mod denominator;
mod event;
mod experiment;
pub(crate) mod labeled;
mod memory_distribution;
mod memory_unit;
mod metrics_disabled_config;
mod numerator;
mod ping;
mod quantity;
mod rate;
mod recorded_experiment;
mod string;
mod string_list;
mod text;
mod time_unit;
mod timespan;
mod timing_distribution;
mod url;
mod uuid;
use crate::common_metric_data::CommonMetricDataInternal;
pub use crate::event_database::RecordedEvent;
use crate::histogram::{Functional, Histogram, PrecomputedExponential, PrecomputedLinear};
pub use crate::metrics::datetime::Datetime;
use crate::util::get_iso_time_string;
use crate::Glean;
pub use self::boolean::BooleanMetric;
pub use self::counter::CounterMetric;
pub use self::custom_distribution::CustomDistributionMetric;
pub use self::datetime::DatetimeMetric;
pub use self::denominator::DenominatorMetric;
pub use self::event::EventMetric;
pub(crate) use self::experiment::ExperimentMetric;
pub use self::labeled::{LabeledBoolean, LabeledCounter, LabeledMetric, LabeledString};
pub use self::memory_distribution::MemoryDistributionMetric;
pub use self::memory_unit::MemoryUnit;
pub use self::numerator::NumeratorMetric;
pub use self::ping::PingType;
pub use self::quantity::QuantityMetric;
pub use self::rate::{Rate, RateMetric};
pub use self::string::StringMetric;
pub use self::string_list::StringListMetric;
pub use self::text::TextMetric;
pub use self::time_unit::TimeUnit;
pub use self::timespan::TimespanMetric;
pub use self::timing_distribution::TimerId;
pub use self::timing_distribution::TimingDistributionMetric;
pub use self::url::UrlMetric;
pub use self::uuid::UuidMetric;
pub use crate::histogram::HistogramType;
pub use recorded_experiment::RecordedExperiment;
pub use self::metrics_disabled_config::MetricsDisabledConfig;
#[derive(Debug, Serialize)]
pub struct DistributionData {
pub values: HashMap<i64, i64>,
pub sum: i64,
#[serde(skip)]
pub count: i64,
}
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
pub enum Metric {
Boolean(bool),
Counter(i32),
CustomDistributionExponential(Histogram<PrecomputedExponential>),
CustomDistributionLinear(Histogram<PrecomputedLinear>),
Datetime(DateTime<FixedOffset>, TimeUnit),
Experiment(recorded_experiment::RecordedExperiment),
Quantity(i64),
String(String),
StringList(Vec<String>),
Uuid(String),
Timespan(std::time::Duration, TimeUnit),
TimingDistribution(Histogram<Functional>),
MemoryDistribution(Histogram<Functional>),
Jwe(String),
Rate(i32, i32),
Url(String),
Text(String),
}
pub trait MetricType {
fn meta(&self) -> &CommonMetricDataInternal;
fn with_name(&self, _name: String) -> Self
where
Self: Sized,
{
unimplemented!()
}
fn with_dynamic_label(&self, _label: String) -> Self
where
Self: Sized,
{
unimplemented!()
}
fn should_record(&self, glean: &Glean) -> bool {
if !glean.is_upload_enabled() {
return false;
}
let disabled_field = self.meta().disabled.load(Ordering::Relaxed);
let epoch = disabled_field >> 4;
let disabled = disabled_field & 0xF;
let remote_settings_epoch = glean.remote_settings_epoch.load(Ordering::Acquire);
if epoch == remote_settings_epoch {
return disabled == 0;
}
let metrics_disabled = &glean
.remote_settings_metrics_config
.lock()
.unwrap()
.metrics_disabled;
let current_disabled = {
let base_id = self.meta().base_identifier();
let identifier = base_id
.split_once('/')
.map(|split| split.0)
.unwrap_or(&base_id);
if let Some(is_disabled) = metrics_disabled.get(identifier) {
u8::from(*is_disabled)
} else {
u8::from(self.meta().inner.disabled)
}
};
let new_disabled = (remote_settings_epoch << 4) | (current_disabled & 0xF);
self.meta().disabled.store(new_disabled, Ordering::Relaxed);
current_disabled == 0
}
}
impl Metric {
pub fn ping_section(&self) -> &'static str {
match self {
Metric::Boolean(_) => "boolean",
Metric::Counter(_) => "counter",
Metric::CustomDistributionExponential(_) => "custom_distribution",
Metric::CustomDistributionLinear(_) => "custom_distribution",
Metric::Datetime(_, _) => "datetime",
Metric::Experiment(_) => panic!("Experiments should not be serialized through this"),
Metric::Quantity(_) => "quantity",
Metric::Rate(..) => "rate",
Metric::String(_) => "string",
Metric::StringList(_) => "string_list",
Metric::Timespan(..) => "timespan",
Metric::TimingDistribution(_) => "timing_distribution",
Metric::Url(_) => "url",
Metric::Uuid(_) => "uuid",
Metric::MemoryDistribution(_) => "memory_distribution",
Metric::Jwe(_) => "jwe",
Metric::Text(_) => "text",
}
}
pub fn as_json(&self) -> JsonValue {
match self {
Metric::Boolean(b) => json!(b),
Metric::Counter(c) => json!(c),
Metric::CustomDistributionExponential(hist) => {
json!(custom_distribution::snapshot(hist))
}
Metric::CustomDistributionLinear(hist) => json!(custom_distribution::snapshot(hist)),
Metric::Datetime(d, time_unit) => json!(get_iso_time_string(*d, *time_unit)),
Metric::Experiment(e) => e.as_json(),
Metric::Quantity(q) => json!(q),
Metric::Rate(num, den) => {
json!({"numerator": num, "denominator": den})
}
Metric::String(s) => json!(s),
Metric::StringList(v) => json!(v),
Metric::Timespan(time, time_unit) => {
json!({"value": time_unit.duration_convert(*time), "time_unit": time_unit})
}
Metric::TimingDistribution(hist) => json!(timing_distribution::snapshot(hist)),
Metric::Url(s) => json!(s),
Metric::Uuid(s) => json!(s),
Metric::MemoryDistribution(hist) => json!(memory_distribution::snapshot(hist)),
Metric::Jwe(s) => json!(s),
Metric::Text(s) => json!(s),
}
}
}