зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1682638
- Update to Glean v33.9.1. r=Dexter
Differential Revision: https://phabricator.services.mozilla.com/D100006
This commit is contained in:
Родитель
a7af908f30
Коммит
04b73ee4f1
|
@ -2083,10 +2083,11 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "glean"
|
name = "glean"
|
||||||
version = "33.8.0"
|
version = "33.9.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b91b0b03e572ce70d06cf92ca899bdab8679e4ca07a2877928df028c848a1e9a"
|
checksum = "120a2f092bcf0b8fa0ec864e1a063426638f7084671eddfd138b5571e1e2c6fd"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"chrono",
|
||||||
"crossbeam-channel",
|
"crossbeam-channel",
|
||||||
"glean-core",
|
"glean-core",
|
||||||
"inherent",
|
"inherent",
|
||||||
|
@ -2101,9 +2102,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "glean-core"
|
name = "glean-core"
|
||||||
version = "33.8.0"
|
version = "33.9.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "440d35335296536f0cf46ca79b6edc4f93b3cfdaef2ce8696b7f7e6ee3d7b69f"
|
checksum = "b2c8e087b69c8392fca868214b4675230e36832f231cc6045d08caed0d33e3c1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bincode",
|
"bincode",
|
||||||
"chrono",
|
"chrono",
|
||||||
|
|
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
|
@ -180,7 +180,7 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "glean-core"
|
name = "glean-core"
|
||||||
version = "33.8.0"
|
version = "33.9.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bincode",
|
"bincode",
|
||||||
"chrono",
|
"chrono",
|
||||||
|
|
|
@ -13,7 +13,7 @@
|
||||||
[package]
|
[package]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
name = "glean-core"
|
name = "glean-core"
|
||||||
version = "33.8.0"
|
version = "33.9.1"
|
||||||
authors = ["Jan-Erik Rediger <jrediger@mozilla.com>", "The Glean Team <glean-team@mozilla.com>"]
|
authors = ["Jan-Erik Rediger <jrediger@mozilla.com>", "The Glean Team <glean-team@mozilla.com>"]
|
||||||
include = ["/README.md", "/LICENSE", "/src", "/examples", "/tests", "/Cargo.toml"]
|
include = ["/README.md", "/LICENSE", "/src", "/examples", "/tests", "/Cargo.toml"]
|
||||||
description = "A modern Telemetry library"
|
description = "A modern Telemetry library"
|
||||||
|
|
|
@ -48,7 +48,21 @@ mod backend {
|
||||||
pub type Writer<'t> = rkv::Writer<rkv::backend::SafeModeRwTransaction<'t>>;
|
pub type Writer<'t> = rkv::Writer<rkv::backend::SafeModeRwTransaction<'t>>;
|
||||||
|
|
||||||
pub fn rkv_new(path: &Path) -> Result<Rkv, rkv::StoreError> {
|
pub fn rkv_new(path: &Path) -> Result<Rkv, rkv::StoreError> {
|
||||||
Rkv::new::<rkv::backend::SafeMode>(path)
|
match Rkv::new::<rkv::backend::SafeMode>(path) {
|
||||||
|
// An invalid file can mean:
|
||||||
|
// 1. An empty file.
|
||||||
|
// 2. A corrupted file.
|
||||||
|
//
|
||||||
|
// In both instances there's not much we can do.
|
||||||
|
// Drop the data by removing the file, and start over.
|
||||||
|
Err(rkv::StoreError::FileInvalid) => {
|
||||||
|
let safebin = path.join("data.safe.bin");
|
||||||
|
fs::remove_file(safebin).map_err(|_| rkv::StoreError::FileInvalid)?;
|
||||||
|
// Now try again, we only handle that error once.
|
||||||
|
Rkv::new::<rkv::backend::SafeMode>(path)
|
||||||
|
}
|
||||||
|
other => other,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn delete_and_log(path: &Path, msg: &str) {
|
fn delete_and_log(path: &Path, msg: &str) {
|
||||||
|
@ -1308,11 +1322,71 @@ mod test {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// LDMB ignores an empty database file just fine.
|
||||||
|
#[cfg(not(feature = "rkv-safe-mode"))]
|
||||||
|
#[test]
|
||||||
|
fn empty_data_file() {
|
||||||
|
let dir = tempdir().unwrap();
|
||||||
|
let str_dir = dir.path().display().to_string();
|
||||||
|
|
||||||
|
// Create database directory structure.
|
||||||
|
let database_dir = dir.path().join("db");
|
||||||
|
fs::create_dir_all(&database_dir).expect("create database dir");
|
||||||
|
|
||||||
|
// Create empty database file.
|
||||||
|
let datamdb = database_dir.join("data.mdb");
|
||||||
|
let f = fs::File::create(datamdb).expect("create database file");
|
||||||
|
drop(f);
|
||||||
|
|
||||||
|
Database::new(&str_dir, false).unwrap();
|
||||||
|
|
||||||
|
assert!(dir.path().exists());
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(feature = "rkv-safe-mode")]
|
#[cfg(feature = "rkv-safe-mode")]
|
||||||
mod safe_mode_migration {
|
mod safe_mode {
|
||||||
|
use std::fs::File;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
use rkv::Value;
|
use rkv::Value;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn empty_data_file() {
|
||||||
|
let dir = tempdir().unwrap();
|
||||||
|
let str_dir = dir.path().display().to_string();
|
||||||
|
|
||||||
|
// Create database directory structure.
|
||||||
|
let database_dir = dir.path().join("db");
|
||||||
|
fs::create_dir_all(&database_dir).expect("create database dir");
|
||||||
|
|
||||||
|
// Create empty database file.
|
||||||
|
let safebin = database_dir.join("data.safe.bin");
|
||||||
|
let f = File::create(safebin).expect("create database file");
|
||||||
|
drop(f);
|
||||||
|
|
||||||
|
Database::new(&str_dir, false).unwrap();
|
||||||
|
|
||||||
|
assert!(dir.path().exists());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn corrupted_data_file() {
|
||||||
|
let dir = tempdir().unwrap();
|
||||||
|
let str_dir = dir.path().display().to_string();
|
||||||
|
|
||||||
|
// Create database directory structure.
|
||||||
|
let database_dir = dir.path().join("db");
|
||||||
|
fs::create_dir_all(&database_dir).expect("create database dir");
|
||||||
|
|
||||||
|
// Create empty database file.
|
||||||
|
let safebin = database_dir.join("data.safe.bin");
|
||||||
|
fs::write(safebin, "<broken>").expect("write to database file");
|
||||||
|
|
||||||
|
Database::new(&str_dir, false).unwrap();
|
||||||
|
|
||||||
|
assert!(dir.path().exists());
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn migration_works_on_startup() {
|
fn migration_works_on_startup() {
|
||||||
let dir = tempdir().unwrap();
|
let dir = tempdir().unwrap();
|
||||||
|
|
|
@ -49,6 +49,7 @@ use crate::debug::DebugOptions;
|
||||||
pub use crate::error::{Error, ErrorKind, Result};
|
pub use crate::error::{Error, ErrorKind, Result};
|
||||||
pub use crate::error_recording::{test_get_num_recorded_errors, ErrorType};
|
pub use crate::error_recording::{test_get_num_recorded_errors, ErrorType};
|
||||||
use crate::event_database::EventDatabase;
|
use crate::event_database::EventDatabase;
|
||||||
|
pub use crate::histogram::HistogramType;
|
||||||
use crate::internal_metrics::{CoreMetrics, DatabaseMetrics};
|
use crate::internal_metrics::{CoreMetrics, DatabaseMetrics};
|
||||||
use crate::internal_pings::InternalPings;
|
use crate::internal_pings::InternalPings;
|
||||||
use crate::metrics::{Metric, MetricType, PingType};
|
use crate::metrics::{Metric, MetricType, PingType};
|
||||||
|
@ -863,6 +864,7 @@ impl Glean {
|
||||||
self.storage(),
|
self.storage(),
|
||||||
INTERNAL_STORAGE,
|
INTERNAL_STORAGE,
|
||||||
&dirty_bit_metric.meta().identifier(self),
|
&dirty_bit_metric.meta().identifier(self),
|
||||||
|
dirty_bit_metric.meta().lifetime,
|
||||||
) {
|
) {
|
||||||
Some(Metric::Boolean(b)) => b,
|
Some(Metric::Boolean(b)) => b,
|
||||||
_ => false,
|
_ => false,
|
||||||
|
|
|
@ -61,6 +61,7 @@ impl BooleanMetric {
|
||||||
glean.storage(),
|
glean.storage(),
|
||||||
storage_name,
|
storage_name,
|
||||||
&self.meta.identifier(glean),
|
&self.meta.identifier(glean),
|
||||||
|
self.meta.lifetime,
|
||||||
) {
|
) {
|
||||||
Some(Metric::Boolean(b)) => Some(b),
|
Some(Metric::Boolean(b)) => Some(b),
|
||||||
_ => None,
|
_ => None,
|
||||||
|
|
|
@ -84,6 +84,7 @@ impl CounterMetric {
|
||||||
glean.storage(),
|
glean.storage(),
|
||||||
storage_name,
|
storage_name,
|
||||||
&self.meta.identifier(glean),
|
&self.meta.identifier(glean),
|
||||||
|
self.meta.lifetime,
|
||||||
) {
|
) {
|
||||||
Some(Metric::Counter(i)) => Some(i),
|
Some(Metric::Counter(i)) => Some(i),
|
||||||
_ => None,
|
_ => None,
|
||||||
|
|
|
@ -162,6 +162,7 @@ impl CustomDistributionMetric {
|
||||||
glean.storage(),
|
glean.storage(),
|
||||||
storage_name,
|
storage_name,
|
||||||
&self.meta.identifier(glean),
|
&self.meta.identifier(glean),
|
||||||
|
self.meta.lifetime,
|
||||||
) {
|
) {
|
||||||
// Boxing the value, in order to return either of the possible buckets
|
// Boxing the value, in order to return either of the possible buckets
|
||||||
Some(Metric::CustomDistributionExponential(hist)) => Some(snapshot(&hist)),
|
Some(Metric::CustomDistributionExponential(hist)) => Some(snapshot(&hist)),
|
||||||
|
|
|
@ -13,7 +13,7 @@ use crate::util::{get_iso_time_string, local_now_with_offset};
|
||||||
use crate::CommonMetricData;
|
use crate::CommonMetricData;
|
||||||
use crate::Glean;
|
use crate::Glean;
|
||||||
|
|
||||||
use chrono::{DateTime, FixedOffset, TimeZone};
|
use chrono::{DateTime, FixedOffset, TimeZone, Timelike};
|
||||||
|
|
||||||
/// A datetime type.
|
/// A datetime type.
|
||||||
///
|
///
|
||||||
|
@ -136,13 +136,73 @@ impl DatetimeMetric {
|
||||||
match StorageManager.snapshot_metric(
|
match StorageManager.snapshot_metric(
|
||||||
glean.storage(),
|
glean.storage(),
|
||||||
storage_name,
|
storage_name,
|
||||||
&self.meta().identifier(glean),
|
&self.meta.identifier(glean),
|
||||||
|
self.meta.lifetime,
|
||||||
) {
|
) {
|
||||||
Some(Metric::Datetime(dt, _)) => Some(dt),
|
Some(Metric::Datetime(dt, _)) => Some(dt),
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// **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),
|
||||||
|
self.meta.lifetime,
|
||||||
|
) {
|
||||||
|
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).**
|
/// **Test-only API (exported for FFI purposes).**
|
||||||
///
|
///
|
||||||
/// Gets the currently stored value as a String.
|
/// Gets the currently stored value as a String.
|
||||||
|
@ -155,6 +215,7 @@ impl DatetimeMetric {
|
||||||
glean.storage(),
|
glean.storage(),
|
||||||
storage_name,
|
storage_name,
|
||||||
&self.meta.identifier(glean),
|
&self.meta.identifier(glean),
|
||||||
|
self.meta.lifetime,
|
||||||
) {
|
) {
|
||||||
Some(Metric::Datetime(d, tu)) => Some(get_iso_time_string(d, tu)),
|
Some(Metric::Datetime(d, tu)) => Some(get_iso_time_string(d, tu)),
|
||||||
_ => None,
|
_ => None,
|
||||||
|
|
|
@ -225,6 +225,7 @@ impl ExperimentMetric {
|
||||||
glean.storage(),
|
glean.storage(),
|
||||||
INTERNAL_STORAGE,
|
INTERNAL_STORAGE,
|
||||||
&self.meta.identifier(glean),
|
&self.meta.identifier(glean),
|
||||||
|
self.meta.lifetime,
|
||||||
) {
|
) {
|
||||||
Some(Metric::Experiment(e)) => Some(json!(e).to_string()),
|
Some(Metric::Experiment(e)) => Some(json!(e).to_string()),
|
||||||
_ => None,
|
_ => None,
|
||||||
|
|
|
@ -294,6 +294,7 @@ impl JweMetric {
|
||||||
glean.storage(),
|
glean.storage(),
|
||||||
storage_name,
|
storage_name,
|
||||||
&self.meta.identifier(glean),
|
&self.meta.identifier(glean),
|
||||||
|
self.meta.lifetime,
|
||||||
) {
|
) {
|
||||||
Some(Metric::Jwe(b)) => Some(b),
|
Some(Metric::Jwe(b)) => Some(b),
|
||||||
_ => None,
|
_ => None,
|
||||||
|
|
|
@ -190,6 +190,7 @@ impl MemoryDistributionMetric {
|
||||||
glean.storage(),
|
glean.storage(),
|
||||||
storage_name,
|
storage_name,
|
||||||
&self.meta.identifier(glean),
|
&self.meta.identifier(glean),
|
||||||
|
self.meta.lifetime,
|
||||||
) {
|
) {
|
||||||
Some(Metric::MemoryDistribution(hist)) => Some(snapshot(&hist)),
|
Some(Metric::MemoryDistribution(hist)) => Some(snapshot(&hist)),
|
||||||
_ => None,
|
_ => None,
|
||||||
|
|
|
@ -78,6 +78,7 @@ impl QuantityMetric {
|
||||||
glean.storage(),
|
glean.storage(),
|
||||||
storage_name,
|
storage_name,
|
||||||
&self.meta.identifier(glean),
|
&self.meta.identifier(glean),
|
||||||
|
self.meta.lifetime,
|
||||||
) {
|
) {
|
||||||
Some(Metric::Quantity(i)) => Some(i),
|
Some(Metric::Quantity(i)) => Some(i),
|
||||||
_ => None,
|
_ => None,
|
||||||
|
|
|
@ -71,6 +71,7 @@ impl StringMetric {
|
||||||
glean.storage(),
|
glean.storage(),
|
||||||
storage_name,
|
storage_name,
|
||||||
&self.meta.identifier(glean),
|
&self.meta.identifier(glean),
|
||||||
|
self.meta.lifetime,
|
||||||
) {
|
) {
|
||||||
Some(Metric::String(s)) => Some(s),
|
Some(Metric::String(s)) => Some(s),
|
||||||
_ => None,
|
_ => None,
|
||||||
|
|
|
@ -137,6 +137,7 @@ impl StringListMetric {
|
||||||
glean.storage(),
|
glean.storage(),
|
||||||
storage_name,
|
storage_name,
|
||||||
&self.meta.identifier(glean),
|
&self.meta.identifier(glean),
|
||||||
|
self.meta.lifetime,
|
||||||
) {
|
) {
|
||||||
Some(Metric::StringList(values)) => Some(values),
|
Some(Metric::StringList(values)) => Some(values),
|
||||||
_ => None,
|
_ => None,
|
||||||
|
|
|
@ -108,7 +108,7 @@ impl TimespanMetric {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let duration = Duration::from_nanos(duration);
|
let duration = Duration::from_nanos(duration);
|
||||||
self.set_raw(glean, duration, false);
|
self.set_raw(glean, duration);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Aborts a previous [`set_start`](TimespanMetric::set_start) call. No
|
/// Aborts a previous [`set_start`](TimespanMetric::set_start) call. No
|
||||||
|
@ -132,8 +132,7 @@ impl TimespanMetric {
|
||||||
/// # Arguments
|
/// # Arguments
|
||||||
///
|
///
|
||||||
/// * `elapsed` - The elapsed time to record.
|
/// * `elapsed` - The elapsed time to record.
|
||||||
/// * `overwrite` - Whether or not to overwrite existing data.
|
pub fn set_raw(&self, glean: &Glean, elapsed: Duration) {
|
||||||
pub fn set_raw(&self, glean: &Glean, elapsed: Duration, overwrite: bool) {
|
|
||||||
if !self.should_record(glean) {
|
if !self.should_record(glean) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -151,19 +150,15 @@ impl TimespanMetric {
|
||||||
|
|
||||||
let mut report_value_exists: bool = false;
|
let mut report_value_exists: bool = false;
|
||||||
glean.storage().record_with(glean, &self.meta, |old_value| {
|
glean.storage().record_with(glean, &self.meta, |old_value| {
|
||||||
if overwrite {
|
match old_value {
|
||||||
Metric::Timespan(elapsed, self.time_unit)
|
Some(old @ Metric::Timespan(..)) => {
|
||||||
} else {
|
// If some value already exists, report an error.
|
||||||
match old_value {
|
// We do this out of the storage since recording an
|
||||||
Some(old @ Metric::Timespan(..)) => {
|
// error accesses the storage as well.
|
||||||
// If some value already exists, report an error.
|
report_value_exists = true;
|
||||||
// We do this out of the storage since recording an
|
old
|
||||||
// error accesses the storage as well.
|
|
||||||
report_value_exists = true;
|
|
||||||
old
|
|
||||||
}
|
|
||||||
_ => Metric::Timespan(elapsed, self.time_unit),
|
|
||||||
}
|
}
|
||||||
|
_ => Metric::Timespan(elapsed, self.time_unit),
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -188,6 +183,7 @@ impl TimespanMetric {
|
||||||
glean.storage(),
|
glean.storage(),
|
||||||
storage_name,
|
storage_name,
|
||||||
&self.meta.identifier(glean),
|
&self.meta.identifier(glean),
|
||||||
|
self.meta.lifetime,
|
||||||
) {
|
) {
|
||||||
Some(Metric::Timespan(time, time_unit)) => Some(time_unit.duration_convert(time)),
|
Some(Metric::Timespan(time, time_unit)) => Some(time_unit.duration_convert(time)),
|
||||||
_ => None,
|
_ => None,
|
||||||
|
|
|
@ -323,6 +323,7 @@ impl TimingDistributionMetric {
|
||||||
glean.storage(),
|
glean.storage(),
|
||||||
storage_name,
|
storage_name,
|
||||||
&self.meta.identifier(glean),
|
&self.meta.identifier(glean),
|
||||||
|
self.meta.lifetime,
|
||||||
) {
|
) {
|
||||||
Some(Metric::TimingDistribution(hist)) => Some(snapshot(&hist)),
|
Some(Metric::TimingDistribution(hist)) => Some(snapshot(&hist)),
|
||||||
_ => None,
|
_ => None,
|
||||||
|
|
|
@ -103,6 +103,7 @@ impl UuidMetric {
|
||||||
glean.storage(),
|
glean.storage(),
|
||||||
storage_name,
|
storage_name,
|
||||||
&self.meta().identifier(glean),
|
&self.meta().identifier(glean),
|
||||||
|
self.meta.lifetime,
|
||||||
) {
|
) {
|
||||||
Some(Metric::Uuid(uuid)) => Uuid::parse_str(&uuid).ok(),
|
Some(Metric::Uuid(uuid)) => Uuid::parse_str(&uuid).ok(),
|
||||||
_ => None,
|
_ => None,
|
||||||
|
|
|
@ -65,6 +65,7 @@ impl PingMaker {
|
||||||
glean.storage(),
|
glean.storage(),
|
||||||
INTERNAL_STORAGE,
|
INTERNAL_STORAGE,
|
||||||
&seq.meta().identifier(glean),
|
&seq.meta().identifier(glean),
|
||||||
|
seq.meta().lifetime,
|
||||||
) {
|
) {
|
||||||
Some(Metric::Counter(i)) => i,
|
Some(Metric::Counter(i)) => i,
|
||||||
_ => 0,
|
_ => 0,
|
||||||
|
|
|
@ -129,6 +129,7 @@ impl StorageManager {
|
||||||
storage: &Database,
|
storage: &Database,
|
||||||
store_name: &str,
|
store_name: &str,
|
||||||
metric_id: &str,
|
metric_id: &str,
|
||||||
|
metric_lifetime: Lifetime,
|
||||||
) -> Option<Metric> {
|
) -> Option<Metric> {
|
||||||
let mut snapshot: Option<Metric> = None;
|
let mut snapshot: Option<Metric> = None;
|
||||||
|
|
||||||
|
@ -139,9 +140,7 @@ impl StorageManager {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
storage.iter_store_from(Lifetime::Ping, &store_name, None, &mut snapshotter);
|
storage.iter_store_from(metric_lifetime, &store_name, None, &mut snapshotter);
|
||||||
storage.iter_store_from(Lifetime::Application, &store_name, None, &mut snapshotter);
|
|
||||||
storage.iter_store_from(Lifetime::User, &store_name, None, &mut snapshotter);
|
|
||||||
|
|
||||||
snapshot
|
snapshot
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,8 @@
|
||||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
// 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/.
|
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
|
use crate::ErrorType;
|
||||||
|
|
||||||
/// A description for the
|
/// A description for the
|
||||||
/// [`CustomDistributionMetric`](crate::metrics::CustomDistributionMetric) type.
|
/// [`CustomDistributionMetric`](crate::metrics::CustomDistributionMetric) type.
|
||||||
///
|
///
|
||||||
|
@ -43,16 +45,20 @@ pub trait CustomDistribution {
|
||||||
|
|
||||||
/// **Exported for test purposes.**
|
/// **Exported for test purposes.**
|
||||||
///
|
///
|
||||||
/// Gets the currently stored histogram as a JSON String of the serialized value.
|
/// Gets the number of recorded errors for the given error type.
|
||||||
///
|
|
||||||
/// This doesn't clear the stored value.
|
|
||||||
///
|
///
|
||||||
/// # Arguments
|
/// # Arguments
|
||||||
///
|
///
|
||||||
|
/// * `error` - The type of error
|
||||||
/// * `ping_name` - represents the optional name of the ping to retrieve the
|
/// * `ping_name` - represents the optional name of the ping to retrieve the
|
||||||
/// metric for. Defaults to the first value in `send_in_pings`.
|
/// metric for. Defaults to the first value in `send_in_pings`.
|
||||||
fn test_get_value_as_json_string<'a, S: Into<Option<&'a str>>>(
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// The number of errors recorded.
|
||||||
|
fn test_get_num_recorded_errors<'a, S: Into<Option<&'a str>>>(
|
||||||
&self,
|
&self,
|
||||||
|
error: ErrorType,
|
||||||
ping_name: S,
|
ping_name: S,
|
||||||
) -> Option<String>;
|
) -> i32;
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,36 +4,13 @@
|
||||||
|
|
||||||
#![allow(clippy::too_many_arguments)]
|
#![allow(clippy::too_many_arguments)]
|
||||||
|
|
||||||
|
use crate::ErrorType;
|
||||||
|
|
||||||
/// A description for the [`DatetimeMetric`](crate::metrics::DatetimeMetric) type.
|
/// A description for the [`DatetimeMetric`](crate::metrics::DatetimeMetric) type.
|
||||||
///
|
///
|
||||||
/// When changing this trait, make sure all the operations are
|
/// When changing this trait, make sure all the operations are
|
||||||
/// implemented in the related type in `../metrics/`.
|
/// implemented in the related type in `../metrics/`.
|
||||||
pub trait Datetime {
|
pub trait Datetime {
|
||||||
/// Sets the metric to a date/time including the timezone offset.
|
|
||||||
///
|
|
||||||
/// # Arguments
|
|
||||||
///
|
|
||||||
/// * `year` - the year to set the metric to.
|
|
||||||
/// * `month` - the month to set the metric to (1-12).
|
|
||||||
/// * `day` - the day to set the metric to (1-based).
|
|
||||||
/// * `hour` - the hour to set the metric to.
|
|
||||||
/// * `minute` - the minute to set the metric to.
|
|
||||||
/// * `second` - the second to set the metric to.
|
|
||||||
/// * `nano` - the nanosecond fraction to the last whole second.
|
|
||||||
/// * `offset_seconds` - the timezone difference, in seconds, for the Eastern
|
|
||||||
/// Hemisphere. Negative seconds mean Western Hemisphere.
|
|
||||||
fn set_with_details(
|
|
||||||
&self,
|
|
||||||
year: i32,
|
|
||||||
month: u32,
|
|
||||||
day: u32,
|
|
||||||
hour: u32,
|
|
||||||
minute: u32,
|
|
||||||
second: u32,
|
|
||||||
nano: u32,
|
|
||||||
offset_seconds: i32,
|
|
||||||
);
|
|
||||||
|
|
||||||
/// Sets the metric to a date/time which including the timezone offset.
|
/// Sets the metric to a date/time which including the timezone offset.
|
||||||
///
|
///
|
||||||
/// # Arguments
|
/// # Arguments
|
||||||
|
@ -62,18 +39,20 @@ pub trait Datetime {
|
||||||
|
|
||||||
/// **Exported for test purposes.**
|
/// **Exported for test purposes.**
|
||||||
///
|
///
|
||||||
/// Gets the currently stored value as a String.
|
/// Gets the number of recorded errors for the given metric and error type.
|
||||||
///
|
|
||||||
/// The precision of this value is truncated to the `time_unit` precision.
|
|
||||||
///
|
|
||||||
/// This doesn't clear the stored value.
|
|
||||||
///
|
///
|
||||||
/// # Arguments
|
/// # Arguments
|
||||||
///
|
///
|
||||||
|
/// * `error` - The type of error
|
||||||
/// * `ping_name` - represents the optional name of the ping to retrieve the
|
/// * `ping_name` - represents the optional name of the ping to retrieve the
|
||||||
/// metric for. Defaults to the first value in `send_in_pings`.
|
/// metric for. Defaults to the first value in `send_in_pings`.
|
||||||
fn test_get_value_as_string<'a, S: Into<Option<&'a str>>>(
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// The number of errors reported.
|
||||||
|
fn test_get_num_recorded_errors<'a, S: Into<Option<&'a str>>>(
|
||||||
&self,
|
&self,
|
||||||
|
error: ErrorType,
|
||||||
ping_name: S,
|
ping_name: S,
|
||||||
) -> Option<String>;
|
) -> i32;
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,8 @@
|
||||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
// 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/.
|
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
|
use crate::ErrorType;
|
||||||
|
|
||||||
/// A description for the [`StringListMetric`](crate::metrics::StringListMetric) type.
|
/// A description for the [`StringListMetric`](crate::metrics::StringListMetric) type.
|
||||||
///
|
///
|
||||||
/// When changing this trait, make sure all the operations are
|
/// When changing this trait, make sure all the operations are
|
||||||
|
@ -45,17 +47,20 @@ pub trait StringList {
|
||||||
|
|
||||||
/// **Exported for test purposes.**
|
/// **Exported for test purposes.**
|
||||||
///
|
///
|
||||||
/// Gets the currently-stored values as a JSON String of the format
|
/// Gets the number of recorded errors for the given error type.
|
||||||
/// ["string1", "string2", ...]
|
|
||||||
///
|
|
||||||
/// This doesn't clear the stored value.
|
|
||||||
///
|
///
|
||||||
/// # Arguments
|
/// # Arguments
|
||||||
///
|
///
|
||||||
|
/// * `error` - The type of error
|
||||||
/// * `ping_name` - represents the optional name of the ping to retrieve the
|
/// * `ping_name` - represents the optional name of the ping to retrieve the
|
||||||
/// metric for. Defaults to the first value in `send_in_pings`.
|
/// metric for. Defaults to the first value in `send_in_pings`.
|
||||||
fn test_get_value_as_json_string<'a, S: Into<Option<&'a str>>>(
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// The number of errors recorded.
|
||||||
|
fn test_get_num_recorded_errors<'a, S: Into<Option<&'a str>>>(
|
||||||
&self,
|
&self,
|
||||||
|
error: ErrorType,
|
||||||
ping_name: S,
|
ping_name: S,
|
||||||
) -> Option<String>;
|
) -> i32;
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
|
|
||||||
use crate::metrics::DistributionData;
|
use crate::metrics::DistributionData;
|
||||||
use crate::metrics::TimerId;
|
use crate::metrics::TimerId;
|
||||||
|
use crate::ErrorType;
|
||||||
|
|
||||||
/// A description for the [`TimingDistributionMetric`](crate::metrics::TimingDistributionMetric) type.
|
/// A description for the [`TimingDistributionMetric`](crate::metrics::TimingDistributionMetric) type.
|
||||||
///
|
///
|
||||||
|
@ -13,23 +14,19 @@ pub trait TimingDistribution {
|
||||||
/// Starts tracking time for the provided metric.
|
/// Starts tracking time for the provided metric.
|
||||||
///
|
///
|
||||||
/// This records an error if it’s already tracking time (i.e.
|
/// This records an error if it’s already tracking time (i.e.
|
||||||
/// [`set_start`](TimingDistribution::set_start) was already called with no corresponding
|
/// [`start`](TimingDistribution::start) was already called with no corresponding
|
||||||
/// [`set_stop_and_accumulate`](TimingDistribution::set_stop_and_accumulate)): in that case the
|
/// [`stop_and_accumulate`](TimingDistribution::stop_and_accumulate)): in that case the
|
||||||
/// original start time will be preserved.
|
/// original start time will be preserved.
|
||||||
///
|
///
|
||||||
/// # Arguments
|
|
||||||
///
|
|
||||||
/// * `start_time` - Timestamp in nanoseconds.
|
|
||||||
///
|
|
||||||
/// # Returns
|
/// # Returns
|
||||||
///
|
///
|
||||||
/// A unique [`TimerId`] for the new timer.
|
/// A unique [`TimerId`] for the new timer.
|
||||||
fn set_start(&mut self, start_time: u64);
|
fn start(&mut self) -> TimerId;
|
||||||
|
|
||||||
/// Stops tracking time for the provided metric and associated timer id.
|
/// Stops tracking time for the provided metric and associated timer id.
|
||||||
///
|
///
|
||||||
/// Adds a count to the corresponding bucket in the timing distribution.
|
/// Adds a count to the corresponding bucket in the timing distribution.
|
||||||
/// This will record an error if no [`set_start`](TimingDistribution::set_start) was
|
/// This will record an error if no [`start`](TimingDistribution::start) was
|
||||||
/// called.
|
/// called.
|
||||||
///
|
///
|
||||||
/// # Arguments
|
/// # Arguments
|
||||||
|
@ -37,11 +34,10 @@ pub trait TimingDistribution {
|
||||||
/// * `id` - The [`TimerId`] to associate with this timing. This allows
|
/// * `id` - The [`TimerId`] to associate with this timing. This allows
|
||||||
/// for concurrent timing of events associated with different ids to the
|
/// for concurrent timing of events associated with different ids to the
|
||||||
/// same timespan metric.
|
/// same timespan metric.
|
||||||
/// * `stop_time` - Timestamp in nanoseconds.
|
fn stop_and_accumulate(&mut self, id: TimerId);
|
||||||
fn set_stop_and_accumulate(&mut self, id: TimerId, stop_time: u64);
|
|
||||||
|
|
||||||
/// Aborts a previous [`set_start`](TimingDistribution::set_start) call. No
|
/// Aborts a previous [`start`](TimingDistribution::start) call. No
|
||||||
/// error is recorded if no [`set_start`](TimingDistribution::set_start) was
|
/// error is recorded if no [`start`](TimingDistribution::start) was
|
||||||
/// called.
|
/// called.
|
||||||
///
|
///
|
||||||
/// # Arguments
|
/// # Arguments
|
||||||
|
@ -51,34 +47,6 @@ pub trait TimingDistribution {
|
||||||
/// same timing distribution metric.
|
/// same timing distribution metric.
|
||||||
fn cancel(&mut self, id: TimerId);
|
fn cancel(&mut self, id: TimerId);
|
||||||
|
|
||||||
/// Accumulates the provided signed samples in the metric.
|
|
||||||
///
|
|
||||||
/// This is required so that the platform-specific code can provide us with
|
|
||||||
/// 64 bit signed integers if no `u64` comparable type is available. This
|
|
||||||
/// will take care of filtering and reporting errors for any provided negative
|
|
||||||
/// sample.
|
|
||||||
///
|
|
||||||
/// Please note that this assumes that the provided samples are already in
|
|
||||||
/// the "unit" declared by the instance of the implementing metric type
|
|
||||||
/// (e.g. if the implementing class is a [TimingDistribution] and the
|
|
||||||
/// instance this method was called on is using second, then `samples` are
|
|
||||||
/// assumed to be in that unit).
|
|
||||||
///
|
|
||||||
/// # Arguments
|
|
||||||
///
|
|
||||||
/// * `samples` - The vector holding the samples to be recorded by the metric.
|
|
||||||
///
|
|
||||||
/// ## Notes
|
|
||||||
///
|
|
||||||
/// Discards any negative value in `samples` and report an
|
|
||||||
/// [`ErrorType::InvalidValue`](crate::ErrorType::InvalidValue) for each of
|
|
||||||
/// them.
|
|
||||||
///
|
|
||||||
/// Reports an
|
|
||||||
/// [`ErrorType::InvalidOverflow`](crate::ErrorType::InvalidOverflow) error
|
|
||||||
/// for samples that are longer than `MAX_SAMPLE_TIME`.
|
|
||||||
fn accumulate_samples_signed(&mut self, samples: Vec<i64>);
|
|
||||||
|
|
||||||
/// **Exported for test purposes.**
|
/// **Exported for test purposes.**
|
||||||
///
|
///
|
||||||
/// Gets the currently stored value as an integer.
|
/// Gets the currently stored value as an integer.
|
||||||
|
@ -96,16 +64,20 @@ pub trait TimingDistribution {
|
||||||
|
|
||||||
/// **Exported for test purposes.**
|
/// **Exported for test purposes.**
|
||||||
///
|
///
|
||||||
/// Gets the currently-stored histogram as a JSON String of the serialized value.
|
/// Gets the number of recorded errors for the given error type.
|
||||||
///
|
|
||||||
/// This doesn't clear the stored value.
|
|
||||||
///
|
///
|
||||||
/// # Arguments
|
/// # Arguments
|
||||||
///
|
///
|
||||||
|
/// * `error` - The type of error
|
||||||
/// * `ping_name` - represents the optional name of the ping to retrieve the
|
/// * `ping_name` - represents the optional name of the ping to retrieve the
|
||||||
/// metric for. Defaults to the first value in `send_in_pings`.
|
/// metric for. Defaults to the first value in `send_in_pings`.
|
||||||
fn test_get_value_as_json_string<'a, S: Into<Option<&'a str>>>(
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// The number of errors recorded.
|
||||||
|
fn test_get_num_recorded_errors<'a, S: Into<Option<&'a str>>>(
|
||||||
&self,
|
&self,
|
||||||
|
error: ErrorType,
|
||||||
ping_name: S,
|
ping_name: S,
|
||||||
) -> Option<String>;
|
) -> i32;
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,6 +32,8 @@ mod policy;
|
||||||
mod request;
|
mod request;
|
||||||
mod result;
|
mod result;
|
||||||
|
|
||||||
|
const WAIT_TIME_FOR_PING_PROCESSING: u64 = 1000; // in milliseconds
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
struct RateLimiter {
|
struct RateLimiter {
|
||||||
/// The instant the current interval has started.
|
/// The instant the current interval has started.
|
||||||
|
@ -50,7 +52,10 @@ enum RateLimiterState {
|
||||||
/// The RateLimiter has not reached the maximum count and is still incrementing.
|
/// The RateLimiter has not reached the maximum count and is still incrementing.
|
||||||
Incrementing,
|
Incrementing,
|
||||||
/// The RateLimiter has reached the maximum count for the current interval.
|
/// The RateLimiter has reached the maximum count for the current interval.
|
||||||
Throttled,
|
///
|
||||||
|
/// This variant contains the remaining time (in milliseconds)
|
||||||
|
/// until the rate limiter is not throttled anymore.
|
||||||
|
Throttled(u64),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RateLimiter {
|
impl RateLimiter {
|
||||||
|
@ -68,6 +73,10 @@ impl RateLimiter {
|
||||||
self.count = 0;
|
self.count = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn elapsed(&self) -> Duration {
|
||||||
|
self.started.unwrap().elapsed()
|
||||||
|
}
|
||||||
|
|
||||||
// The counter should reset if
|
// The counter should reset if
|
||||||
//
|
//
|
||||||
// 1. It has never started;
|
// 1. It has never started;
|
||||||
|
@ -79,8 +88,7 @@ impl RateLimiter {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Safe unwrap, we already stated that `self.started` is not `None` above.
|
// Safe unwrap, we already stated that `self.started` is not `None` above.
|
||||||
let elapsed = self.started.unwrap().elapsed();
|
if self.elapsed() > self.interval {
|
||||||
if elapsed > self.interval {
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -98,7 +106,14 @@ impl RateLimiter {
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.count == self.max_count {
|
if self.count == self.max_count {
|
||||||
return RateLimiterState::Throttled;
|
// Note that `remining` can't be a negative number because we just called `reset`,
|
||||||
|
// which will check if it is and reset if so.
|
||||||
|
let remaining = self.interval.as_millis() - self.elapsed().as_millis();
|
||||||
|
return RateLimiterState::Throttled(
|
||||||
|
remaining
|
||||||
|
.try_into()
|
||||||
|
.unwrap_or(self.interval.as_secs() * 1000),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
self.count += 1;
|
self.count += 1;
|
||||||
|
@ -119,7 +134,10 @@ pub enum PingUploadTask {
|
||||||
Upload(PingRequest),
|
Upload(PingRequest),
|
||||||
/// A flag signaling that the pending pings directories are not done being processed,
|
/// A flag signaling that the pending pings directories are not done being processed,
|
||||||
/// thus the requester should wait and come back later.
|
/// thus the requester should wait and come back later.
|
||||||
Wait,
|
///
|
||||||
|
/// Contains the amount of time in milliseconds
|
||||||
|
/// the requester should wait before requesting a new task.
|
||||||
|
Wait(u64),
|
||||||
/// A flag signaling that requester doesn't need to request any more upload tasks at this moment.
|
/// A flag signaling that requester doesn't need to request any more upload tasks at this moment.
|
||||||
///
|
///
|
||||||
/// There are three possibilities for this scenario:
|
/// There are three possibilities for this scenario:
|
||||||
|
@ -135,6 +153,18 @@ pub enum PingUploadTask {
|
||||||
Done,
|
Done,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl PingUploadTask {
|
||||||
|
/// Whether the current task is an upload task.
|
||||||
|
pub fn is_upload(&self) -> bool {
|
||||||
|
matches!(self, PingUploadTask::Upload(_))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Whether the current task is wait task.
|
||||||
|
pub fn is_wait(&self) -> bool {
|
||||||
|
matches!(self, PingUploadTask::Wait(_))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Manages the pending pings queue and directory.
|
/// Manages the pending pings queue and directory.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct PingUploadManager {
|
pub struct PingUploadManager {
|
||||||
|
@ -474,12 +504,12 @@ impl PingUploadManager {
|
||||||
//
|
//
|
||||||
// We want to limit the amount of PingUploadTask::Wait returned in a row,
|
// We want to limit the amount of PingUploadTask::Wait returned in a row,
|
||||||
// in case we reach MAX_WAIT_ATTEMPTS we want to actually return PingUploadTask::Done.
|
// in case we reach MAX_WAIT_ATTEMPTS we want to actually return PingUploadTask::Done.
|
||||||
let wait_or_done = || {
|
let wait_or_done = |time: u64| {
|
||||||
self.wait_attempt_count.fetch_add(1, Ordering::SeqCst);
|
self.wait_attempt_count.fetch_add(1, Ordering::SeqCst);
|
||||||
if self.wait_attempt_count() > self.policy.max_wait_attempts() {
|
if self.wait_attempt_count() > self.policy.max_wait_attempts() {
|
||||||
PingUploadTask::Done
|
PingUploadTask::Done
|
||||||
} else {
|
} else {
|
||||||
PingUploadTask::Wait
|
PingUploadTask::Wait(time)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -487,7 +517,7 @@ impl PingUploadManager {
|
||||||
log::info!(
|
log::info!(
|
||||||
"Tried getting an upload task, but processing is ongoing. Will come back later."
|
"Tried getting an upload task, but processing is ongoing. Will come back later."
|
||||||
);
|
);
|
||||||
return wait_or_done();
|
return wait_or_done(WAIT_TIME_FOR_PING_PROCESSING);
|
||||||
}
|
}
|
||||||
|
|
||||||
// This is a no-op in case there are no cached pings.
|
// This is a no-op in case there are no cached pings.
|
||||||
|
@ -510,11 +540,11 @@ impl PingUploadManager {
|
||||||
let mut rate_limiter = rate_limiter
|
let mut rate_limiter = rate_limiter
|
||||||
.write()
|
.write()
|
||||||
.expect("Can't write to the rate limiter.");
|
.expect("Can't write to the rate limiter.");
|
||||||
if rate_limiter.get_state() == RateLimiterState::Throttled {
|
if let RateLimiterState::Throttled(remaining) = rate_limiter.get_state() {
|
||||||
log::info!(
|
log::info!(
|
||||||
"Tried getting an upload task, but we are throttled at the moment."
|
"Tried getting an upload task, but we are throttled at the moment."
|
||||||
);
|
);
|
||||||
return wait_or_done();
|
return wait_or_done(remaining);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -554,13 +584,11 @@ impl PingUploadManager {
|
||||||
pub fn get_upload_task(&self, glean: &Glean, log_ping: bool) -> PingUploadTask {
|
pub fn get_upload_task(&self, glean: &Glean, log_ping: bool) -> PingUploadTask {
|
||||||
let task = self.get_upload_task_internal(glean, log_ping);
|
let task = self.get_upload_task_internal(glean, log_ping);
|
||||||
|
|
||||||
if task != PingUploadTask::Wait && self.wait_attempt_count() > 0 {
|
if !task.is_wait() && self.wait_attempt_count() > 0 {
|
||||||
self.wait_attempt_count.store(0, Ordering::SeqCst);
|
self.wait_attempt_count.store(0, Ordering::SeqCst);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (task == PingUploadTask::Wait || task == PingUploadTask::Done)
|
if !task.is_upload() && self.recoverable_failure_count() > 0 {
|
||||||
&& self.recoverable_failure_count() > 0
|
|
||||||
{
|
|
||||||
self.recoverable_failure_count.store(0, Ordering::SeqCst);
|
self.recoverable_failure_count.store(0, Ordering::SeqCst);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -747,10 +775,8 @@ mod test {
|
||||||
|
|
||||||
// Try and get the next request.
|
// Try and get the next request.
|
||||||
// Verify request was returned
|
// Verify request was returned
|
||||||
match upload_manager.get_upload_task(&glean, false) {
|
let task = upload_manager.get_upload_task(&glean, false);
|
||||||
PingUploadTask::Upload(_) => {}
|
assert!(task.is_upload());
|
||||||
_ => panic!("Expected upload manager to return the next request!"),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -767,10 +793,8 @@ mod test {
|
||||||
|
|
||||||
// Verify a request is returned for each submitted ping
|
// Verify a request is returned for each submitted ping
|
||||||
for _ in 0..n {
|
for _ in 0..n {
|
||||||
match upload_manager.get_upload_task(&glean, false) {
|
let task = upload_manager.get_upload_task(&glean, false);
|
||||||
PingUploadTask::Upload(_) => {}
|
assert!(task.is_upload());
|
||||||
_ => panic!("Expected upload manager to return the next request!"),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verify that after all requests are returned, none are left
|
// Verify that after all requests are returned, none are left
|
||||||
|
@ -787,9 +811,8 @@ mod test {
|
||||||
let mut upload_manager = PingUploadManager::no_policy(dir.path());
|
let mut upload_manager = PingUploadManager::no_policy(dir.path());
|
||||||
|
|
||||||
// Add a rate limiter to the upload mangager with max of 10 pings every 3 seconds.
|
// Add a rate limiter to the upload mangager with max of 10 pings every 3 seconds.
|
||||||
let secs_per_interval = 3;
|
|
||||||
let max_pings_per_interval = 10;
|
let max_pings_per_interval = 10;
|
||||||
upload_manager.set_rate_limiter(secs_per_interval, 10);
|
upload_manager.set_rate_limiter(3, 10);
|
||||||
|
|
||||||
// Enqueue the max number of pings allowed per uploading window
|
// Enqueue the max number of pings allowed per uploading window
|
||||||
for _ in 0..max_pings_per_interval {
|
for _ in 0..max_pings_per_interval {
|
||||||
|
@ -798,28 +821,24 @@ mod test {
|
||||||
|
|
||||||
// Verify a request is returned for each submitted ping
|
// Verify a request is returned for each submitted ping
|
||||||
for _ in 0..max_pings_per_interval {
|
for _ in 0..max_pings_per_interval {
|
||||||
match upload_manager.get_upload_task(&glean, false) {
|
let task = upload_manager.get_upload_task(&glean, false);
|
||||||
PingUploadTask::Upload(_) => {}
|
assert!(task.is_upload());
|
||||||
_ => panic!("Expected upload manager to return the next request!"),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Enqueue just one more ping
|
// Enqueue just one more ping
|
||||||
upload_manager.enqueue_ping(&glean, &Uuid::new_v4().to_string(), PATH, "", None);
|
upload_manager.enqueue_ping(&glean, &Uuid::new_v4().to_string(), PATH, "", None);
|
||||||
|
|
||||||
// Verify that we are indeed told to wait because we are at capacity
|
// Verify that we are indeed told to wait because we are at capacity
|
||||||
assert_eq!(
|
|
||||||
PingUploadTask::Wait,
|
|
||||||
upload_manager.get_upload_task(&glean, false)
|
|
||||||
);
|
|
||||||
|
|
||||||
// Wait for the uploading window to reset
|
|
||||||
thread::sleep(Duration::from_secs(secs_per_interval));
|
|
||||||
|
|
||||||
match upload_manager.get_upload_task(&glean, false) {
|
match upload_manager.get_upload_task(&glean, false) {
|
||||||
PingUploadTask::Upload(_) => {}
|
PingUploadTask::Wait(time) => {
|
||||||
_ => panic!("Expected upload manager to return the next request!"),
|
// Wait for the uploading window to reset
|
||||||
}
|
thread::sleep(Duration::from_millis(time));
|
||||||
|
}
|
||||||
|
_ => panic!("Expected upload manager to return a wait task!"),
|
||||||
|
};
|
||||||
|
|
||||||
|
let task = upload_manager.get_upload_task(&glean, false);
|
||||||
|
assert!(task.is_upload());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -895,10 +914,8 @@ mod test {
|
||||||
|
|
||||||
// Verify the requests were properly enqueued
|
// Verify the requests were properly enqueued
|
||||||
for _ in 0..n {
|
for _ in 0..n {
|
||||||
match upload_manager.get_upload_task(&glean, false) {
|
let task = upload_manager.get_upload_task(&glean, false);
|
||||||
PingUploadTask::Upload(_) => {}
|
assert!(task.is_upload());
|
||||||
_ => panic!("Expected upload manager to return the next request!"),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verify that after all requests are returned, none are left
|
// Verify that after all requests are returned, none are left
|
||||||
|
@ -1113,10 +1130,8 @@ mod test {
|
||||||
upload_manager.enqueue_ping(&glean, &doc_id, &path, "", None);
|
upload_manager.enqueue_ping(&glean, &doc_id, &path, "", None);
|
||||||
|
|
||||||
// Get a task once
|
// Get a task once
|
||||||
match upload_manager.get_upload_task(&glean, false) {
|
let task = upload_manager.get_upload_task(&glean, false);
|
||||||
PingUploadTask::Upload(_) => {}
|
assert!(task.is_upload());
|
||||||
_ => panic!("Expected upload manager to return the next request!"),
|
|
||||||
}
|
|
||||||
|
|
||||||
// There should be no more queued tasks
|
// There should be no more queued tasks
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
|
@ -1168,10 +1183,8 @@ mod test {
|
||||||
|
|
||||||
// Verify all requests are returned when we try again.
|
// Verify all requests are returned when we try again.
|
||||||
for _ in 0..n {
|
for _ in 0..n {
|
||||||
match upload_manager.get_upload_task(&glean, false) {
|
let task = upload_manager.get_upload_task(&glean, false);
|
||||||
PingUploadTask::Upload(_) => {}
|
assert!(task.is_upload());
|
||||||
_ => panic!("Expected upload manager to return the next request!"),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1498,10 +1511,8 @@ mod test {
|
||||||
// we should be throttled and thus get a PingUploadTask::Wait.
|
// we should be throttled and thus get a PingUploadTask::Wait.
|
||||||
// Check that we are indeed allowed to get this response as many times as expected.
|
// Check that we are indeed allowed to get this response as many times as expected.
|
||||||
for _ in 0..max_wait_attempts {
|
for _ in 0..max_wait_attempts {
|
||||||
assert_eq!(
|
let task = upload_manager.get_upload_task(&glean, false);
|
||||||
upload_manager.get_upload_task(&glean, false),
|
assert!(task.is_wait());
|
||||||
PingUploadTask::Wait
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check that after we get PingUploadTask::Wait the allowed number of times,
|
// Check that after we get PingUploadTask::Wait the allowed number of times,
|
||||||
|
@ -1515,10 +1526,8 @@ mod test {
|
||||||
thread::sleep(Duration::from_secs(secs_per_interval));
|
thread::sleep(Duration::from_secs(secs_per_interval));
|
||||||
|
|
||||||
// Check that we are allowed again to get pings.
|
// Check that we are allowed again to get pings.
|
||||||
match upload_manager.get_upload_task(&glean, false) {
|
let task = upload_manager.get_upload_task(&glean, false);
|
||||||
PingUploadTask::Upload(_) => {}
|
assert!(task.is_upload());
|
||||||
_ => panic!("Expected upload manager to return the next request!"),
|
|
||||||
}
|
|
||||||
|
|
||||||
// And once we are done we don't need to wait anymore.
|
// And once we are done we don't need to wait anymore.
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
|
@ -1526,4 +1535,16 @@ mod test {
|
||||||
PingUploadTask::Done
|
PingUploadTask::Done
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn wait_task_contains_expected_wait_time_when_pending_pings_dir_not_processed_yet() {
|
||||||
|
let (glean, dir) = new_glean(None);
|
||||||
|
let upload_manager = PingUploadManager::new(dir.path(), "test");
|
||||||
|
match upload_manager.get_upload_task(&glean, false) {
|
||||||
|
PingUploadTask::Wait(time) => {
|
||||||
|
assert_eq!(time, WAIT_TIME_FOR_PING_PROCESSING);
|
||||||
|
}
|
||||||
|
_ => panic!("Expected upload manager to return a wait task!"),
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -247,7 +247,7 @@ fn set_raw_time() {
|
||||||
);
|
);
|
||||||
|
|
||||||
let time = Duration::from_secs(1);
|
let time = Duration::from_secs(1);
|
||||||
metric.set_raw(&glean, time, false);
|
metric.set_raw(&glean, time);
|
||||||
|
|
||||||
let time_in_ns = time.as_nanos() as u64;
|
let time_in_ns = time.as_nanos() as u64;
|
||||||
assert_eq!(Some(time_in_ns), metric.test_get_value(&glean, "store1"));
|
assert_eq!(Some(time_in_ns), metric.test_get_value(&glean, "store1"));
|
||||||
|
@ -272,7 +272,7 @@ fn set_raw_time_does_nothing_when_timer_running() {
|
||||||
let time = Duration::from_secs(42);
|
let time = Duration::from_secs(42);
|
||||||
|
|
||||||
metric.set_start(&glean, 0);
|
metric.set_start(&glean, 0);
|
||||||
metric.set_raw(&glean, time, false);
|
metric.set_raw(&glean, time);
|
||||||
metric.set_stop(&glean, 60);
|
metric.set_stop(&glean, 60);
|
||||||
|
|
||||||
// We expect the start/stop value, not the raw value.
|
// We expect the start/stop value, not the raw value.
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
{"files":{"Cargo.toml":"63c841dda738f53820850e1ab57ee31af77cd8fdec3a92cf2af40ac1ea297a96","LICENSE":"1f256ecad192880510e84ad60474eab7589218784b9a50bc7ceee34c2b91f1d5","README.md":"fd9e0ca6907917ea6bec5de05e15dd21d20fae1cb7f3250467bb20231a8e1065","src/common_test.rs":"cf1ed5c1429e7f2ab5c52a47eb67c14bef455f1853fbdc9c2fd247d9fc7685b6","src/configuration.rs":"b8747397761a9cf6dc64150855b75fd8e48dfe9951ce69e25d646b3a6f46456f","src/core_metrics.rs":"e20697e04f707c34c3c7a0cc4e2ed93e638d3028f03eb75a93f53ae722243986","src/dispatcher/global.rs":"e4a86cf1f8df1728b414dbb3a8cc1a834ebaa0de6c2ffd1d7e9d55a714957193","src/dispatcher/mod.rs":"9bfa5abe6d5fdec06171068abefa419f1d83737f150b7dc191c58bd76fd69bc9","src/glean_metrics.rs":"a5e1ea9c4dccb81aec4aa584bd76cf47e916c66af4aff4a0ef5aa297ee2d9aa3","src/lib.rs":"f006de40c37f65ff5dbad752ae81a6c09fcecf3b8ef546483ed3d2730e24b76f","src/net/http_uploader.rs":"9e8c1837ca0d3f6ea165ec936ab054173c4fe95a958710176c33b4d4d1d98beb","src/net/mod.rs":"4058e8751c44b4785c9d4913789a7e940c0888598228f9378c45132293821fe2","src/pings.rs":"2dfccd84848e1933aa4f6a7a707c58ec794c8f73ef2d93ea4d4df71d4e6abc31","src/private/boolean.rs":"2ead8da55eca0c8738f3c07445b46b1efa706b3e8a1e60428347e9fcb1d1fd3f","src/private/counter.rs":"9b39665fa98e987b091a2a80f23be285bcf0c456ef99c2fcb0d5b42a6c566340","src/private/event.rs":"3ea9d35cab6f42abed853c9f933305d9c04d748e6d2a73a8e7a6a4b25e31f16a","src/private/labeled.rs":"4a9406ca11bc8d14dbc1a822781a3a8156cf99cfa2f548cb1d758d8c94570142","src/private/memory_distribution.rs":"e917a9dc2a26d7039cfcb76b290fb1333e7b38a8131989ce103ea2402c305079","src/private/mod.rs":"2ef82bea88db1565fc915ef3ecb8f3d2507a82e290312d164890f866c48e560e","src/private/ping.rs":"ec956102617989e72c79d61e959bfdd14ab1bb96cc12bd2745e75d5496eba2be","src/private/quantity.rs":"1e3e5677020c73a3f5ff01c1970ff8da8e30c800c5129e568b26260696477a06","src/private/recorded_experiment_data.rs":"66b2601902a2dc2b7a283717c21ce754de94fcca30d12e0398195c8ad49c90af","src/private/string.rs":"1004d0033cbf9191f6c2c8e9e738bf276e0580126c7297ec6ac5c6e6a3f84f58","src/private/timespan.rs":"ddc8d58c006caa9743d0b8386491e5c0715fa93d9ddfa57e3937448b1293836a","src/private/uuid.rs":"c045a6e57e790eea22e77554c7c5afab69e2187c487e503e6b931cff11df5493","src/system.rs":"ba7b3eac040abe4691d9d287562ddca6d7e92a6d6109c3f0c443b707a100d75a","src/test.rs":"0ff633ae8700cc7ee6e293587481bc378e120600e301aef44644e95a59d93141","tests/schema.rs":"b5acf42de034626f2b7e61fec9709a00370ce80ae3b2bab4db9fc79a20ea5f31"},"package":"b91b0b03e572ce70d06cf92ca899bdab8679e4ca07a2877928df028c848a1e9a"}
|
{"files":{"Cargo.toml":"55bb33b222226dc75b46dde412db46ac1b62aa87054406241828cf634017d8a2","LICENSE":"1f256ecad192880510e84ad60474eab7589218784b9a50bc7ceee34c2b91f1d5","README.md":"fd9e0ca6907917ea6bec5de05e15dd21d20fae1cb7f3250467bb20231a8e1065","src/common_test.rs":"a7c7bfb1215b784ed41a4aebac476b7aeb23631ea5646e642b5673e9067ecd95","src/configuration.rs":"b8747397761a9cf6dc64150855b75fd8e48dfe9951ce69e25d646b3a6f46456f","src/core_metrics.rs":"e20697e04f707c34c3c7a0cc4e2ed93e638d3028f03eb75a93f53ae722243986","src/dispatcher/global.rs":"58591cc7ccf55196fd4d8b8a6a73ed7ac35b5e80534e00e02a012367f3f6d296","src/dispatcher/mod.rs":"e86a10f575364575604b00962755f86a1bcb43b280e00b9505f0e8e14995343d","src/glean_metrics.rs":"a5e1ea9c4dccb81aec4aa584bd76cf47e916c66af4aff4a0ef5aa297ee2d9aa3","src/lib.rs":"d5ad8f4b404a78b685adf8364f352db88d4fe96fd0f0b1a127bec9ff572edcbc","src/net/http_uploader.rs":"9e8c1837ca0d3f6ea165ec936ab054173c4fe95a958710176c33b4d4d1d98beb","src/net/mod.rs":"59db2f4dcfd0a2d77feb63f40cae2252da59fa8a87e10877fcb305eb91aa0645","src/pings.rs":"2dfccd84848e1933aa4f6a7a707c58ec794c8f73ef2d93ea4d4df71d4e6abc31","src/private/boolean.rs":"c6fc72573b5d19748418bcccc42d5f706d820d1e31e35a1aad1202cfc73e16a0","src/private/counter.rs":"104bc1a332306edf3597c627d94bc3024a4239e492e045200b5de0fcb2c0bafa","src/private/custom_distribution.rs":"7c9a56e1beac4bbd80d8acb0463daa240d6e80a424dd2de7c005c6ab355cab91","src/private/datetime.rs":"41ff623d1062ef5a85a8d19fae3d48c88ed8f9b59cf8881524e0f5898324a310","src/private/event.rs":"dfdc5a2aa33d7249fdf1bab162d34b1fd5df6ebe1ea77f617229ba35b393743f","src/private/labeled.rs":"ddc93655ac94e47ccc06a0e96ff8918a9d801c1345b817d5305d319d2da10eb2","src/private/memory_distribution.rs":"fe3c828294c3029af01247230c7aa717aa926db507649707113385ec8923eb23","src/private/mod.rs":"9a9031401285468b98bbafb3a71175bf9950c114aa229ae7634484eac7eb2b12","src/private/ping.rs":"45a78f437543a2c922fd94507446cad8b2ab9955356a87046c2db047d7ae4ae9","src/private/quantity.rs":"db922490b1be80c993bda6e604da8004831ecf20da160847aea49b927aca5bd5","src/private/recorded_experiment_data.rs":"66b2601902a2dc2b7a283717c21ce754de94fcca30d12e0398195c8ad49c90af","src/private/string.rs":"fede1ea64c346e8741af1e3fd8c9259b01bb3726f06bcd24d8b2b0b737dd6706","src/private/string_list.rs":"b4f339bc97a00c505ce56bca706f3d439728a3e349b20f952bddb243155e6d82","src/private/timespan.rs":"c4632c19bf4f357587ed4c1b6cf6d93e695c2f424f0afc4e2b33762b3d3dbbeb","src/private/timing_distribution.rs":"666ecff65d0cc61d21f5d043d982954b1bc284a359ea5cc9f6094d852f43b8e3","src/private/uuid.rs":"5997bb9da63384230e5422bb19e61e954901f44d79e39d6eca0c26444e05c2ca","src/system.rs":"ba7b3eac040abe4691d9d287562ddca6d7e92a6d6109c3f0c443b707a100d75a","src/test.rs":"9441d62ff4e45906908d7b6937c3744212e3cd1cbf214e0f84c14c4e836e830a","tests/common/mod.rs":"4837df2e771929cc077e6fb9a9239645e8e0f7bc6c9f409b71c4d147edf334fc","tests/init_fails.rs":"32614f46e49ec91cd33bc381246b44c22caa19f3eca5c2708589619cd1a99471","tests/never_init.rs":"1f33b8ce7ca3514b57b48cc16d98408974c85cf8aa7d13257ffc2ad878ebb295","tests/no_time_to_init.rs":"af55667ce9a7331d48e6a01815f8f184ae252dfc1aefcd8aeb478100a3726972","tests/schema.rs":"b5acf42de034626f2b7e61fec9709a00370ce80ae3b2bab4db9fc79a20ea5f31","tests/simple.rs":"1c1ec8babd3803a4e117d59c62215acf4bb73b1d9d34278c8882216a2686dbca"},"package":"120a2f092bcf0b8fa0ec864e1a063426638f7084671eddfd138b5571e1e2c6fd"}
|
|
@ -13,7 +13,7 @@
|
||||||
[package]
|
[package]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
name = "glean"
|
name = "glean"
|
||||||
version = "33.8.0"
|
version = "33.9.1"
|
||||||
authors = ["Jan-Erik Rediger <jrediger@mozilla.com>", "The Glean Team <glean-team@mozilla.com>"]
|
authors = ["Jan-Erik Rediger <jrediger@mozilla.com>", "The Glean Team <glean-team@mozilla.com>"]
|
||||||
include = ["/README.md", "/LICENSE", "/src", "/tests", "/Cargo.toml"]
|
include = ["/README.md", "/LICENSE", "/src", "/tests", "/Cargo.toml"]
|
||||||
description = "Glean SDK Rust language bindings"
|
description = "Glean SDK Rust language bindings"
|
||||||
|
@ -21,11 +21,15 @@ readme = "README.md"
|
||||||
keywords = ["telemetry", "glean"]
|
keywords = ["telemetry", "glean"]
|
||||||
license = "MPL-2.0"
|
license = "MPL-2.0"
|
||||||
repository = "https://github.com/mozilla/glean"
|
repository = "https://github.com/mozilla/glean"
|
||||||
|
[dependencies.chrono]
|
||||||
|
version = "0.4.10"
|
||||||
|
features = ["serde"]
|
||||||
|
|
||||||
[dependencies.crossbeam-channel]
|
[dependencies.crossbeam-channel]
|
||||||
version = "0.4.3"
|
version = "0.4.3"
|
||||||
|
|
||||||
[dependencies.glean-core]
|
[dependencies.glean-core]
|
||||||
version = "33.8.0"
|
version = "33.9.1"
|
||||||
|
|
||||||
[dependencies.inherent]
|
[dependencies.inherent]
|
||||||
version = "0.1.4"
|
version = "0.1.4"
|
||||||
|
|
|
@ -52,7 +52,6 @@ pub(crate) fn new_glean(
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
crate::shutdown();
|
|
||||||
crate::test_reset_glean(cfg, ClientInfoMetrics::unknown(), clear_stores);
|
crate::test_reset_glean(cfg, ClientInfoMetrics::unknown(), clear_stores);
|
||||||
dir
|
dir
|
||||||
}
|
}
|
||||||
|
|
|
@ -59,13 +59,7 @@ pub fn flush_init() -> Result<(), DispatchError> {
|
||||||
guard().flush_init()
|
guard().flush_init()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Shuts down the dispatch queue.
|
fn join_dispatcher_thread() -> Result<(), DispatchError> {
|
||||||
///
|
|
||||||
/// This will initiate a shutdown of the worker thread
|
|
||||||
/// and no new tasks will be processed after this.
|
|
||||||
pub fn shutdown() -> Result<(), DispatchError> {
|
|
||||||
guard().shutdown()?;
|
|
||||||
|
|
||||||
// After we issue the shutdown command, make sure to wait for the
|
// After we issue the shutdown command, make sure to wait for the
|
||||||
// worker thread to join.
|
// worker thread to join.
|
||||||
let mut lock = GLOBAL_DISPATCHER.write().unwrap();
|
let mut lock = GLOBAL_DISPATCHER.write().unwrap();
|
||||||
|
@ -78,6 +72,25 @@ pub fn shutdown() -> Result<(), DispatchError> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Kill the blocked dispatcher without processing the queue.
|
||||||
|
///
|
||||||
|
/// This will immediately shutdown the worker thread
|
||||||
|
/// and no other tasks will be processed.
|
||||||
|
/// This only has an effect when the queue is still blocked.
|
||||||
|
pub fn kill() -> Result<(), DispatchError> {
|
||||||
|
guard().kill()?;
|
||||||
|
join_dispatcher_thread()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Shuts down the dispatch queue.
|
||||||
|
///
|
||||||
|
/// This will initiate a shutdown of the worker thread
|
||||||
|
/// and no new tasks will be processed after this.
|
||||||
|
pub fn shutdown() -> Result<(), DispatchError> {
|
||||||
|
guard().shutdown()?;
|
||||||
|
join_dispatcher_thread()
|
||||||
|
}
|
||||||
|
|
||||||
/// TEST ONLY FUNCTION.
|
/// TEST ONLY FUNCTION.
|
||||||
/// Resets the Glean state and triggers init again.
|
/// Resets the Glean state and triggers init again.
|
||||||
pub(crate) fn reset_dispatcher() {
|
pub(crate) fn reset_dispatcher() {
|
||||||
|
|
|
@ -42,6 +42,14 @@ pub use global::*;
|
||||||
|
|
||||||
mod global;
|
mod global;
|
||||||
|
|
||||||
|
/// Command received while blocked from further work.
|
||||||
|
enum Blocked {
|
||||||
|
/// Shutdown immediately without processing the queue.
|
||||||
|
Shutdown,
|
||||||
|
/// Unblock and continue with work as normal.
|
||||||
|
Continue,
|
||||||
|
}
|
||||||
|
|
||||||
/// The command a worker should execute.
|
/// The command a worker should execute.
|
||||||
enum Command {
|
enum Command {
|
||||||
/// A task is a user-defined function to run.
|
/// A task is a user-defined function to run.
|
||||||
|
@ -100,7 +108,7 @@ struct DispatchGuard {
|
||||||
queue_preinit: Arc<AtomicBool>,
|
queue_preinit: Arc<AtomicBool>,
|
||||||
|
|
||||||
/// Used to unblock the worker thread initially.
|
/// Used to unblock the worker thread initially.
|
||||||
block_sender: Sender<()>,
|
block_sender: Sender<Blocked>,
|
||||||
|
|
||||||
/// Sender for the preinit queue.
|
/// Sender for the preinit queue.
|
||||||
preinit_sender: Sender<Command>,
|
preinit_sender: Sender<Command>,
|
||||||
|
@ -146,6 +154,18 @@ impl DispatchGuard {
|
||||||
.expect("Failed to receive message on single-use channel");
|
.expect("Failed to receive message on single-use channel");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn kill(&mut self) -> Result<(), DispatchError> {
|
||||||
|
// We immediately stop queueing in the pre-init buffer.
|
||||||
|
let old_val = self.queue_preinit.swap(false, Ordering::SeqCst);
|
||||||
|
if !old_val {
|
||||||
|
return Err(DispatchError::AlreadyFlushed);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unblock the worker thread exactly once.
|
||||||
|
self.block_sender.send(Blocked::Shutdown)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
fn flush_init(&mut self) -> Result<(), DispatchError> {
|
fn flush_init(&mut self) -> Result<(), DispatchError> {
|
||||||
// We immediately stop queueing in the pre-init buffer.
|
// We immediately stop queueing in the pre-init buffer.
|
||||||
let old_val = self.queue_preinit.swap(false, Ordering::SeqCst);
|
let old_val = self.queue_preinit.swap(false, Ordering::SeqCst);
|
||||||
|
@ -154,7 +174,7 @@ impl DispatchGuard {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Unblock the worker thread exactly once.
|
// Unblock the worker thread exactly once.
|
||||||
self.block_sender.send(())?;
|
self.block_sender.send(Blocked::Continue)?;
|
||||||
|
|
||||||
// Single-use channel to communicate with the worker thread.
|
// Single-use channel to communicate with the worker thread.
|
||||||
let (swap_sender, swap_receiver) = bounded(0);
|
let (swap_sender, swap_receiver) = bounded(0);
|
||||||
|
@ -193,7 +213,7 @@ impl Dispatcher {
|
||||||
///
|
///
|
||||||
/// [`flush_init`]: #method.flush_init
|
/// [`flush_init`]: #method.flush_init
|
||||||
pub fn new(max_queue_size: usize) -> Self {
|
pub fn new(max_queue_size: usize) -> Self {
|
||||||
let (block_sender, block_receiver) = bounded(0);
|
let (block_sender, block_receiver) = bounded(1);
|
||||||
let (preinit_sender, preinit_receiver) = bounded(max_queue_size);
|
let (preinit_sender, preinit_receiver) = bounded(max_queue_size);
|
||||||
let (sender, mut unbounded_receiver) = unbounded();
|
let (sender, mut unbounded_receiver) = unbounded();
|
||||||
|
|
||||||
|
@ -202,11 +222,20 @@ impl Dispatcher {
|
||||||
let worker = thread::Builder::new()
|
let worker = thread::Builder::new()
|
||||||
.name("glean.dispatcher".into())
|
.name("glean.dispatcher".into())
|
||||||
.spawn(move || {
|
.spawn(move || {
|
||||||
if block_receiver.recv().is_err() {
|
match block_receiver.recv() {
|
||||||
// The other side was disconnected.
|
Err(_) => {
|
||||||
// There's nothing the worker thread can do.
|
// The other side was disconnected.
|
||||||
log::error!("The task producer was disconnected. Worker thread will exit.");
|
// There's nothing the worker thread can do.
|
||||||
return;
|
log::error!("The task producer was disconnected. Worker thread will exit.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Ok(Blocked::Shutdown) => {
|
||||||
|
// The other side wants us to stop immediately
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Ok(Blocked::Continue) => {
|
||||||
|
// Queue is unblocked, processing continues as normal.
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut receiver = preinit_receiver;
|
let mut receiver = preinit_receiver;
|
||||||
|
|
|
@ -49,7 +49,7 @@ pub use core_metrics::ClientInfoMetrics;
|
||||||
pub use glean_core::{
|
pub use glean_core::{
|
||||||
global_glean,
|
global_glean,
|
||||||
metrics::{DistributionData, MemoryUnit, RecordedEvent, TimeUnit},
|
metrics::{DistributionData, MemoryUnit, RecordedEvent, TimeUnit},
|
||||||
setup_glean, CommonMetricData, Error, ErrorType, Glean, Lifetime, Result,
|
setup_glean, CommonMetricData, Error, ErrorType, Glean, HistogramType, Lifetime, Result,
|
||||||
};
|
};
|
||||||
use private::RecordedExperimentData;
|
use private::RecordedExperimentData;
|
||||||
|
|
||||||
|
@ -92,6 +92,9 @@ static PRE_INIT_DEBUG_VIEW_TAG: OnceCell<Mutex<String>> = OnceCell::new();
|
||||||
static PRE_INIT_LOG_PINGS: AtomicBool = AtomicBool::new(false);
|
static PRE_INIT_LOG_PINGS: AtomicBool = AtomicBool::new(false);
|
||||||
static PRE_INIT_SOURCE_TAGS: OnceCell<Mutex<Vec<String>>> = OnceCell::new();
|
static PRE_INIT_SOURCE_TAGS: OnceCell<Mutex<Vec<String>>> = OnceCell::new();
|
||||||
|
|
||||||
|
/// Keep track of pings registered before Glean is initialized.
|
||||||
|
static PRE_INIT_PING_REGISTRATION: OnceCell<Mutex<Vec<private::PingType>>> = OnceCell::new();
|
||||||
|
|
||||||
/// A global singleton storing additional state for Glean.
|
/// A global singleton storing additional state for Glean.
|
||||||
///
|
///
|
||||||
/// Requires a Mutex, because in tests we can actual reset this.
|
/// Requires a Mutex, because in tests we can actual reset this.
|
||||||
|
@ -251,8 +254,16 @@ pub fn initialize(cfg: Configuration, client_info: ClientInfoMetrics) {
|
||||||
glean.register_ping_type(&glean_metrics::pings::metrics.ping_type);
|
glean.register_ping_type(&glean_metrics::pings::metrics.ping_type);
|
||||||
glean.register_ping_type(&glean_metrics::pings::events.ping_type);
|
glean.register_ping_type(&glean_metrics::pings::events.ping_type);
|
||||||
|
|
||||||
// TODO: perform registration of pings that were attempted to be
|
// Perform registration of pings that were attempted to be
|
||||||
// registered before init. See bug 1673850.
|
// registered before init.
|
||||||
|
if let Some(tags) = PRE_INIT_PING_REGISTRATION.get() {
|
||||||
|
let lock = tags.try_lock();
|
||||||
|
if let Ok(pings) = lock {
|
||||||
|
for ping in &*pings {
|
||||||
|
glean.register_ping_type(&ping.ping_type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// If this is the first time ever the Glean SDK runs, make sure to set
|
// If this is the first time ever the Glean SDK runs, make sure to set
|
||||||
// some initial core metrics in case we need to generate early pings.
|
// some initial core metrics in case we need to generate early pings.
|
||||||
|
@ -310,8 +321,12 @@ pub fn initialize(cfg: Configuration, client_info: ClientInfoMetrics) {
|
||||||
/// This currently only attempts to shut down the
|
/// This currently only attempts to shut down the
|
||||||
/// internal dispatcher.
|
/// internal dispatcher.
|
||||||
pub fn shutdown() {
|
pub fn shutdown() {
|
||||||
if !was_initialize_called() {
|
if global_glean().is_none() {
|
||||||
log::warn!("Shutdown called before Glean is initialized");
|
log::warn!("Shutdown called before Glean is initialized");
|
||||||
|
if let Err(e) = dispatcher::kill() {
|
||||||
|
log::error!("Can't kill dispatcher thread: {:?}", e);
|
||||||
|
}
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -320,6 +335,17 @@ pub fn shutdown() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Block on the dispatcher emptying.
|
||||||
|
///
|
||||||
|
/// This will panic if called before Glean is initialized.
|
||||||
|
fn block_on_dispatcher() {
|
||||||
|
assert!(
|
||||||
|
was_initialize_called(),
|
||||||
|
"initialize was never called. Can't block on the dispatcher queue."
|
||||||
|
);
|
||||||
|
dispatcher::block_on_queue()
|
||||||
|
}
|
||||||
|
|
||||||
/// Checks if [`initialize`] was ever called.
|
/// Checks if [`initialize`] was ever called.
|
||||||
///
|
///
|
||||||
/// # Returns
|
/// # Returns
|
||||||
|
@ -411,6 +437,14 @@ pub fn register_ping_type(ping: &private::PingType) {
|
||||||
glean.register_ping_type(&ping.ping_type);
|
glean.register_ping_type(&ping.ping_type);
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
} else {
|
||||||
|
// We need to keep track of pings, so they get re-registered after a reset or
|
||||||
|
// if ping registration is attempted before Glean initializes.
|
||||||
|
// This state is kept across Glean resets, which should only ever happen in test mode.
|
||||||
|
// It's a set and keeping them around forever should not have much of an impact.
|
||||||
|
let m = PRE_INIT_PING_REGISTRATION.get_or_init(Default::default);
|
||||||
|
let mut lock = m.lock().unwrap();
|
||||||
|
lock.push(ping.clone());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -510,7 +544,7 @@ pub fn set_experiment_inactive(experiment_id: String) {
|
||||||
/// Checks if an experiment is currently active.
|
/// Checks if an experiment is currently active.
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub(crate) fn test_is_experiment_active(experiment_id: String) -> bool {
|
pub(crate) fn test_is_experiment_active(experiment_id: String) -> bool {
|
||||||
dispatcher::block_on_queue();
|
block_on_dispatcher();
|
||||||
with_glean(|glean| glean.test_is_experiment_active(experiment_id.to_owned()))
|
with_glean(|glean| glean.test_is_experiment_active(experiment_id.to_owned()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -519,7 +553,7 @@ pub(crate) fn test_is_experiment_active(experiment_id: String) -> bool {
|
||||||
/// the id isn't found.
|
/// the id isn't found.
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub(crate) fn test_get_experiment_data(experiment_id: String) -> RecordedExperimentData {
|
pub(crate) fn test_get_experiment_data(experiment_id: String) -> RecordedExperimentData {
|
||||||
dispatcher::block_on_queue();
|
block_on_dispatcher();
|
||||||
with_glean(|glean| {
|
with_glean(|glean| {
|
||||||
let json_data = glean
|
let json_data = glean
|
||||||
.test_get_experiment_data_as_json(experiment_id.to_owned())
|
.test_get_experiment_data_as_json(experiment_id.to_owned())
|
||||||
|
|
|
@ -22,9 +22,6 @@ pub use http_uploader::*;
|
||||||
|
|
||||||
mod http_uploader;
|
mod http_uploader;
|
||||||
|
|
||||||
/// The duration the uploader thread should sleep, when told to by glean-core.
|
|
||||||
const THROTTLE_BACKOFF_TIME: Duration = Duration::from_secs(60);
|
|
||||||
|
|
||||||
/// A description of a component used to upload pings.
|
/// A description of a component used to upload pings.
|
||||||
pub trait PingUploader: std::fmt::Debug + Send + Sync {
|
pub trait PingUploader: std::fmt::Debug + Send + Sync {
|
||||||
/// Uploads a ping to a server.
|
/// Uploads a ping to a server.
|
||||||
|
@ -100,8 +97,8 @@ impl UploadManager {
|
||||||
// Process the upload response.
|
// Process the upload response.
|
||||||
with_glean(|glean| glean.process_ping_upload_response(&doc_id, result));
|
with_glean(|glean| glean.process_ping_upload_response(&doc_id, result));
|
||||||
}
|
}
|
||||||
PingUploadTask::Wait => {
|
PingUploadTask::Wait(time) => {
|
||||||
thread::sleep(THROTTLE_BACKOFF_TIME);
|
thread::sleep(Duration::from_millis(time));
|
||||||
}
|
}
|
||||||
PingUploadTask::Done => {
|
PingUploadTask::Done => {
|
||||||
// Nothing to do here, break out of the loop and clear the
|
// Nothing to do here, break out of the loop and clear the
|
||||||
|
|
|
@ -31,28 +31,13 @@ impl BooleanMetric {
|
||||||
|
|
||||||
#[inherent(pub)]
|
#[inherent(pub)]
|
||||||
impl glean_core::traits::Boolean for BooleanMetric {
|
impl glean_core::traits::Boolean for BooleanMetric {
|
||||||
/// Sets to the specified boolean value.
|
|
||||||
///
|
|
||||||
/// # Arguments
|
|
||||||
///
|
|
||||||
/// * `value` - the value to set.
|
|
||||||
fn set(&self, value: bool) {
|
fn set(&self, value: bool) {
|
||||||
let metric = Arc::clone(&self.0);
|
let metric = Arc::clone(&self.0);
|
||||||
dispatcher::launch(move || crate::with_glean(|glean| metric.set(glean, value)));
|
dispatcher::launch(move || crate::with_glean(|glean| metric.set(glean, value)));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// **Exported for test purposes.**
|
|
||||||
///
|
|
||||||
/// Gets the currently stored value as a boolean.
|
|
||||||
///
|
|
||||||
/// This doesn't clear the stored value.
|
|
||||||
///
|
|
||||||
/// # Arguments
|
|
||||||
///
|
|
||||||
/// * `ping_name` - represents the optional name of the ping to retrieve the
|
|
||||||
/// metric for. Defaults to the first value in `send_in_pings`.
|
|
||||||
fn test_get_value<'a, S: Into<Option<&'a str>>>(&self, ping_name: S) -> Option<bool> {
|
fn test_get_value<'a, S: Into<Option<&'a str>>>(&self, ping_name: S) -> Option<bool> {
|
||||||
dispatcher::block_on_queue();
|
crate::block_on_dispatcher();
|
||||||
|
|
||||||
let queried_ping_name = match ping_name.into() {
|
let queried_ping_name = match ping_name.into() {
|
||||||
Some(name) => name,
|
Some(name) => name,
|
||||||
|
|
|
@ -32,32 +32,13 @@ impl CounterMetric {
|
||||||
|
|
||||||
#[inherent(pub)]
|
#[inherent(pub)]
|
||||||
impl glean_core::traits::Counter for CounterMetric {
|
impl glean_core::traits::Counter for CounterMetric {
|
||||||
/// Increases the counter by `amount`.
|
|
||||||
///
|
|
||||||
/// # Arguments
|
|
||||||
///
|
|
||||||
/// * `amount` - The amount to increase by. Should be positive.
|
|
||||||
///
|
|
||||||
/// ## Notes
|
|
||||||
///
|
|
||||||
/// Logs an error if the `amount` is 0 or negative.
|
|
||||||
fn add(&self, amount: i32) {
|
fn add(&self, amount: i32) {
|
||||||
let metric = Arc::clone(&self.0);
|
let metric = Arc::clone(&self.0);
|
||||||
dispatcher::launch(move || crate::with_glean(|glean| metric.add(glean, amount)));
|
dispatcher::launch(move || crate::with_glean(|glean| metric.add(glean, amount)));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// **Exported for test purposes.**
|
|
||||||
///
|
|
||||||
/// Gets the currently stored value as an integer.
|
|
||||||
///
|
|
||||||
/// This doesn't clear the stored value.
|
|
||||||
///
|
|
||||||
/// # Arguments
|
|
||||||
///
|
|
||||||
/// * `ping_name` - represents the optional name of the ping to retrieve the
|
|
||||||
/// metric for. Defaults to the first value in `send_in_pings`.
|
|
||||||
fn test_get_value<'a, S: Into<Option<&'a str>>>(&self, ping_name: S) -> Option<i32> {
|
fn test_get_value<'a, S: Into<Option<&'a str>>>(&self, ping_name: S) -> Option<i32> {
|
||||||
dispatcher::block_on_queue();
|
crate::block_on_dispatcher();
|
||||||
|
|
||||||
let queried_ping_name = ping_name
|
let queried_ping_name = ping_name
|
||||||
.into()
|
.into()
|
||||||
|
@ -66,25 +47,12 @@ impl glean_core::traits::Counter for CounterMetric {
|
||||||
crate::with_glean(|glean| self.0.test_get_value(glean, queried_ping_name))
|
crate::with_glean(|glean| self.0.test_get_value(glean, queried_ping_name))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// **Exported for test purposes.**
|
|
||||||
///
|
|
||||||
/// Gets the number of recorded errors for the given metric and error type.
|
|
||||||
///
|
|
||||||
/// # Arguments
|
|
||||||
///
|
|
||||||
/// * `error` - The type of error
|
|
||||||
/// * `ping_name` - represents the optional name of the ping to retrieve the
|
|
||||||
/// metric for. Defaults to the first value in `send_in_pings`.
|
|
||||||
///
|
|
||||||
/// # Returns
|
|
||||||
///
|
|
||||||
/// The number of errors reported.
|
|
||||||
fn test_get_num_recorded_errors<'a, S: Into<Option<&'a str>>>(
|
fn test_get_num_recorded_errors<'a, S: Into<Option<&'a str>>>(
|
||||||
&self,
|
&self,
|
||||||
error: ErrorType,
|
error: ErrorType,
|
||||||
ping_name: S,
|
ping_name: S,
|
||||||
) -> i32 {
|
) -> i32 {
|
||||||
dispatcher::block_on_queue();
|
crate::block_on_dispatcher();
|
||||||
|
|
||||||
crate::with_glean_mut(|glean| {
|
crate::with_glean_mut(|glean| {
|
||||||
glean_core::test_get_num_recorded_errors(&glean, self.0.meta(), error, ping_name.into())
|
glean_core::test_get_num_recorded_errors(&glean, self.0.meta(), error, ping_name.into())
|
||||||
|
|
|
@ -0,0 +1,81 @@
|
||||||
|
// 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::{DistributionData, MetricType};
|
||||||
|
use glean_core::{CommonMetricData, ErrorType, HistogramType};
|
||||||
|
|
||||||
|
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 custom distribution 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 CustomDistributionMetric(pub(crate) Arc<glean_core::metrics::CustomDistributionMetric>);
|
||||||
|
|
||||||
|
impl CustomDistributionMetric {
|
||||||
|
/// The public constructor used by automatically generated metrics.
|
||||||
|
pub fn new(
|
||||||
|
meta: CommonMetricData,
|
||||||
|
range_min: u64,
|
||||||
|
range_max: u64,
|
||||||
|
bucket_count: u64,
|
||||||
|
histogram_type: HistogramType,
|
||||||
|
) -> Self {
|
||||||
|
Self(Arc::new(
|
||||||
|
glean_core::metrics::CustomDistributionMetric::new(
|
||||||
|
meta,
|
||||||
|
range_min,
|
||||||
|
range_max,
|
||||||
|
bucket_count,
|
||||||
|
histogram_type,
|
||||||
|
),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inherent(pub)]
|
||||||
|
impl glean_core::traits::CustomDistribution for CustomDistributionMetric {
|
||||||
|
fn accumulate_samples_signed(&self, samples: Vec<i64>) {
|
||||||
|
let metric = Arc::clone(&self.0);
|
||||||
|
dispatcher::launch(move || {
|
||||||
|
crate::with_glean(|glean| metric.accumulate_samples_signed(glean, samples))
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_get_value<'a, S: Into<Option<&'a str>>>(
|
||||||
|
&self,
|
||||||
|
ping_name: S,
|
||||||
|
) -> Option<DistributionData> {
|
||||||
|
crate::block_on_dispatcher();
|
||||||
|
|
||||||
|
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 {
|
||||||
|
crate::block_on_dispatcher();
|
||||||
|
|
||||||
|
crate::with_glean_mut(|glean| {
|
||||||
|
glean_core::test_get_num_recorded_errors(&glean, self.0.meta(), error, ping_name.into())
|
||||||
|
.unwrap_or(0)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
|
@ -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> {
|
||||||
|
crate::block_on_dispatcher();
|
||||||
|
|
||||||
|
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 {
|
||||||
|
crate::block_on_dispatcher();
|
||||||
|
|
||||||
|
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
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
|
@ -63,7 +63,7 @@ impl<K: traits::ExtraKeys> traits::Event for EventMetric<K> {
|
||||||
&self,
|
&self,
|
||||||
ping_name: S,
|
ping_name: S,
|
||||||
) -> Option<Vec<RecordedEvent>> {
|
) -> Option<Vec<RecordedEvent>> {
|
||||||
dispatcher::block_on_queue();
|
crate::block_on_dispatcher();
|
||||||
|
|
||||||
let queried_ping_name = ping_name
|
let queried_ping_name = ping_name
|
||||||
.into()
|
.into()
|
||||||
|
@ -77,7 +77,7 @@ impl<K: traits::ExtraKeys> traits::Event for EventMetric<K> {
|
||||||
error: ErrorType,
|
error: ErrorType,
|
||||||
ping_name: S,
|
ping_name: S,
|
||||||
) -> i32 {
|
) -> i32 {
|
||||||
dispatcher::block_on_queue();
|
crate::block_on_dispatcher();
|
||||||
|
|
||||||
crate::with_glean_mut(|glean| {
|
crate::with_glean_mut(|glean| {
|
||||||
glean_core::test_get_num_recorded_errors(
|
glean_core::test_get_num_recorded_errors(
|
||||||
|
|
|
@ -8,8 +8,6 @@ use std::sync::Arc;
|
||||||
use glean_core::metrics::MetricType;
|
use glean_core::metrics::MetricType;
|
||||||
use glean_core::ErrorType;
|
use glean_core::ErrorType;
|
||||||
|
|
||||||
use crate::dispatcher;
|
|
||||||
|
|
||||||
/// Sealed traits protect against downstream implementations.
|
/// Sealed traits protect against downstream implementations.
|
||||||
///
|
///
|
||||||
/// We wrap it in a private module that is inaccessible outside of this module.
|
/// We wrap it in a private module that is inaccessible outside of this module.
|
||||||
|
@ -124,41 +122,17 @@ impl<T> glean_core::traits::Labeled<T> for LabeledMetric<T>
|
||||||
where
|
where
|
||||||
T: AllowLabeled + Clone,
|
T: AllowLabeled + Clone,
|
||||||
{
|
{
|
||||||
/// Gets a specific metric for a given label.
|
|
||||||
///
|
|
||||||
/// If a set of acceptable labels were specified in the `metrics.yaml` file,
|
|
||||||
/// and the given label is not in the set, it will be recorded under the special `OTHER_LABEL` label.
|
|
||||||
///
|
|
||||||
/// If a set of acceptable labels was not specified in the `metrics.yaml` file,
|
|
||||||
/// only the first 16 unique labels will be used.
|
|
||||||
/// After that, any additional labels will be recorded under the special `OTHER_LABEL` label.
|
|
||||||
///
|
|
||||||
/// Labels must be `snake_case` and less than 30 characters.
|
|
||||||
/// If an invalid label is used, the metric will be recorded in the special `OTHER_LABEL` label.
|
|
||||||
fn get(&self, label: &str) -> T {
|
fn get(&self, label: &str) -> T {
|
||||||
let inner = self.0.get(label);
|
let inner = self.0.get(label);
|
||||||
T::from_inner(inner)
|
T::from_inner(inner)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// **Exported for test purposes.**
|
|
||||||
///
|
|
||||||
/// Gets the number of recorded errors for the given metric and error type.
|
|
||||||
///
|
|
||||||
/// # Arguments
|
|
||||||
///
|
|
||||||
/// * `error` - The type of error
|
|
||||||
/// * `ping_name` - represents the optional name of the ping to retrieve the
|
|
||||||
/// metric for. Defaults to the first value in `send_in_pings`.
|
|
||||||
///
|
|
||||||
/// # Returns
|
|
||||||
///
|
|
||||||
/// The number of errors reported.
|
|
||||||
fn test_get_num_recorded_errors<'a, S: Into<Option<&'a str>>>(
|
fn test_get_num_recorded_errors<'a, S: Into<Option<&'a str>>>(
|
||||||
&self,
|
&self,
|
||||||
error: ErrorType,
|
error: ErrorType,
|
||||||
ping_name: S,
|
ping_name: S,
|
||||||
) -> i32 {
|
) -> i32 {
|
||||||
dispatcher::block_on_queue();
|
crate::block_on_dispatcher();
|
||||||
|
|
||||||
crate::with_glean_mut(|glean| {
|
crate::with_glean_mut(|glean| {
|
||||||
glean_core::test_get_num_recorded_errors(
|
glean_core::test_get_num_recorded_errors(
|
||||||
|
|
|
@ -43,7 +43,7 @@ impl glean_core::traits::MemoryDistribution for MemoryDistributionMetric {
|
||||||
&self,
|
&self,
|
||||||
ping_name: S,
|
ping_name: S,
|
||||||
) -> Option<DistributionData> {
|
) -> Option<DistributionData> {
|
||||||
dispatcher::block_on_queue();
|
crate::block_on_dispatcher();
|
||||||
|
|
||||||
let queried_ping_name = ping_name
|
let queried_ping_name = ping_name
|
||||||
.into()
|
.into()
|
||||||
|
@ -57,7 +57,7 @@ impl glean_core::traits::MemoryDistribution for MemoryDistributionMetric {
|
||||||
error: ErrorType,
|
error: ErrorType,
|
||||||
ping_name: S,
|
ping_name: S,
|
||||||
) -> i32 {
|
) -> i32 {
|
||||||
dispatcher::block_on_queue();
|
crate::block_on_dispatcher();
|
||||||
|
|
||||||
crate::with_glean_mut(|glean| {
|
crate::with_glean_mut(|glean| {
|
||||||
glean_core::test_get_num_recorded_errors(&glean, self.0.meta(), error, ping_name.into())
|
glean_core::test_get_num_recorded_errors(&glean, self.0.meta(), error, ping_name.into())
|
||||||
|
|
|
@ -6,6 +6,8 @@
|
||||||
|
|
||||||
mod boolean;
|
mod boolean;
|
||||||
mod counter;
|
mod counter;
|
||||||
|
mod custom_distribution;
|
||||||
|
mod datetime;
|
||||||
mod event;
|
mod event;
|
||||||
mod labeled;
|
mod labeled;
|
||||||
mod memory_distribution;
|
mod memory_distribution;
|
||||||
|
@ -13,12 +15,16 @@ mod ping;
|
||||||
mod quantity;
|
mod quantity;
|
||||||
mod recorded_experiment_data;
|
mod recorded_experiment_data;
|
||||||
mod string;
|
mod string;
|
||||||
|
mod string_list;
|
||||||
mod timespan;
|
mod timespan;
|
||||||
|
mod timing_distribution;
|
||||||
mod uuid;
|
mod uuid;
|
||||||
|
|
||||||
pub use self::uuid::UuidMetric;
|
pub use self::uuid::UuidMetric;
|
||||||
pub use boolean::BooleanMetric;
|
pub use boolean::BooleanMetric;
|
||||||
pub use counter::CounterMetric;
|
pub use counter::CounterMetric;
|
||||||
|
pub use custom_distribution::CustomDistributionMetric;
|
||||||
|
pub use datetime::{Datetime, DatetimeMetric};
|
||||||
pub use event::EventMetric;
|
pub use event::EventMetric;
|
||||||
pub use labeled::{AllowLabeled, LabeledMetric};
|
pub use labeled::{AllowLabeled, LabeledMetric};
|
||||||
pub use memory_distribution::MemoryDistributionMetric;
|
pub use memory_distribution::MemoryDistributionMetric;
|
||||||
|
@ -26,4 +32,6 @@ pub use ping::PingType;
|
||||||
pub use quantity::QuantityMetric;
|
pub use quantity::QuantityMetric;
|
||||||
pub use recorded_experiment_data::RecordedExperimentData;
|
pub use recorded_experiment_data::RecordedExperimentData;
|
||||||
pub use string::StringMetric;
|
pub use string::StringMetric;
|
||||||
|
pub use string_list::StringListMetric;
|
||||||
pub use timespan::TimespanMetric;
|
pub use timespan::TimespanMetric;
|
||||||
|
pub use timing_distribution::TimingDistributionMetric;
|
||||||
|
|
|
@ -42,25 +42,6 @@ impl PingType {
|
||||||
|
|
||||||
#[inherent(pub)]
|
#[inherent(pub)]
|
||||||
impl glean_core::traits::Ping for PingType {
|
impl glean_core::traits::Ping for PingType {
|
||||||
/// Collect and submit the ping for eventual upload.
|
|
||||||
///
|
|
||||||
/// This will collect all stored data to be included in the ping.
|
|
||||||
/// Data with lifetime `ping` will then be reset.
|
|
||||||
///
|
|
||||||
/// If the ping is configured with `send_if_empty = false`
|
|
||||||
/// and the ping currently contains no content,
|
|
||||||
/// it will not be queued for upload.
|
|
||||||
/// If the ping is configured with `send_if_empty = true`
|
|
||||||
/// it will be queued for upload even if otherwise empty.
|
|
||||||
///
|
|
||||||
/// Pings always contain the `ping_info` and `client_info` sections.
|
|
||||||
/// See [ping sections](https://mozilla.github.io/glean/book/user/pings/index.html#ping-sections)
|
|
||||||
/// for details.
|
|
||||||
///
|
|
||||||
/// # Arguments
|
|
||||||
///
|
|
||||||
/// * `reason` - The reason the ping is being submitted.
|
|
||||||
/// Must be one of the configured `reason_codes`.
|
|
||||||
fn submit(&self, reason: Option<&str>) {
|
fn submit(&self, reason: Option<&str>) {
|
||||||
crate::submit_ping(self, reason)
|
crate::submit_ping(self, reason)
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,28 +32,13 @@ impl QuantityMetric {
|
||||||
|
|
||||||
#[inherent(pub)]
|
#[inherent(pub)]
|
||||||
impl glean_core::traits::Quantity for QuantityMetric {
|
impl glean_core::traits::Quantity for QuantityMetric {
|
||||||
/// Sets to the specified value. Must be non-negative.
|
|
||||||
///
|
|
||||||
/// # Arguments
|
|
||||||
///
|
|
||||||
/// * `value` - The Quantity to set the metric to.
|
|
||||||
fn set(&self, value: i64) {
|
fn set(&self, value: i64) {
|
||||||
let metric = Arc::clone(&self.0);
|
let metric = Arc::clone(&self.0);
|
||||||
dispatcher::launch(move || crate::with_glean(|glean| metric.set(glean, value)));
|
dispatcher::launch(move || crate::with_glean(|glean| metric.set(glean, value)));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// **Exported for test purposes.**
|
|
||||||
///
|
|
||||||
/// Gets the currently stored value.
|
|
||||||
///
|
|
||||||
/// This doesn't clear the stored value.
|
|
||||||
///
|
|
||||||
/// # Arguments
|
|
||||||
///
|
|
||||||
/// * `ping_name` - represents the optional name of the ping to retrieve the
|
|
||||||
/// metric for. Defaults to the first value in `send_in_pings`.
|
|
||||||
fn test_get_value<'a, S: Into<Option<&'a str>>>(&self, ping_name: S) -> Option<i64> {
|
fn test_get_value<'a, S: Into<Option<&'a str>>>(&self, ping_name: S) -> Option<i64> {
|
||||||
dispatcher::block_on_queue();
|
crate::block_on_dispatcher();
|
||||||
|
|
||||||
let queried_ping_name = ping_name
|
let queried_ping_name = ping_name
|
||||||
.into()
|
.into()
|
||||||
|
@ -62,26 +47,13 @@ impl glean_core::traits::Quantity for QuantityMetric {
|
||||||
crate::with_glean(|glean| self.0.test_get_value(glean, queried_ping_name))
|
crate::with_glean(|glean| self.0.test_get_value(glean, queried_ping_name))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// **Exported for test purposes.**
|
|
||||||
///
|
|
||||||
/// Gets the number of recorded errors for the given metric and error type.
|
|
||||||
///
|
|
||||||
/// # Arguments
|
|
||||||
///
|
|
||||||
/// * `error` - The type of error
|
|
||||||
/// * `ping_name` - represents the optional name of the ping to retrieve the
|
|
||||||
/// metric for. Defaults to the first value in `send_in_pings`.
|
|
||||||
///
|
|
||||||
/// # Returns
|
|
||||||
///
|
|
||||||
/// The number of errors reported.
|
|
||||||
#[allow(dead_code)] // Remove after mozilla/glean#1328
|
#[allow(dead_code)] // Remove after mozilla/glean#1328
|
||||||
fn test_get_num_recorded_errors<'a, S: Into<Option<&'a str>>>(
|
fn test_get_num_recorded_errors<'a, S: Into<Option<&'a str>>>(
|
||||||
&self,
|
&self,
|
||||||
error: ErrorType,
|
error: ErrorType,
|
||||||
ping_name: S,
|
ping_name: S,
|
||||||
) -> i32 {
|
) -> i32 {
|
||||||
dispatcher::block_on_queue();
|
crate::block_on_dispatcher();
|
||||||
|
|
||||||
crate::with_glean_mut(|glean| {
|
crate::with_glean_mut(|glean| {
|
||||||
glean_core::test_get_num_recorded_errors(&glean, self.0.meta(), error, ping_name.into())
|
glean_core::test_get_num_recorded_errors(&glean, self.0.meta(), error, ping_name.into())
|
||||||
|
|
|
@ -32,36 +32,17 @@ impl StringMetric {
|
||||||
|
|
||||||
#[inherent(pub)]
|
#[inherent(pub)]
|
||||||
impl glean_core::traits::String for StringMetric {
|
impl glean_core::traits::String for StringMetric {
|
||||||
/// Sets to the specified value.
|
|
||||||
///
|
|
||||||
/// # Arguments
|
|
||||||
///
|
|
||||||
/// * `value` - The string to set the metric to.
|
|
||||||
///
|
|
||||||
/// ## Notes
|
|
||||||
///
|
|
||||||
/// Truncates the value if it is longer than `MAX_STRING_LENGTH` bytes and logs an error.
|
|
||||||
fn set<S: Into<std::string::String>>(&self, value: S) {
|
fn set<S: Into<std::string::String>>(&self, value: S) {
|
||||||
let metric = Arc::clone(&self.0);
|
let metric = Arc::clone(&self.0);
|
||||||
let new_value = value.into();
|
let new_value = value.into();
|
||||||
dispatcher::launch(move || crate::with_glean(|glean| metric.set(glean, new_value)));
|
dispatcher::launch(move || crate::with_glean(|glean| metric.set(glean, new_value)));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// **Exported for test purposes.**
|
|
||||||
///
|
|
||||||
/// Gets the currently stored value as a string.
|
|
||||||
///
|
|
||||||
/// This doesn't clear the stored value.
|
|
||||||
///
|
|
||||||
/// # Arguments
|
|
||||||
///
|
|
||||||
/// * `ping_name` - represents the optional name of the ping to retrieve the
|
|
||||||
/// metric for. Defaults to the first value in `send_in_pings`.
|
|
||||||
fn test_get_value<'a, S: Into<Option<&'a str>>>(
|
fn test_get_value<'a, S: Into<Option<&'a str>>>(
|
||||||
&self,
|
&self,
|
||||||
ping_name: S,
|
ping_name: S,
|
||||||
) -> Option<std::string::String> {
|
) -> Option<std::string::String> {
|
||||||
dispatcher::block_on_queue();
|
crate::block_on_dispatcher();
|
||||||
|
|
||||||
let queried_ping_name = ping_name
|
let queried_ping_name = ping_name
|
||||||
.into()
|
.into()
|
||||||
|
@ -70,25 +51,12 @@ impl glean_core::traits::String for StringMetric {
|
||||||
crate::with_glean(|glean| self.0.test_get_value(glean, queried_ping_name))
|
crate::with_glean(|glean| self.0.test_get_value(glean, queried_ping_name))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// **Exported for test purposes.**
|
|
||||||
///
|
|
||||||
/// Gets the number of recorded errors for the given metric and error type.
|
|
||||||
///
|
|
||||||
/// # Arguments
|
|
||||||
///
|
|
||||||
/// * `error` - The type of error
|
|
||||||
/// * `ping_name` - represents the optional name of the ping to retrieve the
|
|
||||||
/// metric for. Defaults to the first value in `send_in_pings`.
|
|
||||||
///
|
|
||||||
/// # Returns
|
|
||||||
///
|
|
||||||
/// The number of errors reported.
|
|
||||||
fn test_get_num_recorded_errors<'a, S: Into<Option<&'a str>>>(
|
fn test_get_num_recorded_errors<'a, S: Into<Option<&'a str>>>(
|
||||||
&self,
|
&self,
|
||||||
error: ErrorType,
|
error: ErrorType,
|
||||||
ping_name: S,
|
ping_name: S,
|
||||||
) -> i32 {
|
) -> i32 {
|
||||||
dispatcher::block_on_queue();
|
crate::block_on_dispatcher();
|
||||||
|
|
||||||
crate::with_glean_mut(|glean| {
|
crate::with_glean_mut(|glean| {
|
||||||
glean_core::test_get_num_recorded_errors(&glean, self.0.meta(), error, ping_name.into())
|
glean_core::test_get_num_recorded_errors(&glean, self.0.meta(), error, ping_name.into())
|
||||||
|
|
|
@ -0,0 +1,108 @@
|
||||||
|
// 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;
|
||||||
|
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 string list 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 StringListMetric(pub(crate) Arc<glean_core::metrics::StringListMetric>);
|
||||||
|
|
||||||
|
impl StringListMetric {
|
||||||
|
/// The public constructor used by automatically generated metrics.
|
||||||
|
pub fn new(meta: glean_core::CommonMetricData) -> Self {
|
||||||
|
Self(Arc::new(glean_core::metrics::StringListMetric::new(meta)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inherent(pub)]
|
||||||
|
impl glean_core::traits::StringList for StringListMetric {
|
||||||
|
fn add<S: Into<String>>(&self, value: S) {
|
||||||
|
let metric = Arc::clone(&self.0);
|
||||||
|
let new_value = value.into();
|
||||||
|
dispatcher::launch(move || crate::with_glean(|glean| metric.add(glean, new_value)));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set(&self, value: Vec<String>) {
|
||||||
|
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<Vec<String>> {
|
||||||
|
crate::block_on_dispatcher();
|
||||||
|
|
||||||
|
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 {
|
||||||
|
crate::block_on_dispatcher();
|
||||||
|
|
||||||
|
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, ErrorType};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn string_list_metric_docs() {
|
||||||
|
let _lock = lock_test();
|
||||||
|
let _t = new_glean(None, true);
|
||||||
|
|
||||||
|
let engine_metric: StringListMetric = StringListMetric::new(CommonMetricData {
|
||||||
|
name: "event".into(),
|
||||||
|
category: "test".into(),
|
||||||
|
send_in_pings: vec!["test1".into()],
|
||||||
|
..Default::default()
|
||||||
|
});
|
||||||
|
|
||||||
|
let engines: Vec<String> = vec!["Google".to_string(), "DuckDuckGo".to_string()];
|
||||||
|
|
||||||
|
// Add them one at a time
|
||||||
|
engines.iter().for_each(|x| engine_metric.add(x));
|
||||||
|
|
||||||
|
// Set them in one go
|
||||||
|
engine_metric.set(engines);
|
||||||
|
|
||||||
|
assert!(engine_metric.test_get_value(None).is_some());
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
vec!["Google".to_string(), "DuckDuckGo".to_string()],
|
||||||
|
engine_metric.test_get_value(None).unwrap()
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
0,
|
||||||
|
engine_metric.test_get_num_recorded_errors(ErrorType::InvalidValue, None)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -34,14 +34,6 @@ impl TimespanMetric {
|
||||||
|
|
||||||
#[inherent(pub)]
|
#[inherent(pub)]
|
||||||
impl glean_core::traits::Timespan for TimespanMetric {
|
impl glean_core::traits::Timespan for TimespanMetric {
|
||||||
/// Starts tracking time for the provided metric.
|
|
||||||
///
|
|
||||||
/// This uses an internal monotonic timer.
|
|
||||||
///
|
|
||||||
/// This records an error if it's already tracking time (i.e.
|
|
||||||
/// [`start`](TimespanMetric::start) was already called with no
|
|
||||||
/// corresponding [`stop`](TimespanMetric::stop)): in that case the original
|
|
||||||
/// start time will be preserved.
|
|
||||||
fn start(&self) {
|
fn start(&self) {
|
||||||
let start_time = time::precise_time_ns();
|
let start_time = time::precise_time_ns();
|
||||||
|
|
||||||
|
@ -56,9 +48,6 @@ impl glean_core::traits::Timespan for TimespanMetric {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Stops tracking time for the provided metric. Sets the metric to the elapsed time.
|
|
||||||
///
|
|
||||||
/// This will record an error if no [`start`](TimespanMetric::start) was called.
|
|
||||||
fn stop(&self) {
|
fn stop(&self) {
|
||||||
let stop_time = time::precise_time_ns();
|
let stop_time = time::precise_time_ns();
|
||||||
|
|
||||||
|
@ -73,8 +62,6 @@ impl glean_core::traits::Timespan for TimespanMetric {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Aborts a previous [`start`](TimespanMetric::start) call. No error is
|
|
||||||
/// recorded if no [`start`](TimespanMetric::start) was called.
|
|
||||||
fn cancel(&self) {
|
fn cancel(&self) {
|
||||||
let metric = Arc::clone(&self.0);
|
let metric = Arc::clone(&self.0);
|
||||||
dispatcher::launch(move || {
|
dispatcher::launch(move || {
|
||||||
|
@ -84,18 +71,9 @@ impl glean_core::traits::Timespan for TimespanMetric {
|
||||||
lock.cancel()
|
lock.cancel()
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
/// **Exported for test purposes.**
|
|
||||||
///
|
|
||||||
/// Gets the currently stored value as an integer.
|
|
||||||
///
|
|
||||||
/// This doesn't clear the stored value.
|
|
||||||
///
|
|
||||||
/// # Arguments
|
|
||||||
///
|
|
||||||
/// * `ping_name` - represents the optional name of the ping to retrieve the
|
|
||||||
/// metric for. Defaults to the first value in `send_in_pings`.
|
|
||||||
fn test_get_value<'a, S: Into<Option<&'a str>>>(&self, ping_name: S) -> Option<u64> {
|
fn test_get_value<'a, S: Into<Option<&'a str>>>(&self, ping_name: S) -> Option<u64> {
|
||||||
dispatcher::block_on_queue();
|
crate::block_on_dispatcher();
|
||||||
|
|
||||||
crate::with_glean(|glean| {
|
crate::with_glean(|glean| {
|
||||||
// Note: The order of operations is important here to avoid potential deadlocks because
|
// Note: The order of operations is important here to avoid potential deadlocks because
|
||||||
|
@ -116,25 +94,12 @@ impl glean_core::traits::Timespan for TimespanMetric {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// **Exported for test purposes.**
|
|
||||||
///
|
|
||||||
/// Gets the number of recorded errors for the given metric and error type.
|
|
||||||
///
|
|
||||||
/// # Arguments
|
|
||||||
///
|
|
||||||
/// * `error` - The type of error
|
|
||||||
/// * `ping_name` - represents the optional name of the ping to retrieve the
|
|
||||||
/// metric for. Defaults to the first value in `send_in_pings`.
|
|
||||||
///
|
|
||||||
/// # Returns
|
|
||||||
///
|
|
||||||
/// The number of errors reported.
|
|
||||||
fn test_get_num_recorded_errors<'a, S: Into<Option<&'a str>>>(
|
fn test_get_num_recorded_errors<'a, S: Into<Option<&'a str>>>(
|
||||||
&self,
|
&self,
|
||||||
error: ErrorType,
|
error: ErrorType,
|
||||||
ping_name: S,
|
ping_name: S,
|
||||||
) -> i32 {
|
) -> i32 {
|
||||||
dispatcher::block_on_queue();
|
crate::block_on_dispatcher();
|
||||||
|
|
||||||
let metric = self
|
let metric = self
|
||||||
.0
|
.0
|
||||||
|
|
|
@ -0,0 +1,93 @@
|
||||||
|
// 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, RwLock};
|
||||||
|
|
||||||
|
use glean_core::metrics::{DistributionData, MetricType, TimeUnit, TimerId};
|
||||||
|
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 timing distribution 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 TimingDistributionMetric(
|
||||||
|
pub(crate) Arc<RwLock<glean_core::metrics::TimingDistributionMetric>>,
|
||||||
|
);
|
||||||
|
|
||||||
|
impl TimingDistributionMetric {
|
||||||
|
/// The public constructor used by automatically generated metrics.
|
||||||
|
pub fn new(meta: glean_core::CommonMetricData, time_unit: TimeUnit) -> Self {
|
||||||
|
Self(Arc::new(RwLock::new(
|
||||||
|
glean_core::metrics::TimingDistributionMetric::new(meta, time_unit),
|
||||||
|
)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inherent(pub)]
|
||||||
|
impl glean_core::traits::TimingDistribution for TimingDistributionMetric {
|
||||||
|
fn start(&mut self) -> TimerId {
|
||||||
|
let start_time = time::precise_time_ns();
|
||||||
|
self.0.write().unwrap().set_start(start_time)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn stop_and_accumulate(&mut self, id: TimerId) {
|
||||||
|
let stop_time = time::precise_time_ns();
|
||||||
|
let metric = Arc::clone(&self.0);
|
||||||
|
dispatcher::launch(move || {
|
||||||
|
crate::with_glean(|glean| {
|
||||||
|
metric
|
||||||
|
.write()
|
||||||
|
.unwrap()
|
||||||
|
.set_stop_and_accumulate(glean, id, stop_time)
|
||||||
|
})
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn cancel(&mut self, id: TimerId) {
|
||||||
|
let metric = Arc::clone(&self.0);
|
||||||
|
dispatcher::launch(move || metric.write().unwrap().cancel(id));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_get_value<'a, S: Into<Option<&'a str>>>(
|
||||||
|
&self,
|
||||||
|
ping_name: S,
|
||||||
|
) -> Option<DistributionData> {
|
||||||
|
crate::block_on_dispatcher();
|
||||||
|
|
||||||
|
let inner = self.0.read().unwrap();
|
||||||
|
let queried_ping_name = ping_name
|
||||||
|
.into()
|
||||||
|
.unwrap_or_else(|| &inner.meta().send_in_pings[0]);
|
||||||
|
|
||||||
|
crate::with_glean(|glean| 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.0.read().unwrap().meta(),
|
||||||
|
error,
|
||||||
|
ping_name.into(),
|
||||||
|
)
|
||||||
|
.unwrap_or(0)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
|
@ -32,17 +32,11 @@ impl UuidMetric {
|
||||||
|
|
||||||
#[inherent(pub)]
|
#[inherent(pub)]
|
||||||
impl glean_core::traits::Uuid for UuidMetric {
|
impl glean_core::traits::Uuid for UuidMetric {
|
||||||
/// Sets to the specified value.
|
|
||||||
///
|
|
||||||
/// # Arguments
|
|
||||||
///
|
|
||||||
/// * `value` - The [`Uuid`](uuid::Uuid) to set the metric to.
|
|
||||||
fn set(&self, value: uuid::Uuid) {
|
fn set(&self, value: uuid::Uuid) {
|
||||||
let metric = Arc::clone(&self.0);
|
let metric = Arc::clone(&self.0);
|
||||||
dispatcher::launch(move || crate::with_glean(|glean| metric.set(glean, value)));
|
dispatcher::launch(move || crate::with_glean(|glean| metric.set(glean, value)));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Generates a new random UUID and sets the metric to it.
|
|
||||||
fn generate_and_set(&self) -> uuid::Uuid {
|
fn generate_and_set(&self) -> uuid::Uuid {
|
||||||
// TODO: We can use glean-core's generate_and_set after bug 1673017.
|
// TODO: We can use glean-core's generate_and_set after bug 1673017.
|
||||||
let uuid = uuid::Uuid::new_v4();
|
let uuid = uuid::Uuid::new_v4();
|
||||||
|
@ -50,18 +44,8 @@ impl glean_core::traits::Uuid for UuidMetric {
|
||||||
uuid
|
uuid
|
||||||
}
|
}
|
||||||
|
|
||||||
/// **Exported for test purposes.**
|
|
||||||
///
|
|
||||||
/// Gets the currently stored value.
|
|
||||||
///
|
|
||||||
/// This doesn't clear the stored value.
|
|
||||||
///
|
|
||||||
/// # Arguments
|
|
||||||
///
|
|
||||||
/// * `ping_name` - represents the optional name of the ping to retrieve the
|
|
||||||
/// metric for. Defaults to the first value in `send_in_pings`.
|
|
||||||
fn test_get_value<'a, S: Into<Option<&'a str>>>(&self, ping_name: S) -> Option<uuid::Uuid> {
|
fn test_get_value<'a, S: Into<Option<&'a str>>>(&self, ping_name: S) -> Option<uuid::Uuid> {
|
||||||
dispatcher::block_on_queue();
|
crate::block_on_dispatcher();
|
||||||
|
|
||||||
let queried_ping_name = ping_name
|
let queried_ping_name = ping_name
|
||||||
.into()
|
.into()
|
||||||
|
@ -70,25 +54,12 @@ impl glean_core::traits::Uuid for UuidMetric {
|
||||||
crate::with_glean(|glean| self.0.test_get_value(glean, queried_ping_name))
|
crate::with_glean(|glean| self.0.test_get_value(glean, queried_ping_name))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// **Exported for test purposes.**
|
|
||||||
///
|
|
||||||
/// Gets the number of recorded errors for the given metric and error type.
|
|
||||||
///
|
|
||||||
/// # Arguments
|
|
||||||
///
|
|
||||||
/// * `error` - The type of error
|
|
||||||
/// * `ping_name` - represents the optional name of the ping to retrieve the
|
|
||||||
/// metric for. Defaults to the first value in `send_in_pings`.
|
|
||||||
///
|
|
||||||
/// # Returns
|
|
||||||
///
|
|
||||||
/// The number of errors reported.
|
|
||||||
fn test_get_num_recorded_errors<'a, S: Into<Option<&'a str>>>(
|
fn test_get_num_recorded_errors<'a, S: Into<Option<&'a str>>>(
|
||||||
&self,
|
&self,
|
||||||
error: ErrorType,
|
error: ErrorType,
|
||||||
ping_name: S,
|
ping_name: S,
|
||||||
) -> i32 {
|
) -> i32 {
|
||||||
dispatcher::block_on_queue();
|
crate::block_on_dispatcher();
|
||||||
|
|
||||||
crate::with_glean_mut(|glean| {
|
crate::with_glean_mut(|glean| {
|
||||||
glean_core::test_get_num_recorded_errors(&glean, self.0.meta(), error, ping_name.into())
|
glean_core::test_get_num_recorded_errors(&glean, self.0.meta(), error, ping_name.into())
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
// 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/.
|
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
|
use crate::private::PingType;
|
||||||
use crate::private::{BooleanMetric, CounterMetric};
|
use crate::private::{BooleanMetric, CounterMetric};
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
@ -48,7 +49,7 @@ fn send_a_ping() {
|
||||||
};
|
};
|
||||||
|
|
||||||
let _t = new_glean(Some(cfg), true);
|
let _t = new_glean(Some(cfg), true);
|
||||||
crate::dispatcher::block_on_queue();
|
crate::block_on_dispatcher();
|
||||||
|
|
||||||
// Define a new ping and submit it.
|
// Define a new ping and submit it.
|
||||||
const PING_NAME: &str = "test-ping";
|
const PING_NAME: &str = "test-ping";
|
||||||
|
@ -65,7 +66,7 @@ fn disabling_upload_disables_metrics_recording() {
|
||||||
let _lock = lock_test();
|
let _lock = lock_test();
|
||||||
|
|
||||||
let _t = new_glean(None, true);
|
let _t = new_glean(None, true);
|
||||||
crate::dispatcher::block_on_queue();
|
crate::block_on_dispatcher();
|
||||||
|
|
||||||
let metric = BooleanMetric::new(CommonMetricData {
|
let metric = BooleanMetric::new(CommonMetricData {
|
||||||
name: "bool_metric".into(),
|
name: "bool_metric".into(),
|
||||||
|
@ -159,7 +160,7 @@ fn test_experiments_recording_before_glean_inits() {
|
||||||
ClientInfoMetrics::unknown(),
|
ClientInfoMetrics::unknown(),
|
||||||
false,
|
false,
|
||||||
);
|
);
|
||||||
crate::dispatcher::block_on_queue();
|
crate::block_on_dispatcher();
|
||||||
|
|
||||||
assert!(test_is_experiment_active(
|
assert!(test_is_experiment_active(
|
||||||
"experiment_set_preinit".to_string()
|
"experiment_set_preinit".to_string()
|
||||||
|
@ -269,7 +270,7 @@ fn initializing_twice_is_a_noop() {
|
||||||
true,
|
true,
|
||||||
);
|
);
|
||||||
|
|
||||||
dispatcher::block_on_queue();
|
crate::block_on_dispatcher();
|
||||||
|
|
||||||
test_reset_glean(
|
test_reset_glean(
|
||||||
Configuration {
|
Configuration {
|
||||||
|
@ -400,7 +401,7 @@ fn setting_debug_view_tag_before_initialization_should_not_crash() {
|
||||||
};
|
};
|
||||||
|
|
||||||
let _t = new_glean(Some(cfg), true);
|
let _t = new_glean(Some(cfg), true);
|
||||||
crate::dispatcher::block_on_queue();
|
crate::block_on_dispatcher();
|
||||||
|
|
||||||
// Submit a baseline ping.
|
// Submit a baseline ping.
|
||||||
submit_ping_by_name("baseline", Some("background"));
|
submit_ping_by_name("baseline", Some("background"));
|
||||||
|
@ -459,7 +460,7 @@ fn setting_source_tags_before_initialization_should_not_crash() {
|
||||||
};
|
};
|
||||||
|
|
||||||
let _t = new_glean(Some(cfg), true);
|
let _t = new_glean(Some(cfg), true);
|
||||||
crate::dispatcher::block_on_queue();
|
crate::block_on_dispatcher();
|
||||||
|
|
||||||
// Submit a baseline ping.
|
// Submit a baseline ping.
|
||||||
submit_ping_by_name("baseline", Some("background"));
|
submit_ping_by_name("baseline", Some("background"));
|
||||||
|
@ -481,3 +482,59 @@ fn setting_source_tags_before_initialization_should_not_crash() {
|
||||||
fn flipping_upload_enabled_respects_order_of_events() {
|
fn flipping_upload_enabled_respects_order_of_events() {
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn registering_pings_before_init_must_work() {
|
||||||
|
let _lock = lock_test();
|
||||||
|
|
||||||
|
destroy_glean(true);
|
||||||
|
assert!(!was_initialize_called());
|
||||||
|
|
||||||
|
// Define a fake uploader that reports back the submission headers
|
||||||
|
// using a crossbeam channel.
|
||||||
|
let (s, r) = crossbeam_channel::bounded::<String>(1);
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct FakeUploader {
|
||||||
|
sender: crossbeam_channel::Sender<String>,
|
||||||
|
};
|
||||||
|
impl net::PingUploader for FakeUploader {
|
||||||
|
fn upload(
|
||||||
|
&self,
|
||||||
|
url: String,
|
||||||
|
_body: Vec<u8>,
|
||||||
|
_headers: Vec<(String, String)>,
|
||||||
|
) -> net::UploadResult {
|
||||||
|
self.sender.send(url).unwrap();
|
||||||
|
net::UploadResult::HttpStatus(200)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a custom ping and attempt its registration.
|
||||||
|
let sample_ping = PingType::new("pre-register", true, true, vec![]);
|
||||||
|
|
||||||
|
// Create a custom configuration to use a fake uploader.
|
||||||
|
let dir = tempfile::tempdir().unwrap();
|
||||||
|
let tmpname = dir.path().display().to_string();
|
||||||
|
|
||||||
|
let cfg = Configuration {
|
||||||
|
data_path: tmpname,
|
||||||
|
application_id: GLOBAL_APPLICATION_ID.into(),
|
||||||
|
upload_enabled: true,
|
||||||
|
max_events: None,
|
||||||
|
delay_ping_lifetime_io: false,
|
||||||
|
channel: Some("testing".into()),
|
||||||
|
server_endpoint: Some("invalid-test-host".into()),
|
||||||
|
uploader: Some(Box::new(FakeUploader { sender: s })),
|
||||||
|
};
|
||||||
|
|
||||||
|
let _t = new_glean(Some(cfg), true);
|
||||||
|
crate::block_on_dispatcher();
|
||||||
|
|
||||||
|
// Submit a baseline ping.
|
||||||
|
sample_ping.submit(None);
|
||||||
|
|
||||||
|
// Wait for the ping to arrive.
|
||||||
|
let url = r.recv().unwrap();
|
||||||
|
assert!(url.contains("pre-register"));
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,50 @@
|
||||||
|
// 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/.
|
||||||
|
|
||||||
|
// #[allow(dead_code)] is required on this module as a workaround for
|
||||||
|
// https://github.com/rust-lang/rust/issues/46379
|
||||||
|
#![allow(dead_code)]
|
||||||
|
|
||||||
|
use std::{panic, process};
|
||||||
|
|
||||||
|
use glean::{ClientInfoMetrics, Configuration};
|
||||||
|
|
||||||
|
/// Initialize the env logger for a test environment.
|
||||||
|
///
|
||||||
|
/// When testing we want all logs to go to stdout/stderr by default.
|
||||||
|
pub fn enable_test_logging() {
|
||||||
|
let _ = env_logger::builder().is_test(true).try_init();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Install a panic handler that exits the whole process when a panic occurs.
|
||||||
|
///
|
||||||
|
/// This causes the process to exit even if a thread panics.
|
||||||
|
/// This is similar to the `panic=abort` configuration, but works in the default configuration
|
||||||
|
/// (as used by `cargo test`).
|
||||||
|
fn install_panic_handler() {
|
||||||
|
let orig_hook = panic::take_hook();
|
||||||
|
panic::set_hook(Box::new(move |panic_info| {
|
||||||
|
// invoke the default handler and exit the process
|
||||||
|
orig_hook(panic_info);
|
||||||
|
process::exit(1);
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create a new instance of Glean.
|
||||||
|
pub fn initialize(cfg: Configuration) {
|
||||||
|
// Ensure panics in threads, such as the init thread or the dispatcher, cause the process to
|
||||||
|
// exit.
|
||||||
|
//
|
||||||
|
// Otherwise in case of a panic in a thread the integration test will just hang.
|
||||||
|
// CI will terminate it after a timeout, but why stick around if we know nothing is happening?
|
||||||
|
install_panic_handler();
|
||||||
|
|
||||||
|
// Use some default values to make our life easier a bit.
|
||||||
|
let client_info = ClientInfoMetrics {
|
||||||
|
app_build: "1.0.0".to_string(),
|
||||||
|
app_display_version: "1.0.0".to_string(),
|
||||||
|
};
|
||||||
|
|
||||||
|
glean::initialize(cfg, client_info);
|
||||||
|
}
|
|
@ -0,0 +1,84 @@
|
||||||
|
// 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/.
|
||||||
|
|
||||||
|
//! This integration test should model how the RLB is used when embedded in another Rust application
|
||||||
|
//! (e.g. FOG/Firefox Desktop).
|
||||||
|
//!
|
||||||
|
//! We write a single test scenario per file to avoid any state keeping across runs
|
||||||
|
//! (different files run as different processes).
|
||||||
|
|
||||||
|
mod common;
|
||||||
|
|
||||||
|
use std::{thread, time::Duration};
|
||||||
|
|
||||||
|
use glean::Configuration;
|
||||||
|
|
||||||
|
/// Some user metrics.
|
||||||
|
mod metrics {
|
||||||
|
use glean::private::*;
|
||||||
|
use glean::{Lifetime, TimeUnit};
|
||||||
|
use glean_core::CommonMetricData;
|
||||||
|
use once_cell::sync::Lazy;
|
||||||
|
|
||||||
|
#[allow(non_upper_case_globals)]
|
||||||
|
pub static initialization: Lazy<TimespanMetric> = Lazy::new(|| {
|
||||||
|
TimespanMetric::new(
|
||||||
|
CommonMetricData {
|
||||||
|
name: "initialization".into(),
|
||||||
|
category: "sample".into(),
|
||||||
|
send_in_pings: vec!["validation".into()],
|
||||||
|
lifetime: Lifetime::Ping,
|
||||||
|
disabled: false,
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
TimeUnit::Nanosecond,
|
||||||
|
)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
mod pings {
|
||||||
|
use glean::private::PingType;
|
||||||
|
use once_cell::sync::Lazy;
|
||||||
|
|
||||||
|
#[allow(non_upper_case_globals)]
|
||||||
|
pub static validation: Lazy<PingType> =
|
||||||
|
Lazy::new(|| glean::private::PingType::new("validation", true, true, vec![]));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Test scenario: Glean initialization fails.
|
||||||
|
///
|
||||||
|
/// App tries to initialize Glean, but that somehow fails.
|
||||||
|
#[test]
|
||||||
|
fn init_fails() {
|
||||||
|
common::enable_test_logging();
|
||||||
|
|
||||||
|
metrics::initialization.start();
|
||||||
|
|
||||||
|
// Create a custom configuration to use a validating uploader.
|
||||||
|
let dir = tempfile::tempdir().unwrap();
|
||||||
|
let tmpname = dir.path().display().to_string();
|
||||||
|
|
||||||
|
let cfg = Configuration {
|
||||||
|
data_path: tmpname,
|
||||||
|
application_id: "".into(), // An empty application ID is invalid.
|
||||||
|
upload_enabled: true,
|
||||||
|
max_events: None,
|
||||||
|
delay_ping_lifetime_io: false,
|
||||||
|
channel: Some("testing".into()),
|
||||||
|
server_endpoint: Some("invalid-test-host".into()),
|
||||||
|
uploader: None,
|
||||||
|
};
|
||||||
|
common::initialize(cfg);
|
||||||
|
|
||||||
|
metrics::initialization.stop();
|
||||||
|
|
||||||
|
pings::validation.submit(None);
|
||||||
|
|
||||||
|
// We don't test for data here, as that would block on the dispatcher.
|
||||||
|
|
||||||
|
// Give it a short amount of time to actually finish initialization.
|
||||||
|
thread::sleep(Duration::from_millis(500));
|
||||||
|
|
||||||
|
glean::shutdown();
|
||||||
|
}
|
|
@ -0,0 +1,66 @@
|
||||||
|
// 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/.
|
||||||
|
|
||||||
|
//! This integration test should model how the RLB is used when embedded in another Rust application
|
||||||
|
//! (e.g. FOG/Firefox Desktop).
|
||||||
|
//!
|
||||||
|
//! We write a single test scenario per file to avoid any state keeping across runs
|
||||||
|
//! (different files run as different processes).
|
||||||
|
|
||||||
|
mod common;
|
||||||
|
|
||||||
|
/// Some user metrics.
|
||||||
|
mod metrics {
|
||||||
|
use glean::private::*;
|
||||||
|
use glean::{Lifetime, TimeUnit};
|
||||||
|
use glean_core::CommonMetricData;
|
||||||
|
use once_cell::sync::Lazy;
|
||||||
|
|
||||||
|
#[allow(non_upper_case_globals)]
|
||||||
|
pub static initialization: Lazy<TimespanMetric> = Lazy::new(|| {
|
||||||
|
TimespanMetric::new(
|
||||||
|
CommonMetricData {
|
||||||
|
name: "initialization".into(),
|
||||||
|
category: "sample".into(),
|
||||||
|
send_in_pings: vec!["validation".into()],
|
||||||
|
lifetime: Lifetime::Ping,
|
||||||
|
disabled: false,
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
TimeUnit::Nanosecond,
|
||||||
|
)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
mod pings {
|
||||||
|
use glean::private::PingType;
|
||||||
|
use once_cell::sync::Lazy;
|
||||||
|
|
||||||
|
#[allow(non_upper_case_globals)]
|
||||||
|
pub static validation: Lazy<PingType> =
|
||||||
|
Lazy::new(|| glean::private::PingType::new("validation", true, true, vec![]));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Test scenario: Glean is never initialized.
|
||||||
|
///
|
||||||
|
/// Glean is never initialized.
|
||||||
|
/// Some data is recorded early on.
|
||||||
|
/// And later the whole process is shutdown.
|
||||||
|
#[test]
|
||||||
|
fn never_initialize() {
|
||||||
|
common::enable_test_logging();
|
||||||
|
|
||||||
|
metrics::initialization.start();
|
||||||
|
|
||||||
|
// NOT calling `initialize` here.
|
||||||
|
// In apps this might happen for several reasons:
|
||||||
|
// 1. Process doesn't run long enough for Glean to be initialized.
|
||||||
|
// 2. Getting some early data used for initialize fails
|
||||||
|
|
||||||
|
pings::validation.submit(None);
|
||||||
|
|
||||||
|
// We can't test for data either, as that would panic because init was never called.
|
||||||
|
|
||||||
|
glean::shutdown();
|
||||||
|
}
|
|
@ -0,0 +1,81 @@
|
||||||
|
// 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/.
|
||||||
|
|
||||||
|
//! This integration test should model how the RLB is used when embedded in another Rust application
|
||||||
|
//! (e.g. FOG/Firefox Desktop).
|
||||||
|
//!
|
||||||
|
//! We write a single test scenario per file to avoid any state keeping across runs
|
||||||
|
//! (different files run as different processes).
|
||||||
|
|
||||||
|
mod common;
|
||||||
|
|
||||||
|
use glean::Configuration;
|
||||||
|
|
||||||
|
/// Some user metrics.
|
||||||
|
mod metrics {
|
||||||
|
use glean::private::*;
|
||||||
|
use glean::{Lifetime, TimeUnit};
|
||||||
|
use glean_core::CommonMetricData;
|
||||||
|
use once_cell::sync::Lazy;
|
||||||
|
|
||||||
|
#[allow(non_upper_case_globals)]
|
||||||
|
pub static initialization: Lazy<TimespanMetric> = Lazy::new(|| {
|
||||||
|
TimespanMetric::new(
|
||||||
|
CommonMetricData {
|
||||||
|
name: "initialization".into(),
|
||||||
|
category: "sample".into(),
|
||||||
|
send_in_pings: vec!["validation".into()],
|
||||||
|
lifetime: Lifetime::Ping,
|
||||||
|
disabled: false,
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
TimeUnit::Nanosecond,
|
||||||
|
)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
mod pings {
|
||||||
|
use glean::private::PingType;
|
||||||
|
use once_cell::sync::Lazy;
|
||||||
|
|
||||||
|
#[allow(non_upper_case_globals)]
|
||||||
|
pub static validation: Lazy<PingType> =
|
||||||
|
Lazy::new(|| glean::private::PingType::new("validation", true, true, vec![]));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Test scenario: Glean initialization fails.
|
||||||
|
///
|
||||||
|
/// The app tries to initializate Glean, but that somehow fails.
|
||||||
|
#[test]
|
||||||
|
fn init_fails() {
|
||||||
|
common::enable_test_logging();
|
||||||
|
|
||||||
|
metrics::initialization.start();
|
||||||
|
|
||||||
|
// Create a custom configuration to use a validating uploader.
|
||||||
|
let dir = tempfile::tempdir().unwrap();
|
||||||
|
let tmpname = dir.path().display().to_string();
|
||||||
|
|
||||||
|
let cfg = Configuration {
|
||||||
|
data_path: tmpname,
|
||||||
|
application_id: "firefox-desktop".into(), // An empty application ID is invalid.
|
||||||
|
upload_enabled: true,
|
||||||
|
max_events: None,
|
||||||
|
delay_ping_lifetime_io: false,
|
||||||
|
channel: Some("testing".into()),
|
||||||
|
server_endpoint: Some("invalid-test-host".into()),
|
||||||
|
uploader: None,
|
||||||
|
};
|
||||||
|
common::initialize(cfg);
|
||||||
|
|
||||||
|
metrics::initialization.stop();
|
||||||
|
|
||||||
|
pings::validation.submit(None);
|
||||||
|
|
||||||
|
// We don't test for data here, as that would block on the dispatcher.
|
||||||
|
|
||||||
|
// Shut it down immediately; this might not be enough time to initialize.
|
||||||
|
|
||||||
|
glean::shutdown();
|
||||||
|
}
|
|
@ -0,0 +1,84 @@
|
||||||
|
// 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/.
|
||||||
|
|
||||||
|
//! This integration test should model how the RLB is used when embedded in another Rust application
|
||||||
|
//! (e.g. FOG/Firefox Desktop).
|
||||||
|
//!
|
||||||
|
//! We write a single test scenario per file to avoid any state keeping across runs
|
||||||
|
//! (different files run as different processes).
|
||||||
|
|
||||||
|
mod common;
|
||||||
|
|
||||||
|
use glean::Configuration;
|
||||||
|
|
||||||
|
/// Some user metrics.
|
||||||
|
mod metrics {
|
||||||
|
use glean::private::*;
|
||||||
|
use glean::{Lifetime, TimeUnit};
|
||||||
|
use glean_core::CommonMetricData;
|
||||||
|
use once_cell::sync::Lazy;
|
||||||
|
|
||||||
|
#[allow(non_upper_case_globals)]
|
||||||
|
pub static initialization: Lazy<TimespanMetric> = Lazy::new(|| {
|
||||||
|
TimespanMetric::new(
|
||||||
|
CommonMetricData {
|
||||||
|
name: "initialization".into(),
|
||||||
|
category: "sample".into(),
|
||||||
|
send_in_pings: vec!["validation".into()],
|
||||||
|
lifetime: Lifetime::Ping,
|
||||||
|
disabled: false,
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
TimeUnit::Nanosecond,
|
||||||
|
)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
mod pings {
|
||||||
|
use glean::private::PingType;
|
||||||
|
use once_cell::sync::Lazy;
|
||||||
|
|
||||||
|
#[allow(non_upper_case_globals)]
|
||||||
|
pub static validation: Lazy<PingType> =
|
||||||
|
Lazy::new(|| glean::private::PingType::new("validation", true, true, vec![]));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Test scenario: A clean run
|
||||||
|
///
|
||||||
|
/// The app is initialized, in turn Glean gets initialized without problems.
|
||||||
|
/// Some data is recorded (before and after initialization).
|
||||||
|
/// And later the whole process is shutdown.
|
||||||
|
#[test]
|
||||||
|
fn simple_lifecycle() {
|
||||||
|
common::enable_test_logging();
|
||||||
|
|
||||||
|
metrics::initialization.start();
|
||||||
|
|
||||||
|
// Create a custom configuration to use a validating uploader.
|
||||||
|
let dir = tempfile::tempdir().unwrap();
|
||||||
|
let tmpname = dir.path().display().to_string();
|
||||||
|
|
||||||
|
let cfg = Configuration {
|
||||||
|
data_path: tmpname,
|
||||||
|
application_id: "firefox-desktop".into(),
|
||||||
|
upload_enabled: true,
|
||||||
|
max_events: None,
|
||||||
|
delay_ping_lifetime_io: false,
|
||||||
|
channel: Some("testing".into()),
|
||||||
|
server_endpoint: Some("invalid-test-host".into()),
|
||||||
|
uploader: None,
|
||||||
|
};
|
||||||
|
common::initialize(cfg);
|
||||||
|
|
||||||
|
metrics::initialization.stop();
|
||||||
|
|
||||||
|
// This would never be called outside of tests,
|
||||||
|
// but it's the only way we can really test it's working right now.
|
||||||
|
assert!(metrics::initialization.test_get_value(None).is_some());
|
||||||
|
|
||||||
|
pings::validation.submit(None);
|
||||||
|
assert!(metrics::initialization.test_get_value(None).is_none());
|
||||||
|
|
||||||
|
glean::shutdown();
|
||||||
|
}
|
|
@ -6,8 +6,8 @@ edition = "2018"
|
||||||
license = "MPL-2.0"
|
license = "MPL-2.0"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
glean = "33.8.0"
|
glean = "33.9.1"
|
||||||
glean-core = { version = "33.8.0", features = ["rkv-safe-mode"] }
|
glean-core = { version = "33.9.1", features = ["rkv-safe-mode"] }
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
nserror = { path = "../../../xpcom/rust/nserror" }
|
nserror = { path = "../../../xpcom/rust/nserror" }
|
||||||
nsstring = { path = "../../../xpcom/rust/nsstring" }
|
nsstring = { path = "../../../xpcom/rust/nsstring" }
|
||||||
|
|
|
@ -8,8 +8,8 @@ publish = false
|
||||||
[dependencies]
|
[dependencies]
|
||||||
bincode = "1.0"
|
bincode = "1.0"
|
||||||
chrono = "0.4.10"
|
chrono = "0.4.10"
|
||||||
glean = "33.8.0"
|
glean = "33.9.1"
|
||||||
glean-core = { version = "33.8.0", features = ["rkv-safe-mode"] }
|
glean-core = { version = "33.9.1", features = ["rkv-safe-mode"] }
|
||||||
inherent = "0.1.4"
|
inherent = "0.1.4"
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
nsstring = { path = "../../../../xpcom/rust/nsstring", optional = true }
|
nsstring = { path = "../../../../xpcom/rust/nsstring", optional = true }
|
||||||
|
|
Загрузка…
Ссылка в новой задаче