diff options
Diffstat (limited to 'third_party/rust/glean-core/src')
-rw-r--r-- | third_party/rust/glean-core/src/common_metric_data.rs | 5 | ||||
-rw-r--r-- | third_party/rust/glean-core/src/core/mod.rs | 29 | ||||
-rw-r--r-- | third_party/rust/glean-core/src/database/mod.rs | 65 | ||||
-rw-r--r-- | third_party/rust/glean-core/src/glean.udl | 22 | ||||
-rw-r--r-- | third_party/rust/glean-core/src/internal_pings.rs | 17 | ||||
-rw-r--r-- | third_party/rust/glean-core/src/lib.rs | 42 | ||||
-rw-r--r-- | third_party/rust/glean-core/src/lib_unit_tests.rs | 118 | ||||
-rw-r--r-- | third_party/rust/glean-core/src/metrics/mod.rs | 21 | ||||
-rw-r--r-- | third_party/rust/glean-core/src/metrics/object.rs | 29 | ||||
-rw-r--r-- | third_party/rust/glean-core/src/metrics/ping.rs | 74 | ||||
-rw-r--r-- | third_party/rust/glean-core/src/metrics/remote_settings_config.rs (renamed from third_party/rust/glean-core/src/metrics/metrics_enabled_config.rs) | 16 | ||||
-rw-r--r-- | third_party/rust/glean-core/src/ping/mod.rs | 3 | ||||
-rw-r--r-- | third_party/rust/glean-core/src/upload/directory.rs | 6 | ||||
-rw-r--r-- | third_party/rust/glean-core/src/upload/mod.rs | 24 |
14 files changed, 380 insertions, 91 deletions
diff --git a/third_party/rust/glean-core/src/common_metric_data.rs b/third_party/rust/glean-core/src/common_metric_data.rs index 9bda9bb462..f5058d995f 100644 --- a/third_party/rust/glean-core/src/common_metric_data.rs +++ b/third_party/rust/glean-core/src/common_metric_data.rs @@ -90,9 +90,10 @@ impl Clone for CommonMetricDataInternal { impl From<CommonMetricData> for CommonMetricDataInternal { fn from(input_data: CommonMetricData) -> Self { + let disabled = input_data.disabled; Self { - inner: input_data.clone(), - disabled: AtomicU8::new(u8::from(input_data.disabled)), + inner: input_data, + disabled: AtomicU8::new(u8::from(disabled)), } } } diff --git a/third_party/rust/glean-core/src/core/mod.rs b/third_party/rust/glean-core/src/core/mod.rs index f69f0c3868..9b8cfe531b 100644 --- a/third_party/rust/glean-core/src/core/mod.rs +++ b/third_party/rust/glean-core/src/core/mod.rs @@ -16,7 +16,7 @@ use crate::event_database::EventDatabase; use crate::internal_metrics::{AdditionalMetrics, CoreMetrics, DatabaseMetrics}; use crate::internal_pings::InternalPings; use crate::metrics::{ - self, ExperimentMetric, Metric, MetricType, MetricsEnabledConfig, PingType, RecordedExperiment, + self, ExperimentMetric, Metric, MetricType, PingType, RecordedExperiment, RemoteSettingsConfig, }; use crate::ping::PingMaker; use crate::storage::{StorageManager, INTERNAL_STORAGE}; @@ -123,7 +123,7 @@ where /// enable_internal_pings: true, /// }; /// let mut glean = Glean::new(cfg).unwrap(); -/// let ping = PingType::new("sample", true, false, true, true, vec![]); +/// let ping = PingType::new("sample", true, false, true, true, true, vec![], vec![]); /// glean.register_ping_type(&ping); /// /// let call_counter: CounterMetric = CounterMetric::new(CommonMetricData { @@ -162,7 +162,7 @@ pub struct Glean { pub(crate) app_build: String, pub(crate) schedule_metrics_pings: bool, pub(crate) remote_settings_epoch: AtomicU8, - pub(crate) remote_settings_metrics_config: Arc<Mutex<MetricsEnabledConfig>>, + pub(crate) remote_settings_config: Arc<Mutex<RemoteSettingsConfig>>, pub(crate) with_timestamps: bool, } @@ -222,7 +222,7 @@ impl Glean { // Subprocess doesn't use "metrics" pings so has no need for a scheduler. schedule_metrics_pings: false, remote_settings_epoch: AtomicU8::new(0), - remote_settings_metrics_config: Arc::new(Mutex::new(MetricsEnabledConfig::new())), + remote_settings_config: Arc::new(Mutex::new(RemoteSettingsConfig::new())), with_timestamps: cfg.enable_event_timestamps, }; @@ -758,19 +758,26 @@ impl Glean { .get_value(self, None) } - /// Set configuration to override the default metric enabled/disabled state, typically from a + /// Set configuration to override the default state, typically initiated from a /// remote_settings experiment or rollout /// /// # Arguments /// - /// * `json` - The stringified JSON representation of a `MetricsEnabledConfig` object - pub fn set_metrics_enabled_config(&self, cfg: MetricsEnabledConfig) { - // Set the current MetricsEnabledConfig, keeping the lock until the epoch is + /// * `cfg` - The stringified JSON representation of a `RemoteSettingsConfig` object + pub fn apply_server_knobs_config(&self, cfg: RemoteSettingsConfig) { + // Set the current RemoteSettingsConfig, keeping the lock until the epoch is // updated to prevent against reading a "new" config but an "old" epoch - let mut metric_config = self.remote_settings_metrics_config.lock().unwrap(); + let mut remote_settings_config = self.remote_settings_config.lock().unwrap(); - // Merge the exising configuration with the supplied one - metric_config.metrics_enabled.extend(cfg.metrics_enabled); + // Merge the exising metrics configuration with the supplied one + remote_settings_config + .metrics_enabled + .extend(cfg.metrics_enabled); + + // Merge the exising ping configuration with the supplied one + remote_settings_config + .pings_enabled + .extend(cfg.pings_enabled); // Update remote_settings epoch self.remote_settings_epoch.fetch_add(1, Ordering::SeqCst); diff --git a/third_party/rust/glean-core/src/database/mod.rs b/third_party/rust/glean-core/src/database/mod.rs index 0dbf0220bc..75a068b42e 100644 --- a/third_party/rust/glean-core/src/database/mod.rs +++ b/third_party/rust/glean-core/src/database/mod.rs @@ -9,6 +9,8 @@ use std::io; use std::num::NonZeroU64; use std::path::Path; use std::str; +#[cfg(target_os = "android")] +use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::RwLock; use crate::ErrorKind; @@ -167,6 +169,13 @@ use crate::Glean; use crate::Lifetime; use crate::Result; +/// The number of writes we accept writes to the ping-lifetime in-memory map +/// before data is flushed to disk. +/// +/// Only considered if `delay_ping_lifetime_io` is set to `true`. +#[cfg(target_os = "android")] +const PING_LIFETIME_THRESHOLD: usize = 1000; + pub struct Database { /// Handle to the database environment. rkv: Rkv, @@ -184,6 +193,14 @@ pub struct Database { /// so as to persist them to disk using rkv in bulk on demand. ping_lifetime_data: Option<RwLock<BTreeMap<String, Metric>>>, + /// A count of how many database writes have been done since the last ping-lifetime flush. + /// + /// A ping-lifetime flush is automatically done after `PING_LIFETIME_THRESHOLD` writes. + /// + /// Only relevant if `delay_ping_lifetime_io` is set to `true`, + #[cfg(target_os = "android")] + ping_lifetime_count: AtomicUsize, + /// Initial file size when opening the database. file_size: Option<NonZeroU64>, @@ -263,6 +280,8 @@ impl Database { ping_store, application_store, ping_lifetime_data, + #[cfg(target_os = "android")] + ping_lifetime_count: AtomicUsize::new(0), file_size, rkv_load_state, }; @@ -528,6 +547,9 @@ impl Database { .write() .expect("Can't read ping lifetime data"); data.insert(final_key, metric.clone()); + + // flush ping lifetime + self.persist_ping_lifetime_data_if_full(&data)?; return Ok(()); } } @@ -609,6 +631,9 @@ impl Database { entry.insert(transform(Some(old_value))); } } + + // flush ping lifetime + self.persist_ping_lifetime_data_if_full(&data)?; return Ok(()); } } @@ -802,6 +827,10 @@ impl Database { .read() .expect("Can't read ping lifetime data"); + // We can reset the write-counter. Current data has been persisted. + #[cfg(target_os = "android")] + self.ping_lifetime_count.store(0, Ordering::Release); + self.write_with_store(Lifetime::Ping, |mut writer, store| { for (key, value) in data.iter() { let encoded = @@ -817,6 +846,42 @@ impl Database { } Ok(()) } + + pub fn persist_ping_lifetime_data_if_full( + &self, + data: &BTreeMap<String, Metric>, + ) -> Result<()> { + #[cfg(target_os = "android")] + { + self.ping_lifetime_count.fetch_add(1, Ordering::Release); + + let write_count = self.ping_lifetime_count.load(Ordering::Relaxed); + if write_count < PING_LIFETIME_THRESHOLD { + return Ok(()); + } + + self.ping_lifetime_count.store(0, Ordering::Release); + let write_result = self.write_with_store(Lifetime::Ping, |mut writer, store| { + for (key, value) in data.iter() { + let encoded = + bincode::serialize(&value).expect("IMPOSSIBLE: Serializing metric failed"); + // There is no need for `get_storage_key` here because + // the key is already formatted from when it was saved + // to ping_lifetime_data. + store.put(&mut writer, key, &rkv::Value::Blob(&encoded))?; + } + writer.commit()?; + Ok(()) + }); + + return write_result; + } + #[cfg(not(target_os = "android"))] + { + _ = data; // suppress unused_variables warning. + Ok(()) + } + } } #[cfg(test)] diff --git a/third_party/rust/glean-core/src/glean.udl b/third_party/rust/glean-core/src/glean.udl index dc71fea594..b107a36f28 100644 --- a/third_party/rust/glean-core/src/glean.udl +++ b/third_party/rust/glean-core/src/glean.udl @@ -47,12 +47,14 @@ namespace glean { void glean_unregister_event_listener(string tag); // Server Knobs API - void glean_set_metrics_enabled_config(string json); + void glean_apply_server_knobs_config(string json); boolean glean_set_debug_view_tag(string tag); boolean glean_set_source_tags(sequence<string> tags); void glean_set_log_pings(boolean value); + void glean_persist_ping_lifetime_data(); + void glean_handle_client_active(); void glean_handle_client_inactive(); @@ -292,7 +294,7 @@ enum ErrorType { }; interface PingType { - constructor(string name, boolean include_client_id, boolean send_if_empty, boolean precise_timestamps, boolean include_info_sections, sequence<string> reason_codes); + constructor(string name, boolean include_client_id, boolean send_if_empty, boolean precise_timestamps, boolean include_info_sections, boolean enabled, sequence<string> schedules_pings, sequence<string> reason_codes); void submit(optional string? reason = null); }; @@ -640,3 +642,19 @@ interface TextMetric { i32 test_get_num_recorded_errors(ErrorType error); }; + +// JSON data encoded into a string +[Custom] +typedef string JsonValue; + +interface ObjectMetric { + constructor(CommonMetricData meta); + + void set_string(string object); + + JsonValue? test_get_value(optional string? ping_name = null); + + i32 test_get_num_recorded_errors(ErrorType error); + + void record_schema_error(); +}; diff --git a/third_party/rust/glean-core/src/internal_pings.rs b/third_party/rust/glean-core/src/internal_pings.rs index 1cf32feb60..4fe15352b2 100644 --- a/third_party/rust/glean-core/src/internal_pings.rs +++ b/third_party/rust/glean-core/src/internal_pings.rs @@ -21,25 +21,28 @@ pub struct InternalPings { impl InternalPings { pub fn new(enabled: bool) -> InternalPings { InternalPings { - baseline: PingType::new_internal( + baseline: PingType::new( "baseline", true, true, true, true, + enabled, + vec![], vec![ "active".to_string(), "dirty_startup".to_string(), "inactive".to_string(), ], - enabled, ), - metrics: PingType::new_internal( + metrics: PingType::new( "metrics", true, false, true, true, + enabled, + vec![], vec![ "overdue".to_string(), "reschedule".to_string(), @@ -47,20 +50,20 @@ impl InternalPings { "tomorrow".to_string(), "upgrade".to_string(), ], - enabled, ), - events: PingType::new_internal( + events: PingType::new( "events", true, false, true, true, + enabled, + vec![], vec![ "startup".to_string(), "inactive".to_string(), "max_capacity".to_string(), ], - enabled, ), deletion_request: PingType::new( "deletion-request", @@ -68,6 +71,8 @@ impl InternalPings { true, true, true, + true, // The deletion-request should not be disabled + vec![], vec!["at_init".to_string(), "set_upload_enabled".to_string()], ), } diff --git a/third_party/rust/glean-core/src/lib.rs b/third_party/rust/glean-core/src/lib.rs index af68fde264..c79fa1e226 100644 --- a/third_party/rust/glean-core/src/lib.rs +++ b/third_party/rust/glean-core/src/lib.rs @@ -28,7 +28,7 @@ use log::LevelFilter; use once_cell::sync::{Lazy, OnceCell}; use uuid::Uuid; -use metrics::MetricsEnabledConfig; +use metrics::RemoteSettingsConfig; mod common_metric_data; mod core; @@ -68,9 +68,9 @@ pub use crate::metrics::labeled::{ pub use crate::metrics::{ BooleanMetric, CounterMetric, CustomDistributionMetric, Datetime, DatetimeMetric, DenominatorMetric, DistributionData, EventMetric, MemoryDistributionMetric, MemoryUnit, - NumeratorMetric, PingType, QuantityMetric, Rate, RateMetric, RecordedEvent, RecordedExperiment, - StringListMetric, StringMetric, TextMetric, TimeUnit, TimerId, TimespanMetric, - TimingDistributionMetric, UrlMetric, UuidMetric, + NumeratorMetric, ObjectMetric, PingType, QuantityMetric, Rate, RateMetric, RecordedEvent, + RecordedExperiment, StringListMetric, StringMetric, TextMetric, TimeUnit, TimerId, + TimespanMetric, TimingDistributionMetric, UrlMetric, UuidMetric, }; pub use crate::upload::{PingRequest, PingUploadTask, UploadResult, UploadTaskAction}; @@ -693,7 +693,7 @@ pub fn shutdown() { /// Only has effect when Glean is configured with `delay_ping_lifetime_io: true`. /// If Glean hasn't been initialized this will dispatch and return Ok(()), /// otherwise it will block until the persist is done and return its Result. -pub fn persist_ping_lifetime_data() { +pub fn glean_persist_ping_lifetime_data() { // This is async, we can't get the Error back to the caller. crate::launch_with_glean(|glean| { let _ = glean.persist_ping_lifetime_data(); @@ -910,17 +910,17 @@ pub fn glean_test_get_experimentation_id() -> Option<String> { /// Sets a remote configuration to override metrics' default enabled/disabled /// state /// -/// See [`core::Glean::set_metrics_enabled_config`]. -pub fn glean_set_metrics_enabled_config(json: String) { +/// See [`core::Glean::apply_server_knobs_config`]. +pub fn glean_apply_server_knobs_config(json: String) { // An empty config means it is not set, // so we avoid logging an error about it. if json.is_empty() { return; } - match MetricsEnabledConfig::try_from(json) { + match RemoteSettingsConfig::try_from(json) { Ok(cfg) => launch_with_glean(|glean| { - glean.set_metrics_enabled_config(cfg); + glean.apply_server_knobs_config(cfg); }), Err(e) => { log::error!("Error setting metrics feature config: {:?}", e); @@ -1125,8 +1125,14 @@ pub fn glean_test_destroy_glean(clear_stores: bool, data_path: Option<String>) { // Only useful if Glean initialization finished successfully // and set up the storage. - let has_storage = - core::with_opt_glean(|glean| glean.storage_opt().is_some()).unwrap_or(false); + let has_storage = core::with_opt_glean(|glean| { + // We need to flush the ping lifetime data before a full shutdown. + glean + .storage_opt() + .map(|storage| storage.persist_ping_lifetime_data()) + .is_some() + }) + .unwrap_or(false); if has_storage { uploader_shutdown(); } @@ -1229,6 +1235,20 @@ mod ffi { obj.into_owned() } } + + type JsonValue = serde_json::Value; + + impl UniffiCustomTypeConverter for JsonValue { + type Builtin = String; + + fn into_custom(val: Self::Builtin) -> uniffi::Result<Self> { + Ok(serde_json::from_str(&val)?) + } + + fn from_custom(obj: Self) -> Self::Builtin { + serde_json::to_string(&obj).unwrap() + } + } } pub use ffi::*; diff --git a/third_party/rust/glean-core/src/lib_unit_tests.rs b/third_party/rust/glean-core/src/lib_unit_tests.rs index 14d3b98417..b74397317f 100644 --- a/third_party/rust/glean-core/src/lib_unit_tests.rs +++ b/third_party/rust/glean-core/src/lib_unit_tests.rs @@ -890,16 +890,17 @@ fn test_set_remote_metric_configuration() { ); // 2. Set a configuration to disable the metrics - let mut metrics_enabled_config = json!( + let mut remote_settings_config = json!( { - "category.string_metric": false, - "category.labeled_string_metric": false, + "metrics_enabled": { + "category.string_metric": false, + "category.labeled_string_metric": false, + } } ) .to_string(); - glean.set_metrics_enabled_config( - MetricsEnabledConfig::try_from(metrics_enabled_config).unwrap(), - ); + glean + .apply_server_knobs_config(RemoteSettingsConfig::try_from(remote_settings_config).unwrap()); // 3. Since the metrics were disabled, setting a new value will be ignored metric.set_sync(&glean, "VALUE_AFTER_DISABLED"); @@ -921,15 +922,16 @@ fn test_set_remote_metric_configuration() { ); // 4. Set a new configuration where one metric is enabled - metrics_enabled_config = json!( + remote_settings_config = json!( { - "category.string_metric": true, + "metrics_enabled": { + "category.string_metric": true, + } } ) .to_string(); - glean.set_metrics_enabled_config( - MetricsEnabledConfig::try_from(metrics_enabled_config).unwrap(), - ); + glean + .apply_server_knobs_config(RemoteSettingsConfig::try_from(remote_settings_config).unwrap()); // 5. Since the first metric is enabled, setting a new value should work // on it but not the second metric @@ -954,15 +956,16 @@ fn test_set_remote_metric_configuration() { // 6. Set a new configuration where the second metric is enabled. This // should be merged with the existing configuration and then both // metrics should be enabled at that point. - metrics_enabled_config = json!( + remote_settings_config = json!( { - "category.labeled_string_metric": true, + "metrics_enabled": { + "category.labeled_string_metric": true, + } } ) .to_string(); - glean.set_metrics_enabled_config( - MetricsEnabledConfig::try_from(metrics_enabled_config).unwrap(), - ); + glean + .apply_server_knobs_config(RemoteSettingsConfig::try_from(remote_settings_config).unwrap()); // 7. Now both metrics are enabled, setting a new value should work for // both metrics with the merged configurations @@ -992,15 +995,16 @@ fn test_remote_settings_epoch() { assert_eq!(0u8, current_epoch, "Current epoch must start at 0"); // 2. Set a configuration which will trigger incrementing the epoch - let metrics_enabled_config = json!( + let remote_settings_config = json!( { - "category.string_metric": false + "metrics_enabled": { + "category.string_metric": false + } } ) .to_string(); - glean.set_metrics_enabled_config( - MetricsEnabledConfig::try_from(metrics_enabled_config).unwrap(), - ); + glean + .apply_server_knobs_config(RemoteSettingsConfig::try_from(remote_settings_config).unwrap()); // 3. Ensure the epoch updated current_epoch = glean.remote_settings_epoch.load(Ordering::Acquire); @@ -1026,15 +1030,16 @@ fn test_remote_settings_epoch_updates_in_metric() { ); // 2. Set a configuration to disable the `category.string_metric` - let metrics_enabled_config = json!( + let remote_settings_config = json!( { - "category.string_metric": false + "metrics_enabled": { + "category.string_metric": false + } } ) .to_string(); - glean.set_metrics_enabled_config( - MetricsEnabledConfig::try_from(metrics_enabled_config).unwrap(), - ); + glean + .apply_server_knobs_config(RemoteSettingsConfig::try_from(remote_settings_config).unwrap()); // 3. Ensure the epoch was updated let current_epoch = glean.remote_settings_epoch.load(Ordering::Acquire); @@ -1168,7 +1173,16 @@ fn disabled_pings_are_not_submitted() { let dir = tempfile::tempdir().unwrap(); let (mut glean, _t) = new_glean(Some(dir)); - let ping = PingType::new_internal("custom-disabled", true, false, true, true, vec![], false); + let ping = PingType::new_internal( + "custom-disabled", + true, + false, + true, + true, + false, + vec![], + vec![], + ); glean.register_ping_type(&ping); // We need to store a metric as an empty ping is not stored. @@ -1203,3 +1217,53 @@ fn internal_pings_can_be_disabled() { let submitted = glean.internal_pings.baseline.submit_sync(&glean, None); assert!(!submitted); } + +#[test] +fn pings_are_controllable_from_remote_settings_config() { + let _ = env_logger::builder().is_test(true).try_init(); + + let dir = tempfile::tempdir().unwrap(); + let (mut glean, _t) = new_glean(Some(dir)); + + let disabled_ping = PingType::new( + "custom-disabled", + true, + true, + true, + true, + false, + vec![], + vec![], + ); + glean.register_ping_type(&disabled_ping); + let enabled_ping = PingType::new( + "custom-enabled", + true, + true, + true, + true, + true, + vec![], + vec![], + ); + glean.register_ping_type(&enabled_ping); + + assert!(!disabled_ping.submit_sync(&glean, None)); + assert!(enabled_ping.submit_sync(&glean, None)); + + // Now, create a configuration to switch the enabled state of the two pings + let remote_settings_config = json!( + { + "pings_enabled": { + "custom-disabled": true, + "custom-enabled": false + } + } + ) + .to_string(); + glean + .apply_server_knobs_config(RemoteSettingsConfig::try_from(remote_settings_config).unwrap()); + + assert!(disabled_ping.submit_sync(&glean, None)); + assert!(!enabled_ping.submit_sync(&glean, None)); +} diff --git a/third_party/rust/glean-core/src/metrics/mod.rs b/third_party/rust/glean-core/src/metrics/mod.rs index 92001efd2a..9234fff2d1 100644 --- a/third_party/rust/glean-core/src/metrics/mod.rs +++ b/third_party/rust/glean-core/src/metrics/mod.rs @@ -22,13 +22,13 @@ mod experiment; pub(crate) mod labeled; mod memory_distribution; mod memory_unit; -mod metrics_enabled_config; mod numerator; mod object; mod ping; mod quantity; mod rate; mod recorded_experiment; +mod remote_settings_config; mod string; mod string_list; mod text; @@ -72,7 +72,7 @@ pub use self::uuid::UuidMetric; pub use crate::histogram::HistogramType; pub use recorded_experiment::RecordedExperiment; -pub use self::metrics_enabled_config::MetricsEnabledConfig; +pub use self::remote_settings_config::RemoteSettingsConfig; /// A snapshot of all buckets and the accumulated sum of a distribution. // @@ -180,7 +180,7 @@ pub trait MetricType { // Technically nothing prevents multiple calls to should_record() to run in parallel, // meaning both are reading self.meta().disabled and later writing it. In between it can - // also read remote_settings_metrics_config, which also could be modified in between those 2 reads. + // also read remote_settings_config, which also could be modified in between those 2 reads. // This means we could write the wrong remote_settings_epoch | current_disabled value. All in all // at worst we would see that metric enabled/disabled wrongly once. // But since everything is tunneled through the dispatcher, this should never ever happen. @@ -200,11 +200,7 @@ pub trait MetricType { } // The epoch's didn't match so we need to look up the disabled flag // by the base_identifier from the in-memory HashMap - let metrics_enabled = &glean - .remote_settings_metrics_config - .lock() - .unwrap() - .metrics_enabled; + let remote_settings_config = &glean.remote_settings_config.lock().unwrap(); // Get the value from the remote configuration if it is there, otherwise return the default value. let current_disabled = { let base_id = self.meta().base_identifier(); @@ -215,8 +211,13 @@ pub trait MetricType { // NOTE: The `!` preceding the `*is_enabled` is important for inverting the logic since the // underlying property in the metrics.yaml is `disabled` and the outward API is treating it as // if it were `enabled` to make it easier to understand. - if let Some(is_enabled) = metrics_enabled.get(identifier) { - u8::from(!*is_enabled) + + if !remote_settings_config.metrics_enabled.is_empty() { + if let Some(is_enabled) = remote_settings_config.metrics_enabled.get(identifier) { + u8::from(!*is_enabled) + } else { + u8::from(self.meta().inner.disabled) + } } else { u8::from(self.meta().inner.disabled) } diff --git a/third_party/rust/glean-core/src/metrics/object.rs b/third_party/rust/glean-core/src/metrics/object.rs index 6071e2b33a..58c8a04c73 100644 --- a/third_party/rust/glean-core/src/metrics/object.rs +++ b/third_party/rust/glean-core/src/metrics/object.rs @@ -48,6 +48,10 @@ impl ObjectMetric { /// * `value` - the value to set. #[doc(hidden)] pub fn set_sync(&self, glean: &Glean, value: JsonValue) { + if !self.should_record(glean) { + return; + } + let value = Metric::Object(serde_json::to_string(&value).unwrap()); glean.storage().record(glean, &self.meta, &value) } @@ -65,6 +69,31 @@ impl ObjectMetric { crate::launch_with_glean(move |glean| metric.set_sync(glean, value)) } + /// Sets to the specified structure. + /// + /// Parses the passed JSON string. + /// If it can't be parsed into a valid object it records an invalid value error. + /// + /// Note: This does not check the structure. This needs to be done by the wrapper. + /// + /// # Arguments + /// + /// * `object` - JSON representation of the object to set. + pub fn set_string(&self, object: String) { + let metric = self.clone(); + crate::launch_with_glean(move |glean| { + let object = match serde_json::from_str(&object) { + Ok(object) => object, + Err(_) => { + let msg = "Value did not match predefined schema"; + record_error(glean, &metric.meta, ErrorType::InvalidValue, msg, None); + return; + } + }; + metric.set_sync(glean, object) + }) + } + /// Record an `InvalidValue` error for this metric. /// /// Only to be used by the RLB. diff --git a/third_party/rust/glean-core/src/metrics/ping.rs b/third_party/rust/glean-core/src/metrics/ping.rs index 5defab7a71..1c5b93c165 100644 --- a/third_party/rust/glean-core/src/metrics/ping.rs +++ b/third_party/rust/glean-core/src/metrics/ping.rs @@ -29,13 +29,12 @@ struct InnerPing { pub precise_timestamps: bool, /// Whether to include the {client|ping}_info sections on assembly. pub include_info_sections: bool, + /// Whether this ping is enabled. + pub enabled: bool, + /// Other pings that should be scheduled when this ping is sent. + pub schedules_pings: Vec<String>, /// The "reason" codes that this ping can send pub reason_codes: Vec<String>, - - /// Whether this ping is enabled. - /// Note: Data for disabled pings is still recorded. - /// It will not be cleared out on submit. - enabled: bool, } impl fmt::Debug for PingType { @@ -46,6 +45,8 @@ impl fmt::Debug for PingType { .field("send_if_empty", &self.0.send_if_empty) .field("precise_timestamps", &self.0.precise_timestamps) .field("include_info_sections", &self.0.include_info_sections) + .field("enabled", &self.0.enabled) + .field("schedules_pings", &self.0.schedules_pings) .field("reason_codes", &self.0.reason_codes) .finish() } @@ -64,13 +65,20 @@ impl PingType { /// * `name` - The name of the ping. /// * `include_client_id` - Whether to include the client ID in the assembled ping when submitting. /// * `send_if_empty` - Whether the ping should be sent empty or not. + /// * `precise_timestamps` - Whether the ping should use precise timestamps for the start and end time. + /// * `include_info_sections` - Whether the ping should include the client/ping_info sections. + /// * `enabled` - Whether or not this ping is enabled. Note: Data that would be sent on a disabled + /// ping will still be collected but is discarded rather than being submitted. /// * `reason_codes` - The valid reason codes for this ping. + #[allow(clippy::too_many_arguments)] pub fn new<A: Into<String>>( name: A, include_client_id: bool, send_if_empty: bool, precise_timestamps: bool, include_info_sections: bool, + enabled: bool, + schedules_pings: Vec<String>, reason_codes: Vec<String>, ) -> Self { Self::new_internal( @@ -79,19 +87,22 @@ impl PingType { send_if_empty, precise_timestamps, include_info_sections, + enabled, + schedules_pings, reason_codes, - true, ) } + #[allow(clippy::too_many_arguments)] pub(crate) fn new_internal<A: Into<String>>( name: A, include_client_id: bool, send_if_empty: bool, precise_timestamps: bool, include_info_sections: bool, - reason_codes: Vec<String>, enabled: bool, + schedules_pings: Vec<String>, + reason_codes: Vec<String>, ) -> Self { let this = Self(Arc::new(InnerPing { name: name.into(), @@ -99,8 +110,9 @@ impl PingType { send_if_empty, precise_timestamps, include_info_sections, - reason_codes, enabled, + schedules_pings, + reason_codes, })); // Register this ping. @@ -130,6 +142,22 @@ impl PingType { self.0.include_info_sections } + pub(crate) fn enabled(&self, glean: &Glean) -> bool { + let remote_settings_config = &glean.remote_settings_config.lock().unwrap(); + + if !remote_settings_config.pings_enabled.is_empty() { + if let Some(remote_enabled) = remote_settings_config.pings_enabled.get(self.name()) { + return *remote_enabled; + } + } + + self.0.enabled + } + + pub(crate) fn schedules_pings(&self) -> &[String] { + &self.0.schedules_pings + } + /// Submits the ping for eventual uploading. /// /// The ping content is assembled as soon as possible, but upload is not @@ -166,11 +194,6 @@ impl PingType { /// Whether the ping was succesfully assembled and queued. #[doc(hidden)] pub fn submit_sync(&self, glean: &Glean, reason: Option<&str>) -> bool { - if !self.0.enabled { - log::info!("Ping disabled: not submitting '{}' ping.", self.0.name); - return false; - } - if !glean.is_upload_enabled() { log::info!("Glean disabled: not submitting any pings."); return false; @@ -208,6 +231,15 @@ impl PingType { false } Some(ping) => { + if !self.enabled(glean) { + log::info!( + "The ping '{}' is disabled and will be discarded and not submitted", + ping.name + ); + + return false; + } + // This metric is recorded *after* the ping is collected (since // that is the only way to know *if* it will be submitted). The // implication of this is that the count for a metrics ping will @@ -219,7 +251,10 @@ impl PingType { .add_sync(glean, 1); if let Err(e) = ping_maker.store_ping(glean.get_data_path(), &ping) { - log::warn!("IO error while writing ping to file: {}. Enqueuing upload of what we have in memory.", e); + log::warn!( + "IO error while writing ping to file: {}. Enqueuing upload of what we have in memory.", + e + ); glean.additional_metrics.io_errors.add_sync(glean, 1); // `serde_json::to_string` only fails if serialization of the content // fails or it contains maps with non-string keys. @@ -248,6 +283,17 @@ impl PingType { ping.name ); + if !ping.schedules_pings.is_empty() { + log::info!( + "The ping '{}' is being used to schedule other pings: {:?}", + ping.name, + ping.schedules_pings + ); + for scheduled_ping_name in &ping.schedules_pings { + glean.submit_ping_by_name(scheduled_ping_name, reason); + } + } + true } } diff --git a/third_party/rust/glean-core/src/metrics/metrics_enabled_config.rs b/third_party/rust/glean-core/src/metrics/remote_settings_config.rs index b36cbc150a..e7a560e81c 100644 --- a/third_party/rust/glean-core/src/metrics/metrics_enabled_config.rs +++ b/third_party/rust/glean-core/src/metrics/remote_settings_config.rs @@ -16,25 +16,31 @@ use serde::{Deserialize, Serialize}; /// } /// ``` #[derive(Serialize, Deserialize, Debug, Clone, Default)] -pub struct MetricsEnabledConfig { +pub struct RemoteSettingsConfig { /// This is a `HashMap` consisting of base_identifiers as keys /// and bool values representing an override for the `disabled` /// property of the metric, only inverted to reduce confusion. /// If a particular metric has a value of `true` here, it means /// the default of the metric will be overriden and set to the /// enabled state. - #[serde(flatten)] + #[serde(default)] pub metrics_enabled: HashMap<String, bool>, + + /// This is a `HashMap` consisting of ping names as keys and + /// boolean values representing on override for the default + /// enabled state of the ping of the same name. + #[serde(default)] + pub pings_enabled: HashMap<String, bool>, } -impl MetricsEnabledConfig { - /// Creates a new MetricsEnabledConfig +impl RemoteSettingsConfig { + /// Creates a new RemoteSettingsConfig pub fn new() -> Self { Default::default() } } -impl TryFrom<String> for MetricsEnabledConfig { +impl TryFrom<String> for RemoteSettingsConfig { type Error = crate::ErrorKind; fn try_from(json: String) -> Result<Self, Self::Error> { diff --git a/third_party/rust/glean-core/src/ping/mod.rs b/third_party/rust/glean-core/src/ping/mod.rs index d1a67ae360..8af4cd27f4 100644 --- a/third_party/rust/glean-core/src/ping/mod.rs +++ b/third_party/rust/glean-core/src/ping/mod.rs @@ -32,6 +32,8 @@ pub struct Ping<'a> { pub headers: HeaderMap, /// Whether the content contains {client|ping}_info sections. pub includes_info_sections: bool, + /// Other pings that should be scheduled when this ping is sent. + pub schedules_pings: Vec<String>, } /// Collect a ping's data, assemble it into its full payload and store it on disk. @@ -314,6 +316,7 @@ impl PingMaker { url_path, headers: self.get_headers(glean), includes_info_sections: ping.include_info_sections(), + schedules_pings: ping.schedules_pings().to_vec(), }) } diff --git a/third_party/rust/glean-core/src/upload/directory.rs b/third_party/rust/glean-core/src/upload/directory.rs index 91a4d061d1..b083fa8f1f 100644 --- a/third_party/rust/glean-core/src/upload/directory.rs +++ b/third_party/rust/glean-core/src/upload/directory.rs @@ -337,7 +337,7 @@ mod test { let (mut glean, dir) = new_glean(None); // Register a ping for testing - let ping_type = PingType::new("test", true, true, true, true, vec![]); + let ping_type = PingType::new("test", true, true, true, true, true, vec![], vec![]); glean.register_ping_type(&ping_type); // Submit the ping to populate the pending_pings directory @@ -364,7 +364,7 @@ mod test { let (mut glean, dir) = new_glean(None); // Register a ping for testing - let ping_type = PingType::new("test", true, true, true, true, vec![]); + let ping_type = PingType::new("test", true, true, true, true, true, vec![], vec![]); glean.register_ping_type(&ping_type); // Submit the ping to populate the pending_pings directory @@ -400,7 +400,7 @@ mod test { let (mut glean, dir) = new_glean(None); // Register a ping for testing - let ping_type = PingType::new("test", true, true, true, true, vec![]); + let ping_type = PingType::new("test", true, true, true, true, true, vec![], vec![]); glean.register_ping_type(&ping_type); // Submit the ping to populate the pending_pings directory diff --git a/third_party/rust/glean-core/src/upload/mod.rs b/third_party/rust/glean-core/src/upload/mod.rs index f217137f00..83b06cca65 100644 --- a/third_party/rust/glean-core/src/upload/mod.rs +++ b/third_party/rust/glean-core/src/upload/mod.rs @@ -1031,6 +1031,8 @@ mod test { /* send_if_empty */ true, true, true, + true, + vec![], vec![], ); glean.register_ping_type(&ping_type); @@ -1070,6 +1072,8 @@ mod test { /* send_if_empty */ true, true, true, + true, + vec![], vec![], ); glean.register_ping_type(&ping_type); @@ -1107,6 +1111,8 @@ mod test { /* send_if_empty */ true, true, true, + true, + vec![], vec![], ); glean.register_ping_type(&ping_type); @@ -1144,6 +1150,8 @@ mod test { /* send_if_empty */ true, true, true, + true, + vec![], vec![], ); glean.register_ping_type(&ping_type); @@ -1181,6 +1189,8 @@ mod test { /* send_if_empty */ true, true, true, + true, + vec![], vec![], ); glean.register_ping_type(&ping_type); @@ -1220,6 +1230,8 @@ mod test { /* send_if_empty */ true, true, true, + true, + vec![], vec![], ); glean.register_ping_type(&ping_type); @@ -1335,6 +1347,8 @@ mod test { /* send_if_empty */ true, true, true, + true, + vec![], vec![], ); glean.register_ping_type(&ping_type); @@ -1408,6 +1422,8 @@ mod test { /* send_if_empty */ true, true, true, + true, + vec![], vec![], ); glean.register_ping_type(&ping_type); @@ -1465,6 +1481,8 @@ mod test { /* send_if_empty */ true, true, true, + true, + vec![], vec![], ); glean.register_ping_type(&ping_type); @@ -1543,6 +1561,8 @@ mod test { /* send_if_empty */ true, true, true, + true, + vec![], vec![], ); glean.register_ping_type(&ping_type); @@ -1622,6 +1642,8 @@ mod test { /* send_if_empty */ true, true, true, + true, + vec![], vec![], ); glean.register_ping_type(&ping_type); @@ -1703,6 +1725,8 @@ mod test { /* send_if_empty */ true, true, true, + true, + vec![], vec![], ); glean.register_ping_type(&ping_type); |