summaryrefslogtreecommitdiffstats
path: root/third_party/rust/glean-core/src
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/rust/glean-core/src')
-rw-r--r--third_party/rust/glean-core/src/core/mod.rs6
-rw-r--r--third_party/rust/glean-core/src/glean.udl10
-rw-r--r--third_party/rust/glean-core/src/internal_pings.rs4
-rw-r--r--third_party/rust/glean-core/src/lib.rs38
-rw-r--r--third_party/rust/glean-core/src/lib_unit_tests.rs4
-rw-r--r--third_party/rust/glean-core/src/metrics/boolean.rs12
-rw-r--r--third_party/rust/glean-core/src/metrics/counter.rs12
-rw-r--r--third_party/rust/glean-core/src/metrics/custom_distribution.rs38
-rw-r--r--third_party/rust/glean-core/src/metrics/datetime.rs10
-rw-r--r--third_party/rust/glean-core/src/metrics/denominator.rs11
-rw-r--r--third_party/rust/glean-core/src/metrics/event.rs7
-rw-r--r--third_party/rust/glean-core/src/metrics/experiment.rs9
-rw-r--r--third_party/rust/glean-core/src/metrics/labeled.rs2
-rw-r--r--third_party/rust/glean-core/src/metrics/memory_distribution.rs11
-rw-r--r--third_party/rust/glean-core/src/metrics/mod.rs11
-rw-r--r--third_party/rust/glean-core/src/metrics/numerator.rs8
-rw-r--r--third_party/rust/glean-core/src/metrics/object.rs135
-rw-r--r--third_party/rust/glean-core/src/metrics/ping.rs28
-rw-r--r--third_party/rust/glean-core/src/metrics/quantity.rs11
-rw-r--r--third_party/rust/glean-core/src/metrics/rate.rs11
-rw-r--r--third_party/rust/glean-core/src/metrics/string.rs11
-rw-r--r--third_party/rust/glean-core/src/metrics/string_list.rs11
-rw-r--r--third_party/rust/glean-core/src/metrics/text.rs11
-rw-r--r--third_party/rust/glean-core/src/metrics/timespan.rs11
-rw-r--r--third_party/rust/glean-core/src/metrics/timing_distribution.rs43
-rw-r--r--third_party/rust/glean-core/src/metrics/url.rs11
-rw-r--r--third_party/rust/glean-core/src/metrics/uuid.rs11
-rw-r--r--third_party/rust/glean-core/src/ping/mod.rs44
-rw-r--r--third_party/rust/glean-core/src/traits/custom_distribution.rs16
-rw-r--r--third_party/rust/glean-core/src/traits/mod.rs6
-rw-r--r--third_party/rust/glean-core/src/traits/object.rs53
-rw-r--r--third_party/rust/glean-core/src/traits/timing_distribution.rs25
-rw-r--r--third_party/rust/glean-core/src/upload/directory.rs78
-rw-r--r--third_party/rust/glean-core/src/upload/mod.rs343
-rw-r--r--third_party/rust/glean-core/src/upload/request.rs37
35 files changed, 899 insertions, 190 deletions
diff --git a/third_party/rust/glean-core/src/core/mod.rs b/third_party/rust/glean-core/src/core/mod.rs
index 5a8dd56cde..30f9a34f11 100644
--- a/third_party/rust/glean-core/src/core/mod.rs
+++ b/third_party/rust/glean-core/src/core/mod.rs
@@ -118,11 +118,11 @@ where
/// trim_data_to_registered_pings: false,
/// log_level: None,
/// rate_limit: None,
-/// enable_event_timestamps: false,
+/// enable_event_timestamps: true,
/// experimentation_id: None,
/// };
/// let mut glean = Glean::new(cfg).unwrap();
-/// let ping = PingType::new("sample", true, false, true, vec![]);
+/// let ping = PingType::new("sample", true, false, true, true, vec![]);
/// glean.register_ping_type(&ping);
///
/// let call_counter: CounterMetric = CounterMetric::new(CommonMetricData {
@@ -318,7 +318,7 @@ impl Glean {
trim_data_to_registered_pings: false,
log_level: None,
rate_limit: None,
- enable_event_timestamps: false,
+ enable_event_timestamps: true,
experimentation_id: None,
};
diff --git a/third_party/rust/glean-core/src/glean.udl b/third_party/rust/glean-core/src/glean.udl
index e531f64a26..e68a57ea4c 100644
--- a/third_party/rust/glean-core/src/glean.udl
+++ b/third_party/rust/glean-core/src/glean.udl
@@ -201,6 +201,10 @@ dictionary PingRequest {
sequence<u8> body;
// A map with all the headers to be sent with the request.
record<DOMString, string> headers;
+ // Whether the body has {client|ping}_info sections.
+ boolean body_has_info_sections;
+ // The ping's name. Likely also somewhere in `path`.
+ string ping_name;
};
// An enum representing the possible upload tasks to be performed by an uploader.
@@ -287,7 +291,7 @@ enum ErrorType {
};
interface PingType {
- constructor(string name, boolean include_client_id, boolean send_if_empty, boolean precise_timestamps, sequence<string> reason_codes);
+ constructor(string name, boolean include_client_id, boolean send_if_empty, boolean precise_timestamps, boolean include_info_sections, sequence<string> reason_codes);
void submit(optional string? reason = null);
};
@@ -480,6 +484,8 @@ interface TimingDistributionMetric {
void accumulate_samples(sequence<i64> samples);
+ void accumulate_single_sample(i64 sample);
+
DistributionData? test_get_value(optional string? ping_name = null);
i32 test_get_num_recorded_errors(ErrorType error);
@@ -523,6 +529,8 @@ interface CustomDistributionMetric {
void accumulate_samples(sequence<i64> samples);
+ void accumulate_single_sample(i64 sample);
+
DistributionData? test_get_value(optional string? ping_name = null);
i32 test_get_num_recorded_errors(ErrorType error);
diff --git a/third_party/rust/glean-core/src/internal_pings.rs b/third_party/rust/glean-core/src/internal_pings.rs
index 076a1f7485..07c3849006 100644
--- a/third_party/rust/glean-core/src/internal_pings.rs
+++ b/third_party/rust/glean-core/src/internal_pings.rs
@@ -26,6 +26,7 @@ impl InternalPings {
true,
true,
true,
+ true,
vec![
"active".to_string(),
"dirty_startup".to_string(),
@@ -37,6 +38,7 @@ impl InternalPings {
true,
false,
true,
+ true,
vec![
"overdue".to_string(),
"reschedule".to_string(),
@@ -50,6 +52,7 @@ impl InternalPings {
true,
false,
true,
+ true,
vec![
"startup".to_string(),
"inactive".to_string(),
@@ -61,6 +64,7 @@ impl InternalPings {
true,
true,
true,
+ true,
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 a54e57a95b..b7f9d73beb 100644
--- a/third_party/rust/glean-core/src/lib.rs
+++ b/third_party/rust/glean-core/src/lib.rs
@@ -91,12 +91,12 @@ pub(crate) const DELETION_REQUEST_PINGS_DIRECTORY: &str = "deletion_request";
static INITIALIZE_CALLED: AtomicBool = AtomicBool::new(false);
/// Keep track of the debug features before Glean is initialized.
-static PRE_INIT_DEBUG_VIEW_TAG: OnceCell<Mutex<String>> = OnceCell::new();
+static PRE_INIT_DEBUG_VIEW_TAG: Mutex<String> = Mutex::new(String::new());
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: Mutex<Vec<String>> = Mutex::new(Vec::new());
/// Keep track of pings registered before Glean is initialized.
-static PRE_INIT_PING_REGISTRATION: OnceCell<Mutex<Vec<metrics::PingType>>> = OnceCell::new();
+static PRE_INIT_PING_REGISTRATION: Mutex<Vec<metrics::PingType>> = Mutex::new(Vec::new());
/// Global singleton of the handles of the glean.init threads.
/// For joining. For tests.
@@ -396,11 +396,9 @@ fn initialize_inner(
core::with_glean_mut(|glean| {
// The debug view tag might have been set before initialize,
// get the cached value and set it.
- if let Some(tag) = PRE_INIT_DEBUG_VIEW_TAG.get() {
- let lock = tag.try_lock();
- if let Ok(ref debug_tag) = lock {
- glean.set_debug_view_tag(debug_tag);
- }
+ let debug_tag = PRE_INIT_DEBUG_VIEW_TAG.lock().unwrap();
+ if debug_tag.len() > 0 {
+ glean.set_debug_view_tag(&debug_tag);
}
// The log pings debug option might have been set before initialize,
@@ -412,11 +410,9 @@ fn initialize_inner(
// The source tags might have been set before initialize,
// get the cached value and set them.
- if let Some(tags) = PRE_INIT_SOURCE_TAGS.get() {
- let lock = tags.try_lock();
- if let Ok(ref source_tags) = lock {
- glean.set_source_tags(source_tags.to_vec());
- }
+ let source_tags = PRE_INIT_SOURCE_TAGS.lock().unwrap();
+ if source_tags.len() > 0 {
+ glean.set_source_tags(source_tags.to_vec());
}
// Get the current value of the dirty flag so we know whether to
@@ -428,13 +424,9 @@ fn initialize_inner(
// Perform registration of pings that were attempted to be
// 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);
- }
- }
+ let pings = PRE_INIT_PING_REGISTRATION.lock().unwrap();
+ for ping in pings.iter() {
+ glean.register_ping_type(ping);
}
// If this is the first time ever the Glean SDK runs, make sure to set
@@ -861,7 +853,7 @@ pub(crate) fn register_ping_type(ping: &PingType) {
// 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 m = &PRE_INIT_PING_REGISTRATION;
let mut lock = m.lock().unwrap();
lock.push(ping.clone());
}
@@ -956,7 +948,7 @@ pub fn glean_set_debug_view_tag(tag: String) -> bool {
true
} else {
// Glean has not been initialized yet. Cache the provided tag value.
- let m = PRE_INIT_DEBUG_VIEW_TAG.get_or_init(Default::default);
+ let m = &PRE_INIT_DEBUG_VIEW_TAG;
let mut lock = m.lock().unwrap();
*lock = tag;
// When setting the debug view tag before initialization,
@@ -984,7 +976,7 @@ pub fn glean_set_source_tags(tags: Vec<String>) -> bool {
true
} else {
// Glean has not been initialized yet. Cache the provided source tags.
- let m = PRE_INIT_SOURCE_TAGS.get_or_init(Default::default);
+ let m = &PRE_INIT_SOURCE_TAGS;
let mut lock = m.lock().unwrap();
*lock = tags;
// When setting the source tags before initialization,
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 0fc85b4602..cb1e4129d8 100644
--- a/third_party/rust/glean-core/src/lib_unit_tests.rs
+++ b/third_party/rust/glean-core/src/lib_unit_tests.rs
@@ -197,7 +197,7 @@ fn experimentation_id_is_set_correctly() {
trim_data_to_registered_pings: false,
log_level: None,
rate_limit: None,
- enable_event_timestamps: false,
+ enable_event_timestamps: true,
experimentation_id: Some(experimentation_id.to_string()),
})
.unwrap();
@@ -423,6 +423,7 @@ fn correct_order() {
Jwe("eyJhbGciOiJSU0EtT0FFUCIsImVuYyI6IkEyNTZHQ00ifQ.OKOawDo13gRp2ojaHV7LFpZcgV7T6DVZKTyKOMTYUmKoTCVJRgckCL9kiMT03JGeipsEdY3mx_etLbbWSrFr05kLzcSr4qKAq7YN7e9jwQRb23nfa6c9d-StnImGyFDbSv04uVuxIp5Zms1gNxKKK2Da14B8S4rzVRltdYwam_lDp5XnZAYpQdb76FdIKLaVmqgfwX7XWRxv2322i-vDxRfqNzo_tETKzpVLzfiwQyeyPGLBIO56YJ7eObdv0je81860ppamavo35UgoRdbYaBcoh9QcfylQr66oc6vFWXRcZ_ZT2LawVCWTIy3brGPi6UklfCpIMfIjf7iGdXKHzg.48V1_ALb6US04U3b.5eym8TW_c8SuK0ltJ3rpYIzOeDQz7TALvtu6UG9oMo4vpzs9tX_EFShS8iB7j6jiSdiwkIr3ajwQzaBtQD_A.XFBoMYUZodetZdvTiFvSkQ".into()),
Rate(0, 0),
Text(long_string),
+ Object("{}".into()),
];
for metric in all_metrics {
@@ -451,6 +452,7 @@ fn correct_order() {
Rate(..) => assert_eq!(14, disc),
Url(..) => assert_eq!(15, disc),
Text(..) => assert_eq!(16, disc),
+ Object(..) => assert_eq!(17, disc),
}
}
}
diff --git a/third_party/rust/glean-core/src/metrics/boolean.rs b/third_party/rust/glean-core/src/metrics/boolean.rs
index 71ed2372c2..ade4a22bfc 100644
--- a/third_party/rust/glean-core/src/metrics/boolean.rs
+++ b/third_party/rust/glean-core/src/metrics/boolean.rs
@@ -74,7 +74,6 @@ impl BooleanMetric {
///
/// # Arguments
///
- /// * `glean` - the Glean instance this metric belongs to.
/// * `value` - the value to set.
pub fn set(&self, value: bool) {
let metric = self.clone();
@@ -106,6 +105,15 @@ impl BooleanMetric {
/// Gets the currently stored value as an integer.
///
/// This doesn't clear the stored value.
+ ///
+ /// # Arguments
+ ///
+ /// * `ping_name` - the optional name of the ping to retrieve the metric
+ /// for. Defaults to the first value in `send_in_pings`.
+ ///
+ /// # Returns
+ ///
+ /// The stored value or `None` if nothing stored.
pub fn test_get_value(&self, ping_name: Option<String>) -> Option<bool> {
crate::block_on_dispatcher();
crate::core::with_glean(|glean| self.get_value(glean, ping_name.as_deref()))
@@ -118,8 +126,6 @@ impl BooleanMetric {
/// # Arguments
///
/// * `error` - The type of error
- /// * `ping_name` - represents the optional name of the ping to retrieve the
- /// metric for. inner to the first value in `send_in_pings`.
///
/// # Returns
///
diff --git a/third_party/rust/glean-core/src/metrics/counter.rs b/third_party/rust/glean-core/src/metrics/counter.rs
index 8f0a01cc3e..7e262c7d68 100644
--- a/third_party/rust/glean-core/src/metrics/counter.rs
+++ b/third_party/rust/glean-core/src/metrics/counter.rs
@@ -105,7 +105,6 @@ impl CounterMetric {
///
/// # Arguments
///
- /// * `glean` - The Glean instance this metric belongs to.
/// * `amount` - The amount to increase by. Should be positive.
///
/// ## Notes
@@ -143,6 +142,15 @@ impl CounterMetric {
/// Gets the currently stored value as an integer.
///
/// This doesn't clear the stored value.
+ ///
+ /// # Arguments
+ ///
+ /// * `ping_name` - the optional name of the ping to retrieve the metric
+ /// for. Defaults to the first value in `send_in_pings`.
+ ///
+ /// # Returns
+ ///
+ /// The stored value or `None` if nothing stored.
pub fn test_get_value(&self, ping_name: Option<String>) -> Option<i32> {
crate::block_on_dispatcher();
crate::core::with_glean(|glean| self.get_value(glean, ping_name.as_deref()))
@@ -155,8 +163,6 @@ impl CounterMetric {
/// # Arguments
///
/// * `error` - The type of error
- /// * `ping_name` - represents the optional name of the ping to retrieve the
- /// metric for. inner to the first value in `send_in_pings`.
///
/// # Returns
///
diff --git a/third_party/rust/glean-core/src/metrics/custom_distribution.rs b/third_party/rust/glean-core/src/metrics/custom_distribution.rs
index 929e4863ec..c7f3fbc56f 100644
--- a/third_party/rust/glean-core/src/metrics/custom_distribution.rs
+++ b/third_party/rust/glean-core/src/metrics/custom_distribution.rs
@@ -84,14 +84,33 @@ impl CustomDistributionMetric {
/// for each of them.
pub fn accumulate_samples(&self, samples: Vec<i64>) {
let metric = self.clone();
- crate::launch_with_glean(move |glean| metric.accumulate_samples_sync(glean, samples))
+ crate::launch_with_glean(move |glean| metric.accumulate_samples_sync(glean, &samples))
+ }
+
+ /// Accumulates precisely one signed sample and appends it to the metric.
+ ///
+ /// Signed is required so that the platform-specific code can provide us with a
+ /// 64 bit signed integer if no `u64` comparable type is available. This
+ /// will take care of filtering and reporting errors.
+ ///
+ /// # Arguments
+ ///
+ /// - `sample` - The singular sample to be recorded by the metric.
+ ///
+ /// ## Notes
+ ///
+ /// Discards any negative value of `sample` and reports an
+ /// [`ErrorType::InvalidValue`](crate::ErrorType::InvalidValue).
+ pub fn accumulate_single_sample(&self, sample: i64) {
+ let metric = self.clone();
+ crate::launch_with_glean(move |glean| metric.accumulate_samples_sync(glean, &[sample]))
}
/// Accumulates the provided sample in the metric synchronously.
///
/// See [`accumulate_samples`](Self::accumulate_samples) for details.
#[doc(hidden)]
- pub fn accumulate_samples_sync(&self, glean: &Glean, samples: Vec<i64>) {
+ pub fn accumulate_samples_sync(&self, glean: &Glean, samples: &[i64]) {
if !self.should_record(glean) {
return;
}
@@ -132,7 +151,7 @@ impl CustomDistributionMetric {
self.bucket_count as usize,
)
};
- accumulate(&samples, hist, Metric::CustomDistributionLinear)
+ accumulate(samples, hist, Metric::CustomDistributionLinear)
}
HistogramType::Exponential => {
let hist = if let Some(Metric::CustomDistributionExponential(hist)) = old_value
@@ -145,7 +164,7 @@ impl CustomDistributionMetric {
self.bucket_count as usize,
)
};
- accumulate(&samples, hist, Metric::CustomDistributionExponential)
+ accumulate(samples, hist, Metric::CustomDistributionExponential)
}
};
@@ -194,6 +213,15 @@ impl CustomDistributionMetric {
/// Gets the currently stored value as an integer.
///
/// This doesn't clear the stored value.
+ ///
+ /// # Arguments
+ ///
+ /// * `ping_name` - the optional name of the ping to retrieve the metric
+ /// for. Defaults to the first value in `send_in_pings`.
+ ///
+ /// # Returns
+ ///
+ /// The stored value or `None` if nothing stored.
pub fn test_get_value(&self, ping_name: Option<String>) -> Option<DistributionData> {
crate::block_on_dispatcher();
crate::core::with_glean(|glean| self.get_value(glean, ping_name.as_deref()))
@@ -206,8 +234,6 @@ impl CustomDistributionMetric {
/// # Arguments
///
/// * `error` - The type of error
- /// * `ping_name` - represents the optional name of the ping to retrieve the
- /// metric for. inner to the first value in `send_in_pings`.
///
/// # Returns
///
diff --git a/third_party/rust/glean-core/src/metrics/datetime.rs b/third_party/rust/glean-core/src/metrics/datetime.rs
index 3ef846a32c..e04f7fc051 100644
--- a/third_party/rust/glean-core/src/metrics/datetime.rs
+++ b/third_party/rust/glean-core/src/metrics/datetime.rs
@@ -262,8 +262,8 @@ impl DatetimeMetric {
///
/// # Arguments
///
- /// * `glean` - the Glean instance this metric belongs to.
- /// * `storage_name` - the storage name to look into.
+ /// * `ping_name` - the optional name of the ping to retrieve the metric
+ /// for. Defaults to the first value in `send_in_pings`.
///
/// # Returns
///
@@ -284,8 +284,8 @@ impl DatetimeMetric {
///
/// # Arguments
///
- /// * `glean` - the Glean instance this metric belongs to.
- /// * `storage_name` - the storage name to look into.
+ /// * `ping_name` - the optional name of the ping to retrieve the metric
+ /// for. Defaults to the first value in `send_in_pings`.
///
/// # Returns
///
@@ -311,8 +311,6 @@ impl DatetimeMetric {
/// # Arguments
///
/// * `error` - The type of error
- /// * `ping_name` - represents the optional name of the ping to retrieve the
- /// metric for. inner to the first value in `send_in_pings`.
///
/// # Returns
///
diff --git a/third_party/rust/glean-core/src/metrics/denominator.rs b/third_party/rust/glean-core/src/metrics/denominator.rs
index fb80874924..3083d6e78a 100644
--- a/third_party/rust/glean-core/src/metrics/denominator.rs
+++ b/third_party/rust/glean-core/src/metrics/denominator.rs
@@ -91,6 +91,15 @@ impl DenominatorMetric {
/// Gets the currently stored value as an integer.
///
/// This doesn't clear the stored value.
+ ///
+ /// # Arguments
+ ///
+ /// * `ping_name` - the optional name of the ping to retrieve the metric
+ /// for. Defaults to the first value in `send_in_pings`.
+ ///
+ /// # Returns
+ ///
+ /// The stored value or `None` if nothing stored.
pub fn test_get_value(&self, ping_name: Option<String>) -> Option<i32> {
crate::block_on_dispatcher();
crate::core::with_glean(|glean| self.get_value(glean, ping_name.as_deref()))
@@ -124,8 +133,6 @@ impl DenominatorMetric {
/// # Arguments
///
/// * `error` - The type of error
- /// * `ping_name` - the optional name of the ping to retrieve the metric
- /// for. inner to the first value in `send_in_pings`.
///
/// # Returns
///
diff --git a/third_party/rust/glean-core/src/metrics/event.rs b/third_party/rust/glean-core/src/metrics/event.rs
index 5ad6e6d50c..c7aefd9cd6 100644
--- a/third_party/rust/glean-core/src/metrics/event.rs
+++ b/third_party/rust/glean-core/src/metrics/event.rs
@@ -185,6 +185,11 @@ impl EventMetric {
/// Get the vector of currently stored events for this event metric.
///
/// This doesn't clear the stored value.
+ ///
+ /// # Arguments
+ ///
+ /// * `ping_name` - the optional name of the ping to retrieve the metric
+ /// for. Defaults to the first value in `send_in_pings`.
pub fn test_get_value(&self, ping_name: Option<String>) -> Option<Vec<RecordedEvent>> {
crate::block_on_dispatcher();
crate::core::with_glean(|glean| self.get_value(glean, ping_name.as_deref()))
@@ -197,8 +202,6 @@ impl EventMetric {
/// # Arguments
///
/// * `error` - The type of error
- /// * `ping_name` - represents the optional name of the ping to retrieve the
- /// metric for. inner to the first value in `send_in_pings`.
///
/// # Returns
///
diff --git a/third_party/rust/glean-core/src/metrics/experiment.rs b/third_party/rust/glean-core/src/metrics/experiment.rs
index 23e6c41ce2..5695bf942e 100644
--- a/third_party/rust/glean-core/src/metrics/experiment.rs
+++ b/third_party/rust/glean-core/src/metrics/experiment.rs
@@ -195,6 +195,15 @@ impl ExperimentMetric {
/// the RecordedExperiment.
///
/// This doesn't clear the stored value.
+ ///
+ /// # Arguments
+ ///
+ /// * `ping_name` - the optional name of the ping to retrieve the metric
+ /// for. Defaults to the first value in `send_in_pings`.
+ ///
+ /// # Returns
+ ///
+ /// The stored value or `None` if nothing stored.
pub fn test_get_value(&self, glean: &Glean) -> Option<RecordedExperiment> {
match StorageManager.snapshot_metric_for_test(
glean.storage(),
diff --git a/third_party/rust/glean-core/src/metrics/labeled.rs b/third_party/rust/glean-core/src/metrics/labeled.rs
index fa3e6a6a75..f9f6a28880 100644
--- a/third_party/rust/glean-core/src/metrics/labeled.rs
+++ b/third_party/rust/glean-core/src/metrics/labeled.rs
@@ -205,8 +205,6 @@ where
/// # 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
///
diff --git a/third_party/rust/glean-core/src/metrics/memory_distribution.rs b/third_party/rust/glean-core/src/metrics/memory_distribution.rs
index ac9eda1a90..7b5e5ee192 100644
--- a/third_party/rust/glean-core/src/metrics/memory_distribution.rs
+++ b/third_party/rust/glean-core/src/metrics/memory_distribution.rs
@@ -254,6 +254,15 @@ impl MemoryDistributionMetric {
/// Gets the currently stored value.
///
/// This doesn't clear the stored value.
+ ///
+ /// # Arguments
+ ///
+ /// * `ping_name` - the optional name of the ping to retrieve the metric
+ /// for. Defaults to the first value in `send_in_pings`.
+ ///
+ /// # Returns
+ ///
+ /// The stored value or `None` if nothing stored.
pub fn test_get_value(&self, ping_name: Option<String>) -> Option<DistributionData> {
crate::block_on_dispatcher();
crate::core::with_glean(|glean| self.get_value(glean, ping_name.as_deref()))
@@ -266,8 +275,6 @@ impl MemoryDistributionMetric {
/// # 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
///
diff --git a/third_party/rust/glean-core/src/metrics/mod.rs b/third_party/rust/glean-core/src/metrics/mod.rs
index 43253b9aa7..92001efd2a 100644
--- a/third_party/rust/glean-core/src/metrics/mod.rs
+++ b/third_party/rust/glean-core/src/metrics/mod.rs
@@ -9,7 +9,8 @@ use std::sync::atomic::Ordering;
use chrono::{DateTime, FixedOffset};
use serde::{Deserialize, Serialize};
-use serde_json::{json, Value as JsonValue};
+use serde_json::json;
+pub use serde_json::Value as JsonValue;
mod boolean;
mod counter;
@@ -23,6 +24,7 @@ mod memory_distribution;
mod memory_unit;
mod metrics_enabled_config;
mod numerator;
+mod object;
mod ping;
mod quantity;
mod rate;
@@ -54,6 +56,7 @@ pub use self::labeled::{LabeledBoolean, LabeledCounter, LabeledMetric, LabeledSt
pub use self::memory_distribution::MemoryDistributionMetric;
pub use self::memory_unit::MemoryUnit;
pub use self::numerator::NumeratorMetric;
+pub use self::object::ObjectMetric;
pub use self::ping::PingType;
pub use self::quantity::QuantityMetric;
pub use self::rate::{Rate, RateMetric};
@@ -141,6 +144,8 @@ pub enum Metric {
Url(String),
/// A Text metric. See [`TextMetric`] for more information.
Text(String),
+ /// An Object metric. See [`ObjectMetric`] for more information.
+ Object(String),
}
/// A [`MetricType`] describes common behavior across all metrics.
@@ -251,6 +256,7 @@ impl Metric {
Metric::MemoryDistribution(_) => "memory_distribution",
Metric::Jwe(_) => "jwe",
Metric::Text(_) => "text",
+ Metric::Object(_) => "object",
}
}
@@ -280,6 +286,9 @@ impl Metric {
Metric::MemoryDistribution(hist) => json!(memory_distribution::snapshot(hist)),
Metric::Jwe(s) => json!(s),
Metric::Text(s) => json!(s),
+ Metric::Object(s) => {
+ serde_json::from_str(s).expect("object storage should have been json")
+ }
}
}
}
diff --git a/third_party/rust/glean-core/src/metrics/numerator.rs b/third_party/rust/glean-core/src/metrics/numerator.rs
index 3c340cab1d..de29338a5c 100644
--- a/third_party/rust/glean-core/src/metrics/numerator.rs
+++ b/third_party/rust/glean-core/src/metrics/numerator.rs
@@ -55,12 +55,16 @@ impl NumeratorMetric {
///
/// Gets the currently stored value as a pair of integers.
///
+ /// This doesn't clear the stored value.
+ ///
/// # Arguments
///
/// * `ping_name` - the optional name of the ping to retrieve the metric
/// for. Defaults to the first value in `send_in_pings`.
///
- /// This doesn't clear the stored value.
+ /// # Returns
+ ///
+ /// The stored value or `None` if nothing stored.
pub fn test_get_value(&self, ping_name: Option<String>) -> Option<Rate> {
crate::block_on_dispatcher();
crate::core::with_glean(|glean| self.get_value(glean, ping_name.as_deref()))
@@ -82,8 +86,6 @@ impl NumeratorMetric {
/// # Arguments
///
/// * `error` - The type of error
- /// * `ping_name` - the optional name of the ping to retrieve the metric
- /// for. Defaults to the first value in `send_in_pings`.
///
/// # Returns
///
diff --git a/third_party/rust/glean-core/src/metrics/object.rs b/third_party/rust/glean-core/src/metrics/object.rs
new file mode 100644
index 0000000000..6071e2b33a
--- /dev/null
+++ b/third_party/rust/glean-core/src/metrics/object.rs
@@ -0,0 +1,135 @@
+// 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 std::sync::Arc;
+
+use crate::common_metric_data::CommonMetricDataInternal;
+use crate::error_recording::{record_error, test_get_num_recorded_errors, ErrorType};
+use crate::metrics::JsonValue;
+use crate::metrics::Metric;
+use crate::metrics::MetricType;
+use crate::storage::StorageManager;
+use crate::CommonMetricData;
+use crate::Glean;
+
+/// An object metric.
+///
+/// Record structured data.
+/// The value must adhere to a predefined structure and is serialized into JSON.
+#[derive(Clone, Debug)]
+pub struct ObjectMetric {
+ meta: Arc<CommonMetricDataInternal>,
+}
+
+impl MetricType for ObjectMetric {
+ fn meta(&self) -> &CommonMetricDataInternal {
+ &self.meta
+ }
+}
+
+// IMPORTANT:
+//
+// When changing this implementation, make sure all the operations are
+// also declared in the related trait in `../traits/`.
+impl ObjectMetric {
+ /// Creates a new object metric.
+ pub fn new(meta: CommonMetricData) -> Self {
+ Self {
+ meta: Arc::new(meta.into()),
+ }
+ }
+
+ /// Sets to the specified structure.
+ ///
+ /// # Arguments
+ ///
+ /// * `glean` - the Glean instance this metric belongs to.
+ /// * `value` - the value to set.
+ #[doc(hidden)]
+ pub fn set_sync(&self, glean: &Glean, value: JsonValue) {
+ let value = Metric::Object(serde_json::to_string(&value).unwrap());
+ glean.storage().record(glean, &self.meta, &value)
+ }
+
+ /// Sets to the specified structure.
+ ///
+ /// No additional verification is done.
+ /// The shape needs to be externally verified.
+ ///
+ /// # Arguments
+ ///
+ /// * `value` - the value to set.
+ pub fn set(&self, value: JsonValue) {
+ let metric = self.clone();
+ crate::launch_with_glean(move |glean| metric.set_sync(glean, value))
+ }
+
+ /// Record an `InvalidValue` error for this metric.
+ ///
+ /// Only to be used by the RLB.
+ // TODO(bug 1691073): This can probably go once we have a more generic mechanism to record
+ // errors
+ pub fn record_schema_error(&self) {
+ let metric = self.clone();
+ crate::launch_with_glean(move |glean| {
+ let msg = "Value did not match predefined schema";
+ record_error(glean, &metric.meta, ErrorType::InvalidValue, msg, None);
+ });
+ }
+
+ /// Get current value
+ #[doc(hidden)]
+ pub fn get_value<'a, S: Into<Option<&'a str>>>(
+ &self,
+ glean: &Glean,
+ ping_name: S,
+ ) -> Option<String> {
+ let queried_ping_name = ping_name
+ .into()
+ .unwrap_or_else(|| &self.meta().inner.send_in_pings[0]);
+
+ match StorageManager.snapshot_metric_for_test(
+ glean.storage(),
+ queried_ping_name,
+ &self.meta.identifier(glean),
+ self.meta.inner.lifetime,
+ ) {
+ Some(Metric::Object(o)) => Some(o),
+ _ => None,
+ }
+ }
+
+ /// **Test-only API (exported for FFI purposes).**
+ ///
+ /// Gets the currently stored value as JSON.
+ ///
+ /// This doesn't clear the stored value.
+ pub fn test_get_value(&self, ping_name: Option<String>) -> Option<JsonValue> {
+ crate::block_on_dispatcher();
+ let value = crate::core::with_glean(|glean| self.get_value(glean, ping_name.as_deref()));
+ // We only store valid JSON
+ value.map(|val| serde_json::from_str(&val).unwrap())
+ }
+
+ /// **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. inner to the first value in `send_in_pings`.
+ ///
+ /// # Returns
+ ///
+ /// The number of errors reported.
+ pub fn test_get_num_recorded_errors(&self, error: ErrorType) -> i32 {
+ crate::block_on_dispatcher();
+
+ crate::core::with_glean(|glean| {
+ test_get_num_recorded_errors(glean, self.meta(), error).unwrap_or(0)
+ })
+ }
+}
diff --git a/third_party/rust/glean-core/src/metrics/ping.rs b/third_party/rust/glean-core/src/metrics/ping.rs
index dc37d76a45..e60284b1e2 100644
--- a/third_party/rust/glean-core/src/metrics/ping.rs
+++ b/third_party/rust/glean-core/src/metrics/ping.rs
@@ -6,6 +6,7 @@ use std::fmt;
use std::sync::Arc;
use crate::ping::PingMaker;
+use crate::upload::PingPayload;
use crate::Glean;
use uuid::Uuid;
@@ -26,6 +27,8 @@ struct InnerPing {
pub send_if_empty: bool,
/// Whether to use millisecond-precise start/end times.
pub precise_timestamps: bool,
+ /// Whether to include the {client|ping}_info sections on assembly.
+ pub include_info_sections: bool,
/// The "reason" codes that this ping can send
pub reason_codes: Vec<String>,
}
@@ -37,6 +40,7 @@ impl fmt::Debug for PingType {
.field("include_client_id", &self.0.include_client_id)
.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("reason_codes", &self.0.reason_codes)
.finish()
}
@@ -61,6 +65,7 @@ impl PingType {
include_client_id: bool,
send_if_empty: bool,
precise_timestamps: bool,
+ include_info_sections: bool,
reason_codes: Vec<String>,
) -> Self {
let this = Self(Arc::new(InnerPing {
@@ -68,6 +73,7 @@ impl PingType {
include_client_id,
send_if_empty,
precise_timestamps,
+ include_info_sections,
reason_codes,
}));
@@ -94,6 +100,10 @@ impl PingType {
self.0.precise_timestamps
}
+ pub(crate) fn include_info_sections(&self) -> bool {
+ self.0.include_info_sections
+ }
+
/// Submits the ping for eventual uploading.
///
/// The ping content is assembled as soon as possible, but upload is not
@@ -186,13 +196,17 @@ impl PingType {
// so both scenarios should be impossible.
let content =
::serde_json::to_string(&ping.content).expect("ping serialization failed");
- glean.upload_manager.enqueue_ping(
- glean,
- ping.doc_id,
- ping.url_path,
- &content,
- Some(ping.headers),
- );
+ // TODO: Shouldn't we consolidate on a single collected Ping representation?
+ let ping = PingPayload {
+ document_id: ping.doc_id.to_string(),
+ upload_path: ping.url_path.to_string(),
+ json_body: content,
+ headers: Some(ping.headers),
+ body_has_info_sections: self.0.include_info_sections,
+ ping_name: self.0.name.to_string(),
+ };
+
+ glean.upload_manager.enqueue_ping(glean, ping);
return true;
}
diff --git a/third_party/rust/glean-core/src/metrics/quantity.rs b/third_party/rust/glean-core/src/metrics/quantity.rs
index c59d3a4a21..92216625d6 100644
--- a/third_party/rust/glean-core/src/metrics/quantity.rs
+++ b/third_party/rust/glean-core/src/metrics/quantity.rs
@@ -98,6 +98,15 @@ impl QuantityMetric {
/// Gets the currently stored value as an integer.
///
/// This doesn't clear the stored value.
+ ///
+ /// # Arguments
+ ///
+ /// * `ping_name` - the optional name of the ping to retrieve the metric
+ /// for. Defaults to the first value in `send_in_pings`.
+ ///
+ /// # Returns
+ ///
+ /// The stored value or `None` if nothing stored.
pub fn test_get_value(&self, ping_name: Option<String>) -> Option<i64> {
crate::block_on_dispatcher();
crate::core::with_glean(|glean| self.get_value(glean, ping_name.as_deref()))
@@ -110,8 +119,6 @@ impl QuantityMetric {
/// # 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
///
diff --git a/third_party/rust/glean-core/src/metrics/rate.rs b/third_party/rust/glean-core/src/metrics/rate.rs
index ba7f085b55..843d35002e 100644
--- a/third_party/rust/glean-core/src/metrics/rate.rs
+++ b/third_party/rust/glean-core/src/metrics/rate.rs
@@ -141,6 +141,15 @@ impl RateMetric {
/// Gets the currently stored value as a pair of integers.
///
/// This doesn't clear the stored value.
+ ///
+ /// # Arguments
+ ///
+ /// * `ping_name` - the optional name of the ping to retrieve the metric
+ /// for. Defaults to the first value in `send_in_pings`.
+ ///
+ /// # Returns
+ ///
+ /// The stored value or `None` if nothing stored.
pub fn test_get_value(&self, ping_name: Option<String>) -> Option<Rate> {
crate::block_on_dispatcher();
crate::core::with_glean(|glean| self.get_value(glean, ping_name.as_deref()))
@@ -175,8 +184,6 @@ impl RateMetric {
/// # 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
///
diff --git a/third_party/rust/glean-core/src/metrics/string.rs b/third_party/rust/glean-core/src/metrics/string.rs
index 5ed7b2c7f1..4aa30a8d7e 100644
--- a/third_party/rust/glean-core/src/metrics/string.rs
+++ b/third_party/rust/glean-core/src/metrics/string.rs
@@ -112,6 +112,15 @@ impl StringMetric {
/// Gets the currently stored value as a string.
///
/// This doesn't clear the stored value.
+ ///
+ /// # Arguments
+ ///
+ /// * `ping_name` - the optional name of the ping to retrieve the metric
+ /// for. Defaults to the first value in `send_in_pings`.
+ ///
+ /// # Returns
+ ///
+ /// The stored value or `None` if nothing stored.
pub fn test_get_value(&self, ping_name: Option<String>) -> Option<String> {
crate::block_on_dispatcher();
crate::core::with_glean(|glean| self.get_value(glean, ping_name.as_deref()))
@@ -124,8 +133,6 @@ impl StringMetric {
/// # 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
///
diff --git a/third_party/rust/glean-core/src/metrics/string_list.rs b/third_party/rust/glean-core/src/metrics/string_list.rs
index 75b2df7f80..cd4e71b885 100644
--- a/third_party/rust/glean-core/src/metrics/string_list.rs
+++ b/third_party/rust/glean-core/src/metrics/string_list.rs
@@ -171,6 +171,15 @@ impl StringListMetric {
/// Gets the currently-stored values.
///
/// This doesn't clear the stored value.
+ ///
+ /// # Arguments
+ ///
+ /// * `ping_name` - the optional name of the ping to retrieve the metric
+ /// for. Defaults to the first value in `send_in_pings`.
+ ///
+ /// # Returns
+ ///
+ /// The stored value or `None` if nothing stored.
pub fn test_get_value(&self, ping_name: Option<String>) -> Option<Vec<String>> {
crate::block_on_dispatcher();
crate::core::with_glean(|glean| self.get_value(glean, ping_name.as_deref()))
@@ -183,8 +192,6 @@ impl StringListMetric {
/// # 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
///
diff --git a/third_party/rust/glean-core/src/metrics/text.rs b/third_party/rust/glean-core/src/metrics/text.rs
index 06ad5c0d78..baa8e88d75 100644
--- a/third_party/rust/glean-core/src/metrics/text.rs
+++ b/third_party/rust/glean-core/src/metrics/text.rs
@@ -116,6 +116,15 @@ impl TextMetric {
/// Gets the currently stored value as a string.
///
/// This doesn't clear the stored value.
+ ///
+ /// # Arguments
+ ///
+ /// * `ping_name` - the optional name of the ping to retrieve the metric
+ /// for. Defaults to the first value in `send_in_pings`.
+ ///
+ /// # Returns
+ ///
+ /// The stored value or `None` if nothing stored.
pub fn test_get_value(&self, ping_name: Option<String>) -> Option<String> {
crate::block_on_dispatcher();
crate::core::with_glean(|glean| self.get_value(glean, ping_name.as_deref()))
@@ -128,8 +137,6 @@ impl TextMetric {
/// # 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
///
diff --git a/third_party/rust/glean-core/src/metrics/timespan.rs b/third_party/rust/glean-core/src/metrics/timespan.rs
index b4d3bd5902..ee63fb52f8 100644
--- a/third_party/rust/glean-core/src/metrics/timespan.rs
+++ b/third_party/rust/glean-core/src/metrics/timespan.rs
@@ -253,6 +253,15 @@ impl TimespanMetric {
/// Gets the currently stored value as an integer.
///
/// This doesn't clear the stored value.
+ ///
+ /// # Arguments
+ ///
+ /// * `ping_name` - the optional name of the ping to retrieve the metric
+ /// for. Defaults to the first value in `send_in_pings`.
+ ///
+ /// # Returns
+ ///
+ /// The stored value or `None` if nothing stored.
pub fn test_get_value(&self, ping_name: Option<String>) -> Option<i64> {
crate::block_on_dispatcher();
crate::core::with_glean(|glean| {
@@ -292,8 +301,6 @@ impl TimespanMetric {
/// # 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
///
diff --git a/third_party/rust/glean-core/src/metrics/timing_distribution.rs b/third_party/rust/glean-core/src/metrics/timing_distribution.rs
index e339ef8882..3293be9518 100644
--- a/third_party/rust/glean-core/src/metrics/timing_distribution.rs
+++ b/third_party/rust/glean-core/src/metrics/timing_distribution.rs
@@ -293,7 +293,35 @@ impl TimingDistributionMetric {
/// are longer than `MAX_SAMPLE_TIME`.
pub fn accumulate_samples(&self, samples: Vec<i64>) {
let metric = self.clone();
- crate::launch_with_glean(move |glean| metric.accumulate_samples_sync(glean, samples))
+ crate::launch_with_glean(move |glean| metric.accumulate_samples_sync(glean, &samples))
+ }
+
+ /// Accumulates precisely one signed sample and appends it to the metric.
+ ///
+ /// Precludes the need for a collection in the most common use case.
+ ///
+ /// Sign is required so that the platform-specific code can provide us with
+ /// a 64 bit signed integer 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 sample is already in
+ /// the "unit" declared by the instance of the metric type (e.g. if the
+ /// instance this method was called on is using [`crate::TimeUnit::Second`], then
+ /// `sample` is assumed to be in that unit).
+ ///
+ /// # Arguments
+ ///
+ /// * `sample` - The singular sample to be recorded by the metric.
+ ///
+ /// ## Notes
+ ///
+ /// Discards any negative value and reports an [`ErrorType::InvalidValue`].
+ /// Reports an [`ErrorType::InvalidOverflow`] error if the sample is longer than
+ /// `MAX_SAMPLE_TIME`.
+ pub fn accumulate_single_sample(&self, sample: i64) {
+ let metric = self.clone();
+ crate::launch_with_glean(move |glean| metric.accumulate_samples_sync(glean, &[sample]))
}
/// **Test-only API (exported for testing purposes).**
@@ -301,7 +329,7 @@ impl TimingDistributionMetric {
///
/// Use [`accumulate_samples`](Self::accumulate_samples)
#[doc(hidden)]
- pub fn accumulate_samples_sync(&self, glean: &Glean, samples: Vec<i64>) {
+ pub fn accumulate_samples_sync(&self, glean: &Glean, samples: &[i64]) {
if !self.should_record(glean) {
return;
}
@@ -464,6 +492,15 @@ impl TimingDistributionMetric {
/// Gets the currently stored value as an integer.
///
/// This doesn't clear the stored value.
+ ///
+ /// # Arguments
+ ///
+ /// * `ping_name` - the optional name of the ping to retrieve the metric
+ /// for. Defaults to the first value in `send_in_pings`.
+ ///
+ /// # Returns
+ ///
+ /// The stored value or `None` if nothing stored.
pub fn test_get_value(&self, ping_name: Option<String>) -> Option<DistributionData> {
crate::block_on_dispatcher();
crate::core::with_glean(|glean| self.get_value(glean, ping_name.as_deref()))
@@ -476,8 +513,6 @@ impl TimingDistributionMetric {
/// # 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
///
diff --git a/third_party/rust/glean-core/src/metrics/url.rs b/third_party/rust/glean-core/src/metrics/url.rs
index c9eb824a3e..48b3f9e7ae 100644
--- a/third_party/rust/glean-core/src/metrics/url.rs
+++ b/third_party/rust/glean-core/src/metrics/url.rs
@@ -131,6 +131,15 @@ impl UrlMetric {
/// Gets the currently stored value as a string.
///
/// This doesn't clear the stored value.
+ ///
+ /// # Arguments
+ ///
+ /// * `ping_name` - the optional name of the ping to retrieve the metric
+ /// for. Defaults to the first value in `send_in_pings`.
+ ///
+ /// # Returns
+ ///
+ /// The stored value or `None` if nothing stored.
pub fn test_get_value(&self, ping_name: Option<String>) -> Option<String> {
crate::block_on_dispatcher();
crate::core::with_glean(|glean| self.get_value(glean, ping_name.as_deref()))
@@ -143,8 +152,6 @@ impl UrlMetric {
/// # 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
///
diff --git a/third_party/rust/glean-core/src/metrics/uuid.rs b/third_party/rust/glean-core/src/metrics/uuid.rs
index e78d15ad3b..77d0f82320 100644
--- a/third_party/rust/glean-core/src/metrics/uuid.rs
+++ b/third_party/rust/glean-core/src/metrics/uuid.rs
@@ -128,6 +128,15 @@ impl UuidMetric {
/// Gets the currently stored value as a string.
///
/// This doesn't clear the stored value.
+ ///
+ /// # Arguments
+ ///
+ /// * `ping_name` - the optional name of the ping to retrieve the metric
+ /// for. Defaults to the first value in `send_in_pings`.
+ ///
+ /// # Returns
+ ///
+ /// The stored value or `None` if nothing stored.
pub fn test_get_value(&self, ping_name: Option<String>) -> Option<String> {
crate::block_on_dispatcher();
crate::core::with_glean(|glean| {
@@ -143,8 +152,6 @@ impl UuidMetric {
/// # 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
///
diff --git a/third_party/rust/glean-core/src/ping/mod.rs b/third_party/rust/glean-core/src/ping/mod.rs
index c22a890aa2..d1a67ae360 100644
--- a/third_party/rust/glean-core/src/ping/mod.rs
+++ b/third_party/rust/glean-core/src/ping/mod.rs
@@ -14,7 +14,7 @@ use serde_json::{json, Value as JsonValue};
use crate::common_metric_data::{CommonMetricData, Lifetime};
use crate::metrics::{CounterMetric, DatetimeMetric, Metric, MetricType, PingType, TimeUnit};
use crate::storage::{StorageManager, INTERNAL_STORAGE};
-use crate::upload::HeaderMap;
+use crate::upload::{HeaderMap, PingMetadata};
use crate::util::{get_iso_time_string, local_now_with_offset};
use crate::{Glean, Result, DELETION_REQUEST_PINGS_DIRECTORY, PENDING_PINGS_DIRECTORY};
@@ -30,6 +30,8 @@ pub struct Ping<'a> {
pub content: JsonValue,
/// The headers to upload with the payload.
pub headers: HeaderMap,
+ /// Whether the content contains {client|ping}_info sections.
+ pub includes_info_sections: bool,
}
/// Collect a ping's data, assemble it into its full payload and store it on disk.
@@ -237,9 +239,9 @@ impl PingMaker {
.snapshot_as_json(glean, ping.name(), true);
// Due to the way the experimentation identifier could link datasets that are intentionally unlinked,
- // it will not be included in pings that specifically exclude the Glean client-id and those pings that
- // should not be sent if empty.
- if (!ping.include_client_id() || !ping.send_if_empty())
+ // it will not be included in pings that specifically exclude the Glean client-id, those pings that
+ // should not be sent if empty, or pings that exclude the {client|ping}_info sections wholesale.
+ if (!ping.include_client_id() || !ping.send_if_empty() || !ping.include_info_sections())
&& glean.test_get_experimentation_id().is_some()
&& metrics_data.is_some()
{
@@ -285,13 +287,18 @@ impl PingMaker {
TimeUnit::Minute
};
- let ping_info = self.get_ping_info(glean, ping.name(), reason, precision);
- let client_info = self.get_client_info(glean, ping.include_client_id());
+ let mut json = if ping.include_info_sections() {
+ let ping_info = self.get_ping_info(glean, ping.name(), reason, precision);
+ let client_info = self.get_client_info(glean, ping.include_client_id());
+
+ json!({
+ "ping_info": ping_info,
+ "client_info": client_info
+ })
+ } else {
+ json!({})
+ };
- let mut json = json!({
- "ping_info": ping_info,
- "client_info": client_info
- });
let json_obj = json.as_object_mut()?;
if let Some(metrics_data) = metrics_data {
json_obj.insert("metrics".to_string(), metrics_data);
@@ -306,6 +313,7 @@ impl PingMaker {
doc_id,
url_path,
headers: self.get_headers(glean),
+ includes_info_sections: ping.include_info_sections(),
})
}
@@ -355,11 +363,17 @@ impl PingMaker {
file.write_all(ping.url_path.as_bytes())?;
file.write_all(b"\n")?;
file.write_all(::serde_json::to_string(&ping.content)?.as_bytes())?;
- if !ping.headers.is_empty() {
- file.write_all(b"\n{\"headers\":")?;
- file.write_all(::serde_json::to_string(&ping.headers)?.as_bytes())?;
- file.write_all(b"}")?;
- }
+ file.write_all(b"\n")?;
+ let metadata = PingMetadata {
+ // We don't actually need to clone the headers except to match PingMetadata's ownership.
+ // But since we're going to write a file to disk in a sec,
+ // and HeaderMaps tend to have only like two things in them, tops,
+ // the cost is bearable.
+ headers: Some(ping.headers.clone()),
+ body_has_info_sections: Some(ping.includes_info_sections),
+ ping_name: Some(ping.name.to_string()),
+ };
+ file.write_all(::serde_json::to_string(&metadata)?.as_bytes())?;
}
if let Err(e) = std::fs::rename(&temp_ping_path, &ping_path) {
diff --git a/third_party/rust/glean-core/src/traits/custom_distribution.rs b/third_party/rust/glean-core/src/traits/custom_distribution.rs
index c0c80c028b..43dfdb7da8 100644
--- a/third_party/rust/glean-core/src/traits/custom_distribution.rs
+++ b/third_party/rust/glean-core/src/traits/custom_distribution.rs
@@ -28,6 +28,22 @@ pub trait CustomDistribution {
/// them.
fn accumulate_samples_signed(&self, samples: Vec<i64>);
+ /// Accumulates precisely one signed sample in the metric.
+ ///
+ /// This is required so that the platform-specific code can provide us with a
+ /// 64 bit signed integer if no `u64` comparable type is available. This
+ /// will take care of filtering and reporting errors.
+ ///
+ /// # Arguments
+ ///
+ /// - `sample` - The singular sample to be recorded by the metric.
+ ///
+ /// ## Notes
+ ///
+ /// Discards any negative value of `sample` and reports an
+ /// [`ErrorType::InvalidValue`](crate::ErrorType::InvalidValue).
+ fn accumulate_single_sample_signed(&self, sample: i64);
+
/// **Exported for test purposes.**
///
/// Gets the currently stored histogram.
diff --git a/third_party/rust/glean-core/src/traits/mod.rs b/third_party/rust/glean-core/src/traits/mod.rs
index c4bcf7cdd6..4115609fdd 100644
--- a/third_party/rust/glean-core/src/traits/mod.rs
+++ b/third_party/rust/glean-core/src/traits/mod.rs
@@ -7,6 +7,10 @@
//! Individual metric types implement this trait to expose the specific metrics API.
//! It can be used by wrapping implementations to guarantee API conformance.
+/// Re-export for use in generated code.
+#[doc(hidden)]
+pub extern crate serde as __serde;
+
mod boolean;
mod counter;
mod custom_distribution;
@@ -15,6 +19,7 @@ mod event;
mod labeled;
mod memory_distribution;
mod numerator;
+mod object;
mod ping;
mod quantity;
mod rate;
@@ -37,6 +42,7 @@ pub use self::event::NoExtraKeys;
pub use self::labeled::Labeled;
pub use self::memory_distribution::MemoryDistribution;
pub use self::numerator::Numerator;
+pub use self::object::{ObjectError, ObjectSerialize};
pub use self::ping::Ping;
pub use self::quantity::Quantity;
pub use self::rate::Rate;
diff --git a/third_party/rust/glean-core/src/traits/object.rs b/third_party/rust/glean-core/src/traits/object.rs
new file mode 100644
index 0000000000..c579efeac7
--- /dev/null
+++ b/third_party/rust/glean-core/src/traits/object.rs
@@ -0,0 +1,53 @@
+// 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 std::fmt::Display;
+
+use serde::{Deserialize, Serialize};
+use serde_json::Value as JsonValue;
+
+/// This type represents all possible errors that can occur when serializing or deserializing an object from/to JSON.
+#[derive(Debug)]
+pub struct ObjectError(serde_json::Error);
+
+impl Display for ObjectError {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ Display::fmt(&self.0, f)
+ }
+}
+
+impl std::error::Error for ObjectError {}
+
+/// An object that can be serialized into JSON.
+///
+/// Objects are defined by their structure in the metrics definition.
+///
+/// This is essentially a wrapper around serde's `Serialize`/`Deserialize`,
+/// but in a way we can name it for our JSON (de)serialization.
+pub trait ObjectSerialize {
+ /// Deserialize the object from its JSON representation.
+ ///
+ /// Returns an error if deserialization fails.
+ /// This should not happen for glean_parser-generated and later serialized objects.
+ fn from_str(obj: &str) -> Result<Self, ObjectError>
+ where
+ Self: Sized;
+
+ /// Serialize this object into a JSON string.
+ fn into_serialized_object(self) -> Result<JsonValue, ObjectError>;
+}
+
+impl<V> ObjectSerialize for V
+where
+ V: Serialize,
+ V: for<'de> Deserialize<'de>,
+{
+ fn from_str(obj: &str) -> Result<Self, ObjectError> {
+ serde_json::from_str(obj).map_err(ObjectError)
+ }
+
+ fn into_serialized_object(self) -> Result<JsonValue, ObjectError> {
+ serde_json::to_value(self).map_err(ObjectError)
+ }
+}
diff --git a/third_party/rust/glean-core/src/traits/timing_distribution.rs b/third_party/rust/glean-core/src/traits/timing_distribution.rs
index 03083753c6..ba618d2b4b 100644
--- a/third_party/rust/glean-core/src/traits/timing_distribution.rs
+++ b/third_party/rust/glean-core/src/traits/timing_distribution.rs
@@ -66,6 +66,31 @@ pub trait TimingDistribution {
/// are longer than `MAX_SAMPLE_TIME`.
fn accumulate_samples(&self, samples: Vec<i64>);
+ /// Accumulates precisely one signed sample in the metric.
+ ///
+ /// Precludes the need for a collection in the most common use case.
+ ///
+ /// Sign is required so that the platform-specific code can provide us with
+ /// a 64 bit signed integer 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 sample is already in
+ /// the "unit" declared by the instance of the metric type (e.g. if the
+ /// instance this method was called on is using [`crate::TimeUnit::Second`], then
+ /// `sample` is assumed to be in that unit).
+ ///
+ /// # Arguments
+ ///
+ /// * `sample` - The singular sample to be recorded by the metric.
+ ///
+ /// ## Notes
+ ///
+ /// Discards any negative value and reports an [`ErrorType::InvalidValue`].
+ /// Reports an [`ErrorType::InvalidOverflow`] error if the sample is longer than
+ /// `MAX_SAMPLE_TIME`.
+ fn accumulate_single_sample(&self, sample: i64);
+
/// Accumulates the provided samples in the metric.
///
/// # Arguments
diff --git a/third_party/rust/glean-core/src/upload/directory.rs b/third_party/rust/glean-core/src/upload/directory.rs
index a78bbf0bdb..706550fe6c 100644
--- a/third_party/rust/glean-core/src/upload/directory.rs
+++ b/third_party/rust/glean-core/src/upload/directory.rs
@@ -9,15 +9,28 @@ use std::fs::{self, File};
use std::io::{BufRead, BufReader};
use std::path::{Path, PathBuf};
-use serde::Deserialize;
+use serde::{Deserialize, Serialize};
use uuid::Uuid;
use super::request::HeaderMap;
use crate::{DELETION_REQUEST_PINGS_DIRECTORY, PENDING_PINGS_DIRECTORY};
/// A representation of the data extracted from a ping file,
-/// this will contain the document_id, path, JSON encoded body of a ping and the persisted headers.
-pub type PingPayload = (String, String, String, Option<HeaderMap>);
+#[derive(Clone, Debug, Default)]
+pub struct PingPayload {
+ /// The ping's doc_id.
+ pub document_id: String,
+ /// The path to upload the ping to.
+ pub upload_path: String,
+ /// The ping body as JSON-encoded string.
+ pub json_body: String,
+ /// HTTP headers to include in the upload request.
+ pub headers: Option<HeaderMap>,
+ /// Whether the ping body contains {client|ping}_info
+ pub body_has_info_sections: bool,
+ /// The ping's name. (Also likely in the upload_path.)
+ pub ping_name: String,
+}
/// A struct to hold the result of scanning all pings directories.
#[derive(Clone, Debug, Default)]
@@ -62,20 +75,28 @@ fn get_file_name_as_str(path: &Path) -> Option<&str> {
}
}
+/// A ping's metadata, as (optionally) represented on disk.
+///
+/// Anything that isn't the upload path or the ping body.
+#[derive(Default, Deserialize, Serialize)]
+pub struct PingMetadata {
+ /// HTTP headers to include when uploading the ping.
+ pub headers: Option<HeaderMap>,
+ /// Whether the body has {client|ping}_info sections.
+ pub body_has_info_sections: Option<bool>,
+ /// The name of the ping.
+ pub ping_name: Option<String>,
+}
+
/// Processes a ping's metadata.
///
/// The metadata is an optional third line in the ping file,
/// currently it contains only additonal headers to be added to each ping request.
/// Therefore, we will process the contents of this line
/// and return a HeaderMap of the persisted headers.
-fn process_metadata(path: &str, metadata: &str) -> Option<HeaderMap> {
- #[derive(Deserialize)]
- struct PingMetadata {
- pub headers: HeaderMap,
- }
-
+fn process_metadata(path: &str, metadata: &str) -> Option<PingMetadata> {
if let Ok(metadata) = serde_json::from_str::<PingMetadata>(metadata) {
- return Some(metadata.headers);
+ return Some(metadata);
} else {
log::warn!("Error while parsing ping metadata: {}", path);
}
@@ -171,8 +192,23 @@ impl PingDirectoryManager {
if let (Some(Ok(path)), Some(Ok(body)), Ok(metadata)) =
(lines.next(), lines.next(), lines.next().transpose())
{
- let headers = metadata.and_then(|m| process_metadata(&path, &m));
- return Some((document_id.into(), path, body, headers));
+ let PingMetadata {
+ headers,
+ body_has_info_sections,
+ ping_name,
+ } = metadata
+ .and_then(|m| process_metadata(&path, &m))
+ .unwrap_or_default();
+ let ping_name =
+ ping_name.unwrap_or_else(|| path.split('/').nth(3).unwrap_or("").into());
+ return Some(PingPayload {
+ document_id: document_id.into(),
+ upload_path: path,
+ json_body: body,
+ headers,
+ body_has_info_sections: body_has_info_sections.unwrap_or(true),
+ ping_name,
+ });
} else {
log::warn!(
"Error processing ping file: {}. Ping file is not formatted as expected.",
@@ -303,7 +339,7 @@ mod test {
let (mut glean, dir) = new_glean(None);
// Register a ping for testing
- let ping_type = PingType::new("test", true, true, true, vec![]);
+ let ping_type = PingType::new("test", true, true, true, true, vec![]);
glean.register_ping_type(&ping_type);
// Submit the ping to populate the pending_pings directory
@@ -320,7 +356,8 @@ mod test {
// Verify request was returned for the "test" ping
let ping = &data.pending_pings[0].1;
- let request_ping_type = ping.1.split('/').nth(3).unwrap();
+ let request_ping_type = ping.upload_path.split('/').nth(3).unwrap();
+ assert_eq!(request_ping_type, ping.ping_name);
assert_eq!(request_ping_type, "test");
}
@@ -329,7 +366,7 @@ mod test {
let (mut glean, dir) = new_glean(None);
// Register a ping for testing
- let ping_type = PingType::new("test", true, true, true, vec![]);
+ let ping_type = PingType::new("test", true, true, true, true, vec![]);
glean.register_ping_type(&ping_type);
// Submit the ping to populate the pending_pings directory
@@ -352,7 +389,8 @@ mod test {
// Verify request was returned for the "test" ping
let ping = &data.pending_pings[0].1;
- let request_ping_type = ping.1.split('/').nth(3).unwrap();
+ let request_ping_type = ping.upload_path.split('/').nth(3).unwrap();
+ assert_eq!(request_ping_type, ping.ping_name);
assert_eq!(request_ping_type, "test");
// Verify that file was indeed deleted
@@ -364,7 +402,7 @@ mod test {
let (mut glean, dir) = new_glean(None);
// Register a ping for testing
- let ping_type = PingType::new("test", true, true, true, vec![]);
+ let ping_type = PingType::new("test", true, true, true, true, vec![]);
glean.register_ping_type(&ping_type);
// Submit the ping to populate the pending_pings directory
@@ -387,7 +425,8 @@ mod test {
// Verify request was returned for the "test" ping
let ping = &data.pending_pings[0].1;
- let request_ping_type = ping.1.split('/').nth(3).unwrap();
+ let request_ping_type = ping.upload_path.split('/').nth(3).unwrap();
+ assert_eq!(request_ping_type, ping.ping_name);
assert_eq!(request_ping_type, "test");
// Verify that file was indeed deleted
@@ -414,7 +453,8 @@ mod test {
// Verify request was returned for the "deletion-request" ping
let ping = &data.deletion_request_pings[0].1;
- let request_ping_type = ping.1.split('/').nth(3).unwrap();
+ let request_ping_type = ping.upload_path.split('/').nth(3).unwrap();
+ assert_eq!(request_ping_type, ping.ping_name);
assert_eq!(request_ping_type, "deletion-request");
}
}
diff --git a/third_party/rust/glean-core/src/upload/mod.rs b/third_party/rust/glean-core/src/upload/mod.rs
index d764dcd29e..e51a9d9508 100644
--- a/third_party/rust/glean-core/src/upload/mod.rs
+++ b/third_party/rust/glean-core/src/upload/mod.rs
@@ -30,6 +30,7 @@ use directory::{PingDirectoryManager, PingPayloadsByDirectory};
use policy::Policy;
use request::create_date_header_value;
+pub use directory::{PingMetadata, PingPayload};
pub use request::{HeaderMap, PingRequest};
pub use result::{UploadResult, UploadTaskAction};
@@ -322,21 +323,24 @@ impl PingUploadManager {
///
/// Returns the `PingRequest` or `None` if unable to build,
/// in which case it will delete the ping file and record an error.
- fn build_ping_request(
- &self,
- glean: &Glean,
- document_id: &str,
- path: &str,
- body: &str,
- headers: Option<HeaderMap>,
- ) -> Option<PingRequest> {
+ fn build_ping_request(&self, glean: &Glean, ping: PingPayload) -> Option<PingRequest> {
+ let PingPayload {
+ document_id,
+ upload_path: path,
+ json_body: body,
+ headers,
+ body_has_info_sections,
+ ping_name,
+ } = ping;
let mut request = PingRequest::builder(
&self.language_binding_name,
self.policy.max_ping_body_size(),
)
- .document_id(document_id)
+ .document_id(&document_id)
.path(path)
- .body(body);
+ .body(body)
+ .body_has_info_sections(body_has_info_sections)
+ .ping_name(ping_name);
if let Some(headers) = headers {
request = request.headers(headers);
@@ -346,7 +350,7 @@ impl PingUploadManager {
Ok(request) => Some(request),
Err(e) => {
log::warn!("Error trying to build ping request: {}", e);
- self.directory_manager.delete_file(document_id);
+ self.directory_manager.delete_file(&document_id);
// Record the error.
// Currently the only possible error is PingBodyOverflow.
@@ -362,23 +366,21 @@ impl PingUploadManager {
}
/// Enqueue a ping for upload.
- pub fn enqueue_ping(
- &self,
- glean: &Glean,
- document_id: &str,
- path: &str,
- body: &str,
- headers: Option<HeaderMap>,
- ) {
+ pub fn enqueue_ping(&self, glean: &Glean, ping: PingPayload) {
let mut queue = self
.queue
.write()
.expect("Can't write to pending pings queue.");
+ let PingPayload {
+ ref document_id,
+ upload_path: ref path,
+ ..
+ } = ping;
// Checks if a ping with this `document_id` is already enqueued.
if queue
.iter()
- .any(|request| request.document_id == document_id)
+ .any(|request| request.document_id.as_str() == document_id)
{
log::warn!(
"Attempted to enqueue a duplicate ping {} at {}.",
@@ -404,7 +406,7 @@ impl PingUploadManager {
}
log::trace!("Enqueuing ping {} at {}", document_id, path);
- if let Some(request) = self.build_ping_request(glean, document_id, path, body, headers) {
+ if let Some(request) = self.build_ping_request(glean, ping) {
queue.push_back(request)
}
}
@@ -455,7 +457,7 @@ impl PingUploadManager {
// Thus, we reverse the order of the pending pings vector,
// so that we iterate in descending order (newest -> oldest).
cached_pings.pending_pings.reverse();
- cached_pings.pending_pings.retain(|(file_size, (document_id, _, _, _))| {
+ cached_pings.pending_pings.retain(|(file_size, PingPayload {document_id, ..})| {
pending_pings_count += 1;
pending_pings_directory_size += file_size;
@@ -493,14 +495,14 @@ impl PingUploadManager {
// Enqueue the remaining pending pings and
// enqueue all deletion-request pings.
- let deletion_request_pings = cached_pings.deletion_request_pings.drain(..);
- for (_, (document_id, path, body, headers)) in deletion_request_pings {
- self.enqueue_ping(glean, &document_id, &path, &body, headers);
- }
- let pending_pings = cached_pings.pending_pings.drain(..);
- for (_, (document_id, path, body, headers)) in pending_pings {
- self.enqueue_ping(glean, &document_id, &path, &body, headers);
- }
+ cached_pings
+ .deletion_request_pings
+ .drain(..)
+ .for_each(|(_, ping)| self.enqueue_ping(glean, ping));
+ cached_pings
+ .pending_pings
+ .drain(..)
+ .for_each(|(_, ping)| self.enqueue_ping(glean, ping));
}
}
@@ -532,10 +534,8 @@ impl PingUploadManager {
/// * `glean` - The Glean object holding the database.
/// * `document_id` - The UUID of the ping in question.
pub fn enqueue_ping_from_file(&self, glean: &Glean, document_id: &str) {
- if let Some((doc_id, path, body, headers)) =
- self.directory_manager.process_file(document_id)
- {
- self.enqueue_ping(glean, &doc_id, &path, &body, headers)
+ if let Some(ping) = self.directory_manager.process_file(document_id) {
+ self.enqueue_ping(glean, ping);
}
}
@@ -883,7 +883,17 @@ mod test {
let upload_manager = PingUploadManager::no_policy(dir.path());
// Enqueue a ping
- upload_manager.enqueue_ping(&glean, &Uuid::new_v4().to_string(), PATH, "", None);
+ upload_manager.enqueue_ping(
+ &glean,
+ PingPayload {
+ document_id: Uuid::new_v4().to_string(),
+ upload_path: PATH.into(),
+ json_body: "".into(),
+ headers: None,
+ body_has_info_sections: true,
+ ping_name: "ping-name".into(),
+ },
+ );
// Try and get the next request.
// Verify request was returned
@@ -900,7 +910,17 @@ mod test {
// Enqueue a ping multiple times
let n = 10;
for _ in 0..n {
- upload_manager.enqueue_ping(&glean, &Uuid::new_v4().to_string(), PATH, "", None);
+ upload_manager.enqueue_ping(
+ &glean,
+ PingPayload {
+ document_id: Uuid::new_v4().to_string(),
+ upload_path: PATH.into(),
+ json_body: "".into(),
+ headers: None,
+ body_has_info_sections: true,
+ ping_name: "ping-name".into(),
+ },
+ );
}
// Verify a request is returned for each submitted ping
@@ -928,7 +948,17 @@ mod test {
// Enqueue the max number of pings allowed per uploading window
for _ in 0..max_pings_per_interval {
- upload_manager.enqueue_ping(&glean, &Uuid::new_v4().to_string(), PATH, "", None);
+ upload_manager.enqueue_ping(
+ &glean,
+ PingPayload {
+ document_id: Uuid::new_v4().to_string(),
+ upload_path: PATH.into(),
+ json_body: "".into(),
+ headers: None,
+ body_has_info_sections: true,
+ ping_name: "ping-name".into(),
+ },
+ );
}
// Verify a request is returned for each submitted ping
@@ -938,7 +968,17 @@ mod test {
}
// Enqueue just one more ping
- upload_manager.enqueue_ping(&glean, &Uuid::new_v4().to_string(), PATH, "", None);
+ upload_manager.enqueue_ping(
+ &glean,
+ PingPayload {
+ document_id: Uuid::new_v4().to_string(),
+ upload_path: PATH.into(),
+ json_body: "".into(),
+ headers: None,
+ body_has_info_sections: true,
+ ping_name: "ping-name".into(),
+ },
+ );
// Verify that we are indeed told to wait because we are at capacity
match upload_manager.get_upload_task(&glean, false) {
@@ -961,7 +1001,17 @@ mod test {
// Enqueue a ping multiple times
for _ in 0..10 {
- upload_manager.enqueue_ping(&glean, &Uuid::new_v4().to_string(), PATH, "", None);
+ upload_manager.enqueue_ping(
+ &glean,
+ PingPayload {
+ document_id: Uuid::new_v4().to_string(),
+ upload_path: PATH.into(),
+ json_body: "".into(),
+ headers: None,
+ body_has_info_sections: true,
+ ping_name: "ping-name".into(),
+ },
+ );
}
// Clear the queue
@@ -979,7 +1029,14 @@ mod test {
let (mut glean, _t) = new_glean(None);
// Register a ping for testing
- let ping_type = PingType::new("test", true, /* send_if_empty */ true, true, vec![]);
+ let ping_type = PingType::new(
+ "test",
+ true,
+ /* send_if_empty */ true,
+ true,
+ true,
+ vec![],
+ );
glean.register_ping_type(&ping_type);
// Submit the ping multiple times
@@ -1011,7 +1068,14 @@ mod test {
let (mut glean, dir) = new_glean(None);
// Register a ping for testing
- let ping_type = PingType::new("test", true, /* send_if_empty */ true, true, vec![]);
+ let ping_type = PingType::new(
+ "test",
+ true,
+ /* send_if_empty */ true,
+ true,
+ true,
+ vec![],
+ );
glean.register_ping_type(&ping_type);
// Submit the ping multiple times
@@ -1041,7 +1105,14 @@ mod test {
let (mut glean, dir) = new_glean(None);
// Register a ping for testing
- let ping_type = PingType::new("test", true, /* send_if_empty */ true, true, vec![]);
+ let ping_type = PingType::new(
+ "test",
+ true,
+ /* send_if_empty */ true,
+ true,
+ true,
+ vec![],
+ );
glean.register_ping_type(&ping_type);
// Submit a ping
@@ -1071,7 +1142,14 @@ mod test {
let (mut glean, dir) = new_glean(None);
// Register a ping for testing
- let ping_type = PingType::new("test", true, /* send_if_empty */ true, true, vec![]);
+ let ping_type = PingType::new(
+ "test",
+ true,
+ /* send_if_empty */ true,
+ true,
+ true,
+ vec![],
+ );
glean.register_ping_type(&ping_type);
// Submit a ping
@@ -1101,7 +1179,14 @@ mod test {
let (mut glean, _t) = new_glean(None);
// Register a ping for testing
- let ping_type = PingType::new("test", true, /* send_if_empty */ true, true, vec![]);
+ let ping_type = PingType::new(
+ "test",
+ true,
+ /* send_if_empty */ true,
+ true,
+ true,
+ vec![],
+ );
glean.register_ping_type(&ping_type);
// Submit a ping
@@ -1133,7 +1218,14 @@ mod test {
let (mut glean, dir) = new_glean(None);
// Register a ping for testing
- let ping_type = PingType::new("test", true, /* send_if_empty */ true, true, vec![]);
+ let ping_type = PingType::new(
+ "test",
+ true,
+ /* send_if_empty */ true,
+ true,
+ true,
+ vec![],
+ );
glean.register_ping_type(&ping_type);
// Submit a ping
@@ -1174,7 +1266,17 @@ mod test {
let path2 = format!("/submit/app_id/test-ping/1/{}", doc2);
// Enqueue a ping
- upload_manager.enqueue_ping(&glean, &doc1, &path1, "", None);
+ upload_manager.enqueue_ping(
+ &glean,
+ PingPayload {
+ document_id: doc1.clone(),
+ upload_path: path1,
+ json_body: "".into(),
+ headers: None,
+ body_has_info_sections: true,
+ ping_name: "test-ping".into(),
+ },
+ );
// Try and get the first request.
let req = match upload_manager.get_upload_task(&glean, false) {
@@ -1184,7 +1286,17 @@ mod test {
assert_eq!(doc1, req.document_id);
// Schedule the next one while the first one is "in progress"
- upload_manager.enqueue_ping(&glean, &doc2, &path2, "", None);
+ upload_manager.enqueue_ping(
+ &glean,
+ PingPayload {
+ document_id: doc2.clone(),
+ upload_path: path2,
+ json_body: "".into(),
+ headers: None,
+ body_has_info_sections: true,
+ ping_name: "test-ping".into(),
+ },
+ );
// Mark as processed
upload_manager.process_ping_upload_response(
@@ -1221,7 +1333,14 @@ mod test {
glean.set_debug_view_tag("valid-tag");
// Register a ping for testing
- let ping_type = PingType::new("test", true, /* send_if_empty */ true, true, vec![]);
+ let ping_type = PingType::new(
+ "test",
+ true,
+ /* send_if_empty */ true,
+ true,
+ true,
+ vec![],
+ );
glean.register_ping_type(&ping_type);
// Submit a ping
@@ -1248,8 +1367,28 @@ mod test {
let path = format!("/submit/app_id/test-ping/1/{}", doc_id);
// Try to enqueue a ping with the same doc_id twice
- upload_manager.enqueue_ping(&glean, &doc_id, &path, "", None);
- upload_manager.enqueue_ping(&glean, &doc_id, &path, "", None);
+ upload_manager.enqueue_ping(
+ &glean,
+ PingPayload {
+ document_id: doc_id.clone(),
+ upload_path: path.clone(),
+ json_body: "".into(),
+ headers: None,
+ body_has_info_sections: true,
+ ping_name: "test-ping".into(),
+ },
+ );
+ upload_manager.enqueue_ping(
+ &glean,
+ PingPayload {
+ document_id: doc_id,
+ upload_path: path,
+ json_body: "".into(),
+ headers: None,
+ body_has_info_sections: true,
+ ping_name: "test-ping".into(),
+ },
+ );
// Get a task once
let task = upload_manager.get_upload_task(&glean, false);
@@ -1267,7 +1406,14 @@ mod test {
let (mut glean, dir) = new_glean(None);
// Register a ping for testing
- let ping_type = PingType::new("test", true, /* send_if_empty */ true, true, vec![]);
+ let ping_type = PingType::new(
+ "test",
+ true,
+ /* send_if_empty */ true,
+ true,
+ true,
+ vec![],
+ );
glean.register_ping_type(&ping_type);
// Submit the ping multiple times
@@ -1317,7 +1463,14 @@ mod test {
let (mut glean, dir) = new_glean(None);
// Register a ping for testing
- let ping_type = PingType::new("test", true, /* send_if_empty */ true, true, vec![]);
+ let ping_type = PingType::new(
+ "test",
+ true,
+ /* send_if_empty */ true,
+ true,
+ true,
+ vec![],
+ );
glean.register_ping_type(&ping_type);
// Submit the ping multiple times
@@ -1331,7 +1484,10 @@ mod test {
// The pending pings array is sorted by date in ascending order,
// the newest element is the last one.
let (_, newest_ping) = &pending_pings.last().unwrap();
- let (newest_ping_id, _, _, _) = &newest_ping;
+ let PingPayload {
+ document_id: newest_ping_id,
+ ..
+ } = &newest_ping;
// Create a new upload manager pointing to the same data_path as the glean instance.
let mut upload_manager = PingUploadManager::no_policy(dir.path());
@@ -1385,7 +1541,14 @@ mod test {
let (mut glean, dir) = new_glean(None);
// Register a ping for testing
- let ping_type = PingType::new("test", true, /* send_if_empty */ true, true, vec![]);
+ let ping_type = PingType::new(
+ "test",
+ true,
+ /* send_if_empty */ true,
+ true,
+ true,
+ vec![],
+ );
glean.register_ping_type(&ping_type);
// How many pings we allow at maximum
@@ -1406,7 +1569,7 @@ mod test {
.iter()
.rev()
.take(count_quota)
- .map(|(_, ping)| ping.0.clone())
+ .map(|(_, ping)| ping.document_id.clone())
.collect::<Vec<_>>();
// Create a new upload manager pointing to the same data_path as the glean instance.
@@ -1457,7 +1620,14 @@ mod test {
let (mut glean, dir) = new_glean(None);
// Register a ping for testing
- let ping_type = PingType::new("test", true, /* send_if_empty */ true, true, vec![]);
+ let ping_type = PingType::new(
+ "test",
+ true,
+ /* send_if_empty */ true,
+ true,
+ true,
+ vec![],
+ );
glean.register_ping_type(&ping_type);
let expected_number_of_pings = 3;
@@ -1477,7 +1647,7 @@ mod test {
.iter()
.rev()
.take(expected_number_of_pings)
- .map(|(_, ping)| ping.0.clone())
+ .map(|(_, ping)| ping.document_id.clone())
.collect::<Vec<_>>();
// Create a new upload manager pointing to the same data_path as the glean instance.
@@ -1531,7 +1701,14 @@ mod test {
let (mut glean, dir) = new_glean(None);
// Register a ping for testing
- let ping_type = PingType::new("test", true, /* send_if_empty */ true, true, vec![]);
+ let ping_type = PingType::new(
+ "test",
+ true,
+ /* send_if_empty */ true,
+ true,
+ true,
+ vec![],
+ );
glean.register_ping_type(&ping_type);
let expected_number_of_pings = 2;
@@ -1551,7 +1728,7 @@ mod test {
.iter()
.rev()
.take(expected_number_of_pings)
- .map(|(_, ping)| ping.0.clone())
+ .map(|(_, ping)| ping.document_id.clone())
.collect::<Vec<_>>();
// Create a new upload manager pointing to the same data_path as the glean instance.
@@ -1622,8 +1799,28 @@ mod test {
upload_manager.set_rate_limiter(secs_per_interval, max_pings_per_interval);
// Enqueue two pings
- upload_manager.enqueue_ping(&glean, &Uuid::new_v4().to_string(), PATH, "", None);
- upload_manager.enqueue_ping(&glean, &Uuid::new_v4().to_string(), PATH, "", None);
+ upload_manager.enqueue_ping(
+ &glean,
+ PingPayload {
+ document_id: Uuid::new_v4().to_string(),
+ upload_path: PATH.into(),
+ json_body: "".into(),
+ headers: None,
+ body_has_info_sections: true,
+ ping_name: "ping-name".into(),
+ },
+ );
+ upload_manager.enqueue_ping(
+ &glean,
+ PingPayload {
+ document_id: Uuid::new_v4().to_string(),
+ upload_path: PATH.into(),
+ json_body: "".into(),
+ headers: None,
+ body_has_info_sections: true,
+ ping_name: "ping-name".into(),
+ },
+ );
// Get the first ping, it should be returned normally.
match upload_manager.get_upload_task(&glean, false) {
@@ -1679,12 +1876,28 @@ mod test {
let upload_manager = PingUploadManager::no_policy(dir.path());
// Enqueue a ping and start processing it
- let identifier = &Uuid::new_v4().to_string();
- upload_manager.enqueue_ping(&glean, identifier, PATH, "", None);
+ let identifier = &Uuid::new_v4();
+ let ping = PingPayload {
+ document_id: identifier.to_string(),
+ upload_path: PATH.into(),
+ json_body: "".into(),
+ headers: None,
+ body_has_info_sections: true,
+ ping_name: "ping-name".into(),
+ };
+ upload_manager.enqueue_ping(&glean, ping);
assert!(upload_manager.get_upload_task(&glean, false).is_upload());
// Attempt to re-enqueue the same ping
- upload_manager.enqueue_ping(&glean, identifier, PATH, "", None);
+ let ping = PingPayload {
+ document_id: identifier.to_string(),
+ upload_path: PATH.into(),
+ json_body: "".into(),
+ headers: None,
+ body_has_info_sections: true,
+ ping_name: "ping-name".into(),
+ };
+ upload_manager.enqueue_ping(&glean, ping);
// No new pings should have been enqueued so the upload task is Done.
assert_eq!(
@@ -1695,7 +1908,7 @@ mod test {
// Process the upload response
upload_manager.process_ping_upload_response(
&glean,
- identifier,
+ &identifier.to_string(),
UploadResult::http_status(200),
);
}
diff --git a/third_party/rust/glean-core/src/upload/request.rs b/third_party/rust/glean-core/src/upload/request.rs
index 0fd5ec5713..b4ac6eba97 100644
--- a/third_party/rust/glean-core/src/upload/request.rs
+++ b/third_party/rust/glean-core/src/upload/request.rs
@@ -62,6 +62,8 @@ pub struct Builder {
body: Option<Vec<u8>>,
headers: HeaderMap,
body_max_size: usize,
+ body_has_info_sections: Option<bool>,
+ ping_name: Option<String>,
}
impl Builder {
@@ -87,6 +89,8 @@ impl Builder {
body: None,
headers,
body_max_size,
+ body_has_info_sections: None,
+ ping_name: None,
}
}
@@ -138,6 +142,18 @@ impl Builder {
self
}
+ /// Sets whether the request body has {client|ping}_info sections.
+ pub fn body_has_info_sections(mut self, body_has_info_sections: bool) -> Self {
+ self.body_has_info_sections = Some(body_has_info_sections);
+ self
+ }
+
+ /// Sets the ping's name aka doctype.
+ pub fn ping_name<S: Into<String>>(mut self, ping_name: S) -> Self {
+ self.ping_name = Some(ping_name.into());
+ self
+ }
+
/// Sets a header for this request.
pub fn header<S: Into<String>>(mut self, key: S, value: S) -> Self {
self.headers.insert(key.into(), value.into());
@@ -174,6 +190,12 @@ impl Builder {
.expect("path must be set before attempting to build PingRequest"),
body,
headers: self.headers,
+ body_has_info_sections: self.body_has_info_sections.expect(
+ "body_has_info_sections must be set before attempting to build PingRequest",
+ ),
+ ping_name: self
+ .ping_name
+ .expect("ping_name must be set before attempting to build PingRequest"),
})
}
}
@@ -192,6 +214,10 @@ pub struct PingRequest {
pub body: Vec<u8>,
/// A map with all the headers to be sent with the request.
pub headers: HeaderMap,
+ /// Whether the body has {client|ping}_info sections.
+ pub body_has_info_sections: bool,
+ /// The ping's name. Likely also somewhere in `path`.
+ pub ping_name: String,
}
impl PingRequest {
@@ -208,12 +234,7 @@ impl PingRequest {
/// Verifies if current request is for a deletion-request ping.
pub fn is_deletion_request(&self) -> bool {
- // The path format should be `/submit/<app_id>/<ping_name>/<schema_version/<doc_id>`
- self.path
- .split('/')
- .nth(3)
- .map(|url| url == "deletion-request")
- .unwrap_or(false)
+ self.ping_name == "deletion-request"
}
/// Decompresses and pretty-format the ping payload
@@ -257,11 +278,15 @@ mod test {
.document_id("woop")
.path("/random/path/doesnt/matter")
.body("{}")
+ .body_has_info_sections(false)
+ .ping_name("whatevs")
.build()
.unwrap();
assert_eq!(request.document_id, "woop");
assert_eq!(request.path, "/random/path/doesnt/matter");
+ assert!(!request.body_has_info_sections);
+ assert_eq!(request.ping_name, "whatevs");
// Make sure all the expected headers were added.
assert!(request.headers.contains_key("X-Telemetry-Agent"));