/* This Source Code Form is subject to the terms of the Mozilla Public *License,v.2.0.IfacopyoftheMPLwasnotdistributedwiththis
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
//! Manage recording sync telemetry. Assumes some external telemetry //! library/code which manages submitting.
// A test helper, used by the many test modules below. #[cfg(test)] fn assert_json<T>(v: &T, expected: serde_json::Value) where
T: serde::Serialize + ?Sized,
{
assert_eq!(
serde_json::to_value(v).expect("should get a value"),
expected
);
}
/// What we record for 'when' and 'took' in a telemetry record. #[derive(Debug, Serialize)] struct WhenTook {
when: f64, #[serde(skip_serializing_if = "crate::skip_if_default")]
took: u64,
}
/// What we track while recording 'when' and 'took. It serializes as a WhenTook, /// except when .finished() hasn't been called, in which case it panics. #[allow(dead_code)] #[derive(Debug)] enum Stopwatch {
Started(time::SystemTime, time::Instant),
Finished(WhenTook),
}
// For tests we don't want real timestamps because we test against literals. #[cfg(test)] fn finished(&self) -> Self {
Stopwatch::Finished(WhenTook { when: 0.0, took: 0 })
}
#[cfg(not(test))] fn finished(&self) -> Self { matchself {
Stopwatch::Started(st, si) => { let std = st.duration_since(time::UNIX_EPOCH).unwrap_or_default(); let when = std.as_secs() as f64; // we don't want sub-sec accuracy. Do we need to write a float?
let sid = si.elapsed(); let took = sid.as_secs() * 1000 + (u64::from(sid.subsec_nanos()) / 1_000_000);
Stopwatch::Finished(WhenTook { when, took })
}
_ => {
unreachable!("can't finish twice");
}
}
}
}
impl Serialize for Stopwatch { fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error> where
S: Serializer,
{ matchself {
Stopwatch::Started(_, _) => Err(ser::Error::custom("StopWatch has not been finished")),
Stopwatch::Finished(c) => c.serialize(serializer),
}
}
}
#[cfg(test)] mod stopwatch_tests { usesuper::*;
// A wrapper struct because we flatten - this struct should serialize with // 'when' and 'took' keys (but with no 'sw'.) #[derive(Debug, Serialize)] struct WT { #[serde(flatten)]
sw: Stopwatch,
}
#[test] fn test_not_finished() { let wt = WT {
sw: Stopwatch::new(),
};
serde_json::to_string(&wt).expect_err("unfinished stopwatch should fail");
}
/// A generic "Event" - suitable for all kinds of pings (although this module /// only cares about the sync ping) #[derive(Debug, Serialize)] pubstruct Event { // We use static str references as we expect values to be literals.
object: &'static str,
method: &'static str,
// Maybe "value" should be a string? #[serde(skip_serializing_if = "Option::is_none")]
value: Option<&'static str>,
// we expect the keys to be literals but values are real strings. #[serde(skip_serializing_if = "Option::is_none")]
extra: Option<HashMap<&'static str, String>>,
}
#[test] #[should_panic] fn test_invalid_length_ctor() {
Event::new("A very long object value", "Method");
}
#[test] #[should_panic] fn test_invalid_length_extra_key() {
Event::new("O", "M").extra("A very long key value", "v".to_string());
}
#[test] #[should_panic] fn test_invalid_length_extra_val() { let l = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
Event::new("O", "M").extra("k", l.to_string());
}
#[test] #[should_panic] fn test_too_many_extras() { let l = "abcdefghijk"; letmut e = Event::new("Object", "Method"); for i in0..l.len() {
e = e.extra(&l[i..=i], "v".to_string());
}
}
// A bit hacky as we need this to report telemetry for desktop via the bridged engine. pubfn get_incoming(&self) -> &Option<EngineIncoming> {
&self.incoming
}
pubfn failure(&mutself, err: impl Into<SyncFailure>) { // Currently we take the first error, under the assumption that the // first is the most important and all others stem from that. let failure = err.into(); ifself.failure.is_none() { self.failure = Some(failure);
} else {
log::warn!( "engine already has recorded a failure of {:?} - ignoring {:?}",
&self.failure,
&failure
);
}
}
/// A single sync. May have many engines, may have its own failure. #[derive(Debug, Serialize, Default)] pubstruct SyncTelemetry { #[serde(flatten)]
when_took: Stopwatch,
// Note that unlike other 'finished' methods, this isn't private - someone // needs to explicitly call this before handling the json payload to // whatever ends up submitting it. pubfn finished(&mutself) { self.when_took = self.when_took.finished();
}
}
/// The Sync ping payload, as documented at /// https://firefox-source-docs.mozilla.org/toolkit/components/telemetry/telemetry/data/sync-ping.html. /// May have many syncs, may have many events. However, due to the architecture /// of apps which use these components, this payload is almost certainly not /// suitable for submitting directly. For example, we will always return a /// payload with exactly 1 sync, and it will not know certain other fields /// in the payload, such as the *hashed* FxA device ID (see /// https://searchfox.org/mozilla-central/rev/c3ebaf6de2d481c262c04bb9657eaf76bf47e2ac/services/sync/modules/browserid_identity.js#185 /// for an example of how the device ID is constructed). The intention is that /// consumers of this will use this to create a "real" payload - eg, accumulating /// until some threshold number of syncs is reached, and contributing /// additional data which only the consumer knows. #[derive(Debug, Serialize, Default)] pubstruct SyncTelemetryPing {
version: u32,
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.