зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1517469 - Enable Events Telemetry for Uptake monitory r=janerik
Enable Events Telemetry for Uptake monitoring Differential Revision: https://phabricator.services.mozilla.com/D19496 --HG-- extra : moz-landing-system : lando
This commit is contained in:
Родитель
26b2971330
Коммит
c8bbaf4887
|
@ -154,23 +154,47 @@ function uninstallFakePAC() {
|
|||
MockRegistrar.unregister(fakePACCID);
|
||||
}
|
||||
|
||||
function _eventsTelemetrySnapshot(component, source) {
|
||||
const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
|
||||
const TELEMETRY_CATEGORY_ID = "uptake.remotecontent.result";
|
||||
const snapshot = Services.telemetry.snapshotEvents(Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTOUT, true);
|
||||
const parentEvents = snapshot.parent || [];
|
||||
return parentEvents
|
||||
// Transform raw event data to objects.
|
||||
.map(([i, category, method, object, value, extras]) => { return { category, method, object, value, extras }; })
|
||||
// Keep only for the specified component and source.
|
||||
.filter((e) => e.category == TELEMETRY_CATEGORY_ID && e.object == component && e.extras.source == source)
|
||||
// Return total number of events received by status, to mimic histograms snapshots.
|
||||
.reduce((acc, e) => {
|
||||
acc[e.value] = (acc[e.value] || 0) + 1;
|
||||
return acc;
|
||||
}, {});
|
||||
}
|
||||
|
||||
function getUptakeTelemetrySnapshot(key) {
|
||||
const {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
|
||||
const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
|
||||
const TELEMETRY_HISTOGRAM_ID = "UPTAKE_REMOTE_CONTENT_RESULT_1";
|
||||
return Services.telemetry
|
||||
.getKeyedHistogramById(TELEMETRY_HISTOGRAM_ID)
|
||||
.snapshot()[key];
|
||||
const TELEMETRY_COMPONENT = "remotesettings";
|
||||
const histogram = Services.telemetry.getKeyedHistogramById(TELEMETRY_HISTOGRAM_ID).snapshot()[key];
|
||||
const events = _eventsTelemetrySnapshot(TELEMETRY_COMPONENT, key);
|
||||
return { histogram, events };
|
||||
}
|
||||
|
||||
function checkUptakeTelemetry(snapshot1, snapshot2, expectedIncrements) {
|
||||
const LABELS = ["up_to_date", "success", "backoff", "pref_disabled", "parse_error", "content_error", "sign_error", "sign_retry_error", "conflict_error", "sync_error", "apply_error", "server_error", "certificate_error", "download_error", "timeout_error", "network_error", "offline_error", "cleanup_error", "unknown_error", "custom_1_error", "custom_2_error", "custom_3_error", "custom_4_error", "custom_5_error"];
|
||||
for (const label of LABELS) {
|
||||
const key = LABELS.indexOf(label);
|
||||
const expected = expectedIncrements[label] || 0;
|
||||
let value1 = (snapshot1 && snapshot1.values[key]) || 0;
|
||||
let value2 = (snapshot2 && snapshot2.values[key]) || 0;
|
||||
const actual = value2 - value1;
|
||||
equal(expected, actual, `check histogram values for ${label}`);
|
||||
const STATUSES = ["up_to_date", "success", "backoff", "pref_disabled", "parse_error", "content_error", "sign_error", "sign_retry_error", "conflict_error", "sync_error", "apply_error", "server_error", "certificate_error", "download_error", "timeout_error", "network_error", "offline_error", "cleanup_error", "unknown_error", "custom_1_error", "custom_2_error", "custom_3_error", "custom_4_error", "custom_5_error"];
|
||||
|
||||
for (const status of STATUSES) {
|
||||
const key = STATUSES.indexOf(status);
|
||||
const expected = expectedIncrements[status] || 0;
|
||||
// Check histogram increments.
|
||||
let value1 = (snapshot1 && snapshot1.histogram && snapshot1.histogram.values[key]) || 0;
|
||||
let value2 = (snapshot2 && snapshot2.histogram && snapshot2.histogram.values[key]) || 0;
|
||||
let actual = value2 - value1;
|
||||
equal(expected, actual, `check histogram values for ${status}`);
|
||||
// Check events increments.
|
||||
value1 = (snapshot1 && snapshot1.histogram && snapshot1.histogram.values[key]) || 0;
|
||||
value2 = (snapshot2 && snapshot2.histogram && snapshot2.histogram.values[key]) || 0;
|
||||
actual = value2 - value1;
|
||||
equal(expected, actual, `check events for ${status}`);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,10 +1,14 @@
|
|||
const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
|
||||
const { UptakeTelemetry } = ChromeUtils.import("resource://services-common/uptake-telemetry.js");
|
||||
|
||||
const COMPONENT = "remotesettings";
|
||||
|
||||
|
||||
add_task(async function test_unknown_status_is_not_reported() {
|
||||
const source = "update-source";
|
||||
const startHistogram = getUptakeTelemetrySnapshot(source);
|
||||
|
||||
UptakeTelemetry.report(source, "unknown-status");
|
||||
UptakeTelemetry.report(COMPONENT, "unknown-status", { source });
|
||||
|
||||
const endHistogram = getUptakeTelemetrySnapshot(source);
|
||||
const expectedIncrements = {};
|
||||
|
@ -18,7 +22,7 @@ add_task(async function test_each_status_can_be_caught_in_snapshot() {
|
|||
const expectedIncrements = {};
|
||||
for (const label of Object.keys(UptakeTelemetry.STATUS)) {
|
||||
const status = UptakeTelemetry.STATUS[label];
|
||||
UptakeTelemetry.report(source, status);
|
||||
UptakeTelemetry.report(COMPONENT, status, { source });
|
||||
expectedIncrements[status] = 1;
|
||||
}
|
||||
|
||||
|
|
|
@ -10,9 +10,13 @@ var EXPORTED_SYMBOLS = ["UptakeTelemetry"];
|
|||
const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
|
||||
// Telemetry report results.
|
||||
// Telemetry histogram id (see Histograms.json).
|
||||
const TELEMETRY_HISTOGRAM_ID = "UPTAKE_REMOTE_CONTENT_RESULT_1";
|
||||
|
||||
// Telemetry events id (see Events.yaml).
|
||||
const TELEMETRY_EVENTS_ID = "uptake.remotecontent.result";
|
||||
|
||||
|
||||
/**
|
||||
* A Telemetry helper to report uptake of remote content.
|
||||
*/
|
||||
|
@ -79,10 +83,30 @@ class UptakeTelemetry {
|
|||
/**
|
||||
* Reports the uptake status for the specified source.
|
||||
*
|
||||
* @param {string} source the identifier of the update source.
|
||||
* @param {string} status the uptake status.
|
||||
* @param {string} component the component reporting the uptake (eg. "normandy").
|
||||
* @param {string} status the uptake status (eg. "network_error")
|
||||
* @param {Object} extra extra values to report
|
||||
* @param {string} extra.source the update source (eg. "recipe-42").
|
||||
*/
|
||||
static report(source, status) {
|
||||
static report(component, status, extra = {}) {
|
||||
const { source } = extra;
|
||||
|
||||
if (!source) {
|
||||
throw new Error("`source` value is mandatory.");
|
||||
}
|
||||
|
||||
// Report event for real-time monitoring. See Events.yaml for registration.
|
||||
// Contrary to histograms, Telemetry Events are not enabled by default.
|
||||
// Enable them on first call to `report()`.
|
||||
if (!this._eventsEnabled) {
|
||||
Services.telemetry.setEventRecordingEnabled(TELEMETRY_EVENTS_ID, true);
|
||||
this._eventsEnabled = true;
|
||||
}
|
||||
Services.telemetry
|
||||
.recordEvent(TELEMETRY_EVENTS_ID, "uptake", component, status, extra);
|
||||
|
||||
// Report via histogram in main ping.
|
||||
// Note: this is the legacy equivalent of the above event. We keep it for continuity.
|
||||
Services.telemetry
|
||||
.getKeyedHistogramById(TELEMETRY_HISTOGRAM_ID)
|
||||
.add(source, status);
|
||||
|
|
|
@ -29,6 +29,8 @@ XPCOMUtils.defineLazyGlobalGetters(this, ["fetch"]);
|
|||
// IndexedDB name.
|
||||
const DB_NAME = "remote-settings";
|
||||
|
||||
const TELEMETRY_COMPONENT = "remotesettings";
|
||||
|
||||
const INVALID_SIGNATURE = "Invalid content signature";
|
||||
const MISSING_SIGNATURE = "Missing signature";
|
||||
|
||||
|
@ -383,7 +385,7 @@ class RemoteSettingsClient extends EventEmitter {
|
|||
reportStatus = UptakeTelemetry.STATUS.SUCCESS;
|
||||
}
|
||||
// Report success/error status to Telemetry.
|
||||
UptakeTelemetry.report(this.identifier, reportStatus);
|
||||
UptakeTelemetry.report(TELEMETRY_COMPONENT, reportStatus, { source: this.identifier });
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -40,8 +40,10 @@ const PREF_SETTINGS_CLOCK_SKEW_SECONDS = "clock_skew_seconds";
|
|||
const PREF_SETTINGS_LOAD_DUMP = "load_dump";
|
||||
|
||||
|
||||
// Telemetry update source identifier.
|
||||
const TELEMETRY_HISTOGRAM_KEY = "settings-changes-monitoring";
|
||||
// Telemetry identifiers.
|
||||
const TELEMETRY_COMPONENT = "remotesettings";
|
||||
const TELEMETRY_SOURCE = "settings-changes-monitoring";
|
||||
|
||||
// Push broadcast id.
|
||||
const BROADCAST_ID = "remote-settings/monitor_changes";
|
||||
|
||||
|
@ -160,8 +162,9 @@ function remoteSettingsFunction() {
|
|||
const remainingMilliseconds = parseInt(backoffReleaseTime, 10) - Date.now();
|
||||
if (remainingMilliseconds > 0) {
|
||||
// Backoff time has not elapsed yet.
|
||||
UptakeTelemetry.report(TELEMETRY_HISTOGRAM_KEY,
|
||||
UptakeTelemetry.STATUS.BACKOFF);
|
||||
UptakeTelemetry.report(TELEMETRY_COMPONENT,
|
||||
UptakeTelemetry.STATUS.BACKOFF,
|
||||
{ source: TELEMETRY_SOURCE });
|
||||
throw new Error(`Server is asking clients to back off; retry in ${Math.ceil(remainingMilliseconds / 1000)}s.`);
|
||||
} else {
|
||||
gPrefs.clearUserPref(PREF_SETTINGS_SERVER_BACKOFF);
|
||||
|
@ -177,15 +180,15 @@ function remoteSettingsFunction() {
|
|||
pollResult = await Utils.fetchLatestChanges(remoteSettings.pollingEndpoint, { expectedTimestamp, lastEtag });
|
||||
} catch (e) {
|
||||
// Report polling error to Uptake Telemetry.
|
||||
let report;
|
||||
let reportStatus;
|
||||
if (/Server/.test(e.message)) {
|
||||
report = UptakeTelemetry.STATUS.SERVER_ERROR;
|
||||
reportStatus = UptakeTelemetry.STATUS.SERVER_ERROR;
|
||||
} else if (/NetworkError/.test(e.message)) {
|
||||
report = UptakeTelemetry.STATUS.NETWORK_ERROR;
|
||||
reportStatus = UptakeTelemetry.STATUS.NETWORK_ERROR;
|
||||
} else {
|
||||
report = UptakeTelemetry.STATUS.UNKNOWN_ERROR;
|
||||
reportStatus = UptakeTelemetry.STATUS.UNKNOWN_ERROR;
|
||||
}
|
||||
UptakeTelemetry.report(TELEMETRY_HISTOGRAM_KEY, report);
|
||||
UptakeTelemetry.report(TELEMETRY_COMPONENT, reportStatus, { source: TELEMETRY_SOURCE });
|
||||
// No need to go further.
|
||||
throw new Error(`Polling for changes failed: ${e.message}.`);
|
||||
}
|
||||
|
@ -193,9 +196,9 @@ function remoteSettingsFunction() {
|
|||
const {serverTimeMillis, changes, currentEtag, backoffSeconds} = pollResult;
|
||||
|
||||
// Report polling success to Uptake Telemetry.
|
||||
const report = changes.length == 0 ? UptakeTelemetry.STATUS.UP_TO_DATE
|
||||
: UptakeTelemetry.STATUS.SUCCESS;
|
||||
UptakeTelemetry.report(TELEMETRY_HISTOGRAM_KEY, report);
|
||||
const reportStatus = changes.length === 0 ? UptakeTelemetry.STATUS.UP_TO_DATE
|
||||
: UptakeTelemetry.STATUS.SUCCESS;
|
||||
UptakeTelemetry.report(TELEMETRY_COMPONENT, reportStatus, { source: TELEMETRY_SOURCE });
|
||||
|
||||
// Check if the server asked the clients to back off (for next poll).
|
||||
if (backoffSeconds) {
|
||||
|
|
|
@ -9,7 +9,7 @@ ChromeUtils.defineModuleGetter(
|
|||
|
||||
var EXPORTED_SYMBOLS = ["Uptake"];
|
||||
|
||||
const SOURCE_PREFIX = "normandy";
|
||||
const COMPONENT = "normandy";
|
||||
|
||||
var Uptake = {
|
||||
// Action uptake
|
||||
|
@ -32,14 +32,14 @@ var Uptake = {
|
|||
RUNNER_SUCCESS: UptakeTelemetry.STATUS.SUCCESS,
|
||||
|
||||
reportRunner(status) {
|
||||
UptakeTelemetry.report(`${SOURCE_PREFIX}/runner`, status);
|
||||
UptakeTelemetry.report(COMPONENT, status, { source: `${COMPONENT}/runner` });
|
||||
},
|
||||
|
||||
reportRecipe(recipeId, status) {
|
||||
UptakeTelemetry.report(`${SOURCE_PREFIX}/recipe/${recipeId}`, status);
|
||||
UptakeTelemetry.report(COMPONENT, status, { source: `${COMPONENT}/recipe/${recipeId}` });
|
||||
},
|
||||
|
||||
reportAction(actionName, status) {
|
||||
UptakeTelemetry.report(`${SOURCE_PREFIX}/action/${actionName}`, status);
|
||||
UptakeTelemetry.report(COMPONENT, status, { source: `${COMPONENT}/action/${actionName}` });
|
||||
},
|
||||
};
|
||||
|
|
|
@ -858,6 +858,31 @@ security.ui.identitypopup:
|
|||
cr: Whether Cookie Restrictions was active while the user interacted with the UI
|
||||
products:
|
||||
- firefox
|
||||
uptake.remotecontent.result:
|
||||
uptake:
|
||||
description: >
|
||||
Was the remote content successfully pulled?
|
||||
This uptake telemetry allows to monitor the behaviour of our clients when it comes
|
||||
to fetching data from remote servers. This helps defect-detection and allow observation of
|
||||
the proportion of success among clients and sources, the distribution of error causes, and
|
||||
its evolution over time.
|
||||
methods:
|
||||
- uptake
|
||||
objects:
|
||||
- remotesettings
|
||||
- normandy
|
||||
extra_keys:
|
||||
source: >
|
||||
A label to distinguish what is being pulled or updated in the component (eg. recipe id,
|
||||
settings collection name, ...).
|
||||
bug_numbers:
|
||||
- 1517469
|
||||
record_in_processes: ["main"]
|
||||
release_channel_collection: opt-out
|
||||
expiry_version: never
|
||||
notification_emails:
|
||||
- mleplatre@mozilla.com
|
||||
- bens-directs@mozilla.com
|
||||
|
||||
intl.ui.browserLanguage:
|
||||
action:
|
||||
|
|
|
@ -74,6 +74,8 @@ Only ``value`` and the values of ``extra`` will be truncated if over the specifi
|
|||
Any other ``String`` going over its limit will be reported as an error and the operation
|
||||
aborted.
|
||||
|
||||
.. _eventdefinition:
|
||||
|
||||
The YAML definition file
|
||||
========================
|
||||
|
||||
|
|
|
@ -26,9 +26,10 @@ Usage
|
|||
|
||||
const { UptakeTelemetry } = ChromeUtils.import("resource://services-common/uptake-telemetry.js", {});
|
||||
|
||||
UptakeTelemetry.report(source, status);
|
||||
UptakeTelemetry.report(component, status, { source });
|
||||
|
||||
- ``source``, a ``string`` that is an identifier for the update source (eg. ``addons-blocklist``)
|
||||
- ``component``, a ``string`` that identifies the calling component (eg. ``"remotesettings"``, ``"normandy"``). Arbitrary components have to be previously declared in the :ref:`Telemetry Events definition file <eventdefinition>`.
|
||||
- ``source``, a ``string`` to distinguish what is being pulled or updated in the component (eg. ``"blocklists/addons"``, ``"recipes/33"``)
|
||||
- ``status``, one of the following status constants:
|
||||
|
||||
- ``UptakeTelemetry.STATUS.UP_TO_DATE``: Local content was already up-to-date with remote content.
|
||||
|
@ -57,12 +58,13 @@ Usage
|
|||
- ``UptakeTelemetry.STATUS.CUSTOM_5_ERROR``: Error #5 specific to this update source.
|
||||
|
||||
|
||||
The data is submitted to a single :ref:`keyed histogram <histogram-type-keyed>` whose id is ``UPTAKE_REMOTE_CONTENT_RESULT_1`` and the specified update ``source`` as the key.
|
||||
The data is submitted as a :ref:`Telemetry Event <eventtelemetry>`, and to a single :ref:`keyed histogram <histogram-type-keyed>` whose id is ``UPTAKE_REMOTE_CONTENT_RESULT_1`` and the specified update ``source`` as the key.
|
||||
|
||||
Example:
|
||||
|
||||
.. code-block:: js
|
||||
|
||||
const COMPONENT = "normandy";
|
||||
const UPDATE_SOURCE = "update-monitoring";
|
||||
|
||||
let status;
|
||||
|
@ -74,7 +76,7 @@ Example:
|
|||
UptakeTelemetry.STATUS.NETWORK_ERROR :
|
||||
UptakeTelemetry.STATUS.SERVER_ERROR ;
|
||||
}
|
||||
UptakeTelemetry.report(UPDATE_SOURCE, status);
|
||||
UptakeTelemetry.report(COMPONENT, status, { source: UPDATE_SOURCE });
|
||||
|
||||
|
||||
Use-cases
|
||||
|
@ -88,7 +90,7 @@ The following remote data sources are already using this unified histogram.
|
|||
* plugins blocklist
|
||||
* certificate revocation
|
||||
* certificate pinning
|
||||
* :ref:`Shield Recipe client <components/normandy>`
|
||||
* :ref:`Normandy Recipe client <components/normandy>`
|
||||
|
||||
Obviously, the goal is to eventually converge and avoid ad-hoc Telemetry probes for measuring uptake of remote content. Some notable potential use-cases are:
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче