Bug 1254099 - Rework the blocklist client telemetry to provide more details draft
authorMathieu Leplatre <mathieu@mozilla.com>
Fri, 03 Feb 2017 16:51:23 +0100
changeset 470335 3a33c2580a16ffdb4f0b6b391b48b2b1a212686a
parent 470232 3627f7ddcb5e1b53608feaf51362f02a0346200e
child 544447 6f179cbe00a7630457334d57948ae4c1b14890fb
push id43996
push usermleplatre@mozilla.com
push dateFri, 03 Feb 2017 15:52:06 +0000
bugs1254099
milestone54.0a1
Bug 1254099 - Rework the blocklist client telemetry to provide more details MozReview-Commit-ID: B31E9LnFWW1
services/common/blocklist-clients.js
services/common/blocklist-updater.js
services/common/tests/unit/test_blocklist_clients.js
services/common/tests/unit/test_blocklist_signatures.js
toolkit/components/telemetry/Histograms.json
toolkit/mozapps/extensions/nsBlocklistService.js
--- a/services/common/blocklist-clients.js
+++ b/services/common/blocklist-clients.js
@@ -20,25 +20,16 @@ const { Task } = Cu.import("resource://g
 const { OS } = Cu.import("resource://gre/modules/osfile.jsm", {});
 Cu.importGlobalProperties(["fetch"]);
 
 const { Kinto } = Cu.import("resource://services-common/kinto-offline-client.js", {});
 const { KintoHttpClient } = Cu.import("resource://services-common/kinto-http-client.js", {});
 const { FirefoxAdapter } = Cu.import("resource://services-common/kinto-storage-adapter.js", {});
 const { CanonicalJSON } = Components.utils.import("resource://gre/modules/CanonicalJSON.jsm", {});
 
-const SERVICES_SETTINGS_SYNC_SIG_FAIL        = 3;
-const SERVICES_SETTINGS_SYNC_RETRY_SIG_FAIL  = 4;
-const SERVICES_SETTINGS_SYNC_ONECRL_FAIL     = 5;
-const SERVICES_SETTINGS_SYNC_ONECRL_ENTRY    = 6;
-const SERVICES_SETTINGS_SYNC_ADDONS_FAIL     = 7;
-const SERVICES_SETTINGS_SYNC_GFX_FAIL        = 8;
-const SERVICES_SETTINGS_SYNC_PLUGINS_FAIL    = 9;
-const SERVICES_SETTINGS_SYNC_PINNING_FAIL    = 10;
-
 const PREF_SETTINGS_SERVER                   = "services.settings.server";
 const PREF_BLOCKLIST_BUCKET                  = "services.blocklist.bucket";
 const PREF_BLOCKLIST_ONECRL_COLLECTION       = "services.blocklist.onecrl.collection";
 const PREF_BLOCKLIST_ONECRL_CHECKED_SECONDS  = "services.blocklist.onecrl.checked";
 const PREF_BLOCKLIST_ADDONS_COLLECTION       = "services.blocklist.addons.collection";
 const PREF_BLOCKLIST_ADDONS_CHECKED_SECONDS  = "services.blocklist.addons.checked";
 const PREF_BLOCKLIST_PLUGINS_COLLECTION      = "services.blocklist.plugins.collection";
 const PREF_BLOCKLIST_PLUGINS_CHECKED_SECONDS = "services.blocklist.plugins.checked";
@@ -47,16 +38,26 @@ const PREF_BLOCKLIST_PINNING_BUCKET     
 const PREF_BLOCKLIST_PINNING_COLLECTION      = "services.blocklist.pinning.collection";
 const PREF_BLOCKLIST_PINNING_CHECKED_SECONDS = "services.blocklist.pinning.checked";
 const PREF_BLOCKLIST_GFX_COLLECTION          = "services.blocklist.gfx.collection";
 const PREF_BLOCKLIST_GFX_CHECKED_SECONDS     = "services.blocklist.gfx.checked";
 const PREF_BLOCKLIST_ENFORCE_SIGNING         = "services.blocklist.signing.enforced";
 
 const INVALID_SIGNATURE = "Invalid content/signature";
 
+// Telemetry report results.
+const SERVICE_SETTINGS_UPDATE_UP_TO_DATE                = -1;
+const SERVICE_SETTINGS_UPDATE_SUCCESS                   = 0;
+const SERVICE_SETTINGS_UPDATE_UNKNOWN_FAILURE           = 1;
+const SERVICE_SETTINGS_UPDATE_SYNC_FAILURE              = 2;
+const SERVICE_SETTINGS_UPDATE_CONFLICT_FAILURE          = 3;
+const SERVICE_SETTINGS_UPDATE_SIGNATURE_FAILURE         = 4;
+const SERVICE_SETTINGS_UPDATE_SIGNATURE_RETRY_FAILURE   = 5;
+const SERVICE_SETTINGS_UPDATE_APPLICATION_FAILURE       = 6;
+
 // FIXME: this was the default path in earlier versions of
 // FirefoxAdapter, so for backwards compatibility we maintain this
 // filename, even though it isn't descriptive of who is using it.
 this.KINTO_STORAGE_PATH    = "kinto.sqlite";
 
 this.FILENAME_ADDONS_JSON  = "blocklist-addons.json";
 this.FILENAME_GFX_JSON     = "blocklist-gfx.json";
 this.FILENAME_PLUGINS_JSON = "blocklist-plugins.json";
@@ -108,23 +109,27 @@ function kintoClient(connection, bucket)
   };
 
   return new Kinto(config);
 }
 
 
 class BlocklistClient {
 
-  constructor(collectionName, lastCheckTimePref, processCallback, bucketName, signerName, failureBucket) {
+  constructor(collectionName, lastCheckTimePref, processCallback, bucketName, signerName) {
     this.collectionName = collectionName;
     this.lastCheckTimePref = lastCheckTimePref;
     this.processCallback = processCallback;
     this.bucketName = bucketName;
     this.signerName = signerName;
-    this.failureBucket = failureBucket;
+  }
+
+  get histogramId() {
+    const identifier = `${this.bucketName.toUpperCase()}_${this.collectionName.toUpperCase()}`;
+    return `SERVICE_SETTINGS_UPDATE_${identifier}_RESULT`;
   }
 
   validateCollectionSignature(payload, collection, ignoreLocal) {
     return Task.spawn((function* () {
       // this is a content-signature field from an autograph response.
       const {x5u, signature} = yield fetchCollectionMetadata(collection);
       const certChain = yield fetch(x5u).then((res) => res.text());
 
@@ -149,19 +154,16 @@ class BlocklistClient {
       const serialized = CanonicalJSON.stringify(toSerialize);
 
       if (verifier.verifyContentSignature(serialized, "p384ecdsa=" + signature,
                                           certChain,
                                           this.signerName)) {
         // In case the hash is valid, apply the changes locally.
         return payload;
       }
-      Services.telemetry
-          .getHistogramById("SERVICES_SETTINGS_SUCCESS")
-          .add(SERVICES_SETTINGS_SYNC_SIG_FAIL);
       throw new Error(INVALID_SIGNATURE);
     }).bind(this));
   }
 
   /**
    * Synchronize from Kinto server, if necessary.
    *
    * @param {int}  lastModified the lastModified date (on the server) for
@@ -180,75 +182,101 @@ class BlocklistClient {
       opts.hooks = {
         "incoming-changes": [this.validateCollectionSignature.bind(this)]
       }
     }
 
 
     return Task.spawn((function* syncCollection() {
       let connection;
+      let reportStatus;
       try {
         connection = yield FirefoxAdapter.openConnection({path: KINTO_STORAGE_PATH});
         const db = kintoClient(connection, this.bucketName);
         const collection = db.collection(this.collectionName, opts);
 
         const collectionLastModified = yield collection.db.getLastModified();
         // If the data is up to date, there's no need to sync. We still need
         // to record the fact that a check happened.
         if (lastModified <= collectionLastModified) {
           this.updateLastCheck(serverTime);
+          reportStatus = SERVICE_SETTINGS_UPDATE_UP_TO_DATE;
           return;
         }
         // Fetch changes from server.
         try {
           const {ok} = yield collection.sync();
           if (!ok) {
+            // Some synchronization conflicts occured.
+            reportStatus = SERVICE_SETTINGS_UPDATE_CONFLICT_FAILURE;
             throw new Error("Sync failed");
           }
         } catch (e) {
           if (e.message == INVALID_SIGNATURE) {
+            // Signature verification failed during synchronzation.
+            reportStatus = SERVICE_SETTINGS_UPDATE_SIGNATURE_FAILURE;
             // if sync fails with a signature error, it's likely that our
             // local data has been modified in some way.
             // We will attempt to fix this by retrieving the whole
             // remote collection.
             const payload = yield fetchRemoteCollection(collection);
             try {
               yield this.validateCollectionSignature(payload, collection, true);
             } catch (e) {
-              Services.telemetry
-                  .getHistogramById("SERVICES_SETTINGS_SUCCESS")
-                  .add(SERVICES_SETTINGS_SYNC_RETRY_SIG_FAIL);
+              reportStatus = SERVICE_SETTINGS_UPDATE_SIGNATURE_RETRY_FAILURE;
               throw(e);
             }
             // if the signature is good (we haven't thrown), and the remote
             // last_modified is newer than the local last_modified, replace the
             // local data
             const localLastModified = yield collection.db.getLastModified();
             if (payload.last_modified >= localLastModified) {
               yield collection.clear();
               yield collection.loadDump(payload.data);
             }
           } else {
+            // The sync has thrown, it can be a network or local database error.
+            reportStatus = SERVICE_SETTINGS_UPDATE_SYNC_FAILURE;
             throw e;
           }
         }
         // Read local collection of records.
         const {data} = yield collection.list();
 
-        yield this.processCallback(data);
+        // Handle the obtained records (ie. apply locally).
+        try {
+          yield this.processCallback(data);
+        } catch (e) {
+          reportStatus = SERVICE_SETTINGS_UPDATE_APPLICATION_FAILURE;
+          throw e;
+        }
 
         // Track last update.
         this.updateLastCheck(serverTime);
       } catch(e) {
-        Services.telemetry
-            .getHistogramById("SERVICES_SETTINGS_SUCCESS")
-            .add(this.failureBucket);
+        // No specific error was tracked, mark it as unknown.
+        if (!reportStatus) {
+          reportStatus = SERVICE_SETTINGS_UPDATE_UNKNOWN_FAILURE;
+        }
         throw e;
       } finally {
-        yield connection.close();
+        if (connection) {
+          yield connection.close();
+        }
+        // No error was reported, this is a success!
+        if (!reportStatus) {
+          reportStatus = SERVICE_SETTINGS_UPDATE_SUCCESS;
+        }
+        // Report status to Telemetry.
+        // XXX: TODO: check that histogram exists (e.g. QA using preview bucket)
+        if (reportStatus >= 0) {
+          Services.telemetry
+            .getHistogramById(this.histogramId)
+            .add(reportStatus);
+        }
       }
     }).bind(this));
   }
 
   /**
    * Save last time server was checked in users prefs.
    *
    * @param {Date} serverTime   the current date return by server.
@@ -352,47 +380,42 @@ function* updateJSONBlocklist(filename, 
   }
 }
 
 this.OneCRLBlocklistClient = new BlocklistClient(
   Services.prefs.getCharPref(PREF_BLOCKLIST_ONECRL_COLLECTION),
   PREF_BLOCKLIST_ONECRL_CHECKED_SECONDS,
   updateCertBlocklist,
   Services.prefs.getCharPref(PREF_BLOCKLIST_BUCKET),
-  "onecrl.content-signature.mozilla.org",
-  SERVICES_SETTINGS_SYNC_ONECRL_FAIL
+  "onecrl.content-signature.mozilla.org"
 );
 
 this.AddonBlocklistClient = new BlocklistClient(
   Services.prefs.getCharPref(PREF_BLOCKLIST_ADDONS_COLLECTION),
   PREF_BLOCKLIST_ADDONS_CHECKED_SECONDS,
   updateJSONBlocklist.bind(undefined, FILENAME_ADDONS_JSON),
   Services.prefs.getCharPref(PREF_BLOCKLIST_BUCKET),
-  undefined,
-  SERVICES_SETTINGS_SYNC_ADDONS_FAIL
+  undefined
 );
 
 this.GfxBlocklistClient = new BlocklistClient(
   Services.prefs.getCharPref(PREF_BLOCKLIST_GFX_COLLECTION),
   PREF_BLOCKLIST_GFX_CHECKED_SECONDS,
   updateJSONBlocklist.bind(undefined, FILENAME_GFX_JSON),
   Services.prefs.getCharPref(PREF_BLOCKLIST_BUCKET),
-  undefined,
-  SERVICES_SETTINGS_SYNC_GFX_FAIL
+  undefined
 );
 
 this.PluginBlocklistClient = new BlocklistClient(
   Services.prefs.getCharPref(PREF_BLOCKLIST_PLUGINS_COLLECTION),
   PREF_BLOCKLIST_PLUGINS_CHECKED_SECONDS,
   updateJSONBlocklist.bind(undefined, FILENAME_PLUGINS_JSON),
   Services.prefs.getCharPref(PREF_BLOCKLIST_BUCKET),
-  undefined,
-  SERVICES_SETTINGS_SYNC_PLUGINS_FAIL
+  undefined
 );
 
 this.PinningPreloadClient = new BlocklistClient(
   Services.prefs.getCharPref(PREF_BLOCKLIST_PINNING_COLLECTION),
   PREF_BLOCKLIST_PINNING_CHECKED_SECONDS,
   updatePinningList,
   Services.prefs.getCharPref(PREF_BLOCKLIST_PINNING_BUCKET),
-  "pinning-preload.content-signature.mozilla.org",
-  SERVICES_SETTINGS_SYNC_PINNING_FAIL
+  "pinning-preload.content-signature.mozilla.org"
 );
--- a/services/common/blocklist-updater.js
+++ b/services/common/blocklist-updater.js
@@ -12,17 +12,18 @@ Cu.importGlobalProperties(["fetch"]);
 const BlocklistClients = Cu.import("resource://services-common/blocklist-clients.js", {});
 
 const PREF_SETTINGS_SERVER              = "services.settings.server";
 const PREF_BLOCKLIST_CHANGES_PATH       = "services.blocklist.changes.path";
 const PREF_BLOCKLIST_LAST_UPDATE        = "services.blocklist.last_update_seconds";
 const PREF_BLOCKLIST_LAST_ETAG          = "services.blocklist.last_etag";
 const PREF_BLOCKLIST_CLOCK_SKEW_SECONDS = "services.blocklist.clock_skew_seconds";
 
-// Telemetry report result.
+// Telemetry report results.
+const TELEMETRY_HISTOGRAM = "SERVICE_SETTINGS_POLLING_RESULT";
 const SERVICE_SETTINGS_POLLING_SUCCESS          = 0;
 const SERVICE_SETTINGS_POLLING_NETWORK_FAILURE  = 1;
 const SERVICE_SETTINGS_POLLING_SERVER_FAILURE   = 2;
 
 const gBlocklistClients = {
   [BlocklistClients.OneCRLBlocklistClient.collectionName]: BlocklistClients.OneCRLBlocklistClient,
   [BlocklistClients.AddonBlocklistClient.collectionName]: BlocklistClients.AddonBlocklistClient,
   [BlocklistClients.GfxBlocklistClient.collectionName]: BlocklistClients.GfxBlocklistClient,
@@ -96,17 +97,17 @@ this.checkVersions = function() {
     } catch (e) {
       pollError = e;
     }
     // Report polling status to Telemetry.
     const report =  pollResult ? SERVICE_SETTINGS_POLLING_SUCCESS
                                : /Server/.test(pollError.message) ? SERVICE_SETTINGS_POLLING_SERVER_FAILURE
                                                                   : SERVICE_SETTINGS_POLLING_NETWORK_FAILURE;
     Services.telemetry
-      .getHistogramById("SERVICE_SETTINGS_POLLING_RESULT")
+      .getHistogramById(TELEMETRY_HISTOGRAM)
       .add(report);
 
     if (pollError) {
       // No need to go further.
       throw new Error(`Polling for changes failed: ${pollError.message}.`);
     }
 
     const {serverTimeMillis, versionInfo, currentEtag} = pollResult;
--- a/services/common/tests/unit/test_blocklist_clients.js
+++ b/services/common/tests/unit/test_blocklist_clients.js
@@ -1,12 +1,18 @@
 const { Constructor: CC } = Components;
 
 const KEY_PROFILEDIR = "ProfD";
 
+const SERVICE_SETTINGS_UPDATE_SUCCESS                = 0;
+const SERVICE_SETTINGS_UPDATE_UNKNOWN_FAILURE        = 1;
+const SERVICE_SETTINGS_UPDATE_SYNC_FAILURE           = 2;
+const SERVICE_SETTINGS_UPDATE_APPLICATION_FAILURE    = 6;
+
+
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://testing-common/httpd.js");
 Cu.import("resource://gre/modules/Timer.jsm");
 const { FileUtils } = Cu.import("resource://gre/modules/FileUtils.jsm", {});
 const { OS } = Cu.import("resource://gre/modules/osfile.jsm", {});
 
 const { Kinto } = Cu.import("resource://services-common/kinto-offline-client.js", {});
 const { FirefoxAdapter } = Cu.import("resource://services-common/kinto-storage-adapter.js", {});
@@ -194,29 +200,109 @@ add_task(function* test_sends_reload_mes
 add_task(clear_state);
 
 add_task(function* test_do_nothing_when_blocklist_is_up_to_date() {
   for (let {client, filename} of gBlocklistClients) {
     yield client.maybeSync(2000, Date.now() - 1000);
     const profFile = FileUtils.getFile(KEY_PROFILEDIR, [filename]);
     const fileLastModified = profFile.lastModifiedTime = profFile.lastModifiedTime - 1000;
     const serverTime = Date.now();
+    const startHistogram = getHistogramSnapshot(client.histogramId);
 
     yield client.maybeSync(3000, serverTime);
 
     // File was not updated.
     equal(fileLastModified, profFile.lastModifiedTime);
     // Server time was updated.
     const after = Services.prefs.getIntPref(client.lastCheckTimePref);
     equal(after, Math.round(serverTime / 1000));
+    // No Telemetry was sent.
+    const endHistogram = getHistogramSnapshot(client.histogramId);
+    const expectedIncrements = getDefaultIncrements(startHistogram);
+    checkHistogramIncrements(startHistogram, endHistogram, expectedIncrements);
+  }
+});
+add_task(clear_state);
+
+add_task(function* test_telemetry_if_sync_succeeds() {
+  // We test each client because Telemetry requires preleminary declarations.
+  for (let {client} of gBlocklistClients) {
+    const serverTime = Date.now();
+    const startHistogram = getHistogramSnapshot(client.histogramId);
+
+    yield client.maybeSync(2000, serverTime);
+
+    const endHistogram = getHistogramSnapshot(client.histogramId);
+    const expectedIncrements = getDefaultIncrements(startHistogram);
+    expectedIncrements[SERVICE_SETTINGS_UPDATE_SUCCESS] = 1;
+    checkHistogramIncrements(startHistogram, endHistogram, expectedIncrements);
   }
 });
 add_task(clear_state);
 
+add_task(function* test_telemetry_reports_if_application_fails() {
+  const {client} = gBlocklistClients[0];
+  const serverTime = Date.now();
+  const startHistogram = getHistogramSnapshot(client.histogramId);
+  const backup = client.processCallback;
+  client.processCallback = () => {throw new Error("boom");}
 
+  try {
+    yield client.maybeSync(2000, serverTime);
+  } catch (e) {}
+
+  client.processCallback = backup;
+
+  const endHistogram = getHistogramSnapshot(client.histogramId);
+  const expectedIncrements = getDefaultIncrements(startHistogram);
+  expectedIncrements[SERVICE_SETTINGS_UPDATE_APPLICATION_FAILURE] = 1;
+  checkHistogramIncrements(startHistogram, endHistogram, expectedIncrements);
+});
+add_task(clear_state);
+
+add_task(function* test_telemetry_reports_if_sync_fails() {
+  const {client} = gBlocklistClients[0];
+  const serverTime = Date.now();
+
+  const sqliteHandle = yield FirefoxAdapter.openConnection({path: kintoFilename});
+  const collection = kintoCollection(client.collectionName, sqliteHandle);
+  yield collection.db.saveLastModified(9999);
+  yield sqliteHandle.close();
+
+  const startHistogram = getHistogramSnapshot(client.histogramId);
+
+  try {
+    yield client.maybeSync(10000, serverTime);
+  } catch (e) {}
+
+  const endHistogram = getHistogramSnapshot(client.histogramId);
+  const expectedIncrements = getDefaultIncrements(startHistogram);
+  expectedIncrements[SERVICE_SETTINGS_UPDATE_SYNC_FAILURE] = 1;
+  checkHistogramIncrements(startHistogram, endHistogram, expectedIncrements);
+});
+add_task(clear_state);
+
+add_task(function* test_telemetry_reports_unknown_errors() {
+  const {client} = gBlocklistClients[0];
+  const serverTime = Date.now();
+  const backup = FirefoxAdapter.openConnection;
+  FirefoxAdapter.openConnection = () => {throw new Error("Internal")};
+  const startHistogram = getHistogramSnapshot(client.histogramId);
+
+  try {
+    yield client.maybeSync(2000, serverTime);
+  } catch (e) {}
+
+  FirefoxAdapter.openConnection = backup;
+  const endHistogram = getHistogramSnapshot(client.histogramId);
+  const expectedIncrements = getDefaultIncrements(startHistogram);
+  expectedIncrements[SERVICE_SETTINGS_UPDATE_UNKNOWN_FAILURE] = 1;
+  checkHistogramIncrements(startHistogram, endHistogram, expectedIncrements);
+});
+add_task(clear_state);
 
 // get a response for a given request from sample data
 function getSampleResponse(req, port) {
   const responses = {
     "OPTIONS": {
       "sampleHeaders": [
         "Access-Control-Allow-Headers: Content-Length,Expires,Backoff,Retry-After,Last-Modified,Total-Records,ETag,Pragma,Cache-Control,authorization,content-type,if-none-match,Alert,Next-Page",
         "Access-Control-Allow-Methods: GET,HEAD,OPTIONS,POST,DELETE,OPTIONS",
@@ -394,14 +480,28 @@ function getSampleResponse(req, port) {
         "blockID": "g200",
         "feature": "WEBGL_MSAA",
         "devices": [],
         "id": "c3a15ba9-e0e2-421f-e399-c995e5b8d14e",
         "last_modified": 3500,
         "os": "Darwin 11",
         "featureStatus": "BLOCKED_DEVICE"
       }]})
+    },
+    "GET:/v1/buckets/blocklists/collections/addons/records?_sort=-last_modified&_since=9999": {
+      "sampleHeaders": [
+        "Access-Control-Allow-Origin: *",
+        "Access-Control-Expose-Headers: Retry-After, Content-Length, Alert, Backoff",
+        "Content-Type: application/json; charset=UTF-8",
+        "Server: waitress",
+      ],
+      "status": {status: 503, statusText: "Service Unavailable"},
+      "responseBody": JSON.stringify({
+        code: 503,
+        errno: 999,
+        error: "Service Unavailable",
+      })
     }
   };
   return responses[`${req.method}:${req.path}?${req.queryString}`] ||
          responses[req.method];
 
 }
--- a/services/common/tests/unit/test_blocklist_signatures.js
+++ b/services/common/tests/unit/test_blocklist_signatures.js
@@ -11,19 +11,21 @@ const { OneCRLBlocklistClient } = Cu.imp
 let server;
 
 const PREF_BLOCKLIST_BUCKET            = "services.blocklist.bucket";
 const PREF_BLOCKLIST_ENFORCE_SIGNING   = "services.blocklist.signing.enforced";
 const PREF_BLOCKLIST_ONECRL_COLLECTION = "services.blocklist.onecrl.collection";
 const PREF_SETTINGS_SERVER             = "services.settings.server";
 const PREF_SIGNATURE_ROOT              = "security.content.signature.root_hash";
 
-const SERVICES_SETTINGS_SYNC_SIG_FAIL       = 3;
-const SERVICES_SETTINGS_SYNC_RETRY_SIG_FAIL = 4;
-const SERVICES_SETTINGS_SYNC_ONECRL_FAIL    = 5;
+// Telemetry reports.
+const histogramId = OneCRLBlocklistClient.histogramId;
+const SERVICE_SETTINGS_UPDATE_SUCCESS                   = 0;
+const SERVICE_SETTINGS_UPDATE_SIGNATURE_FAILURE         = 4;
+const SERVICE_SETTINGS_UPDATE_SIGNATURE_RETRY_FAILURE   = 5;
 
 const kintoFilename = "kinto.sqlite";
 
 const CERT_DIR = "test_blocklist_signatures/";
 const CHAIN_FILES =
     ["collection_signing_ee.pem",
      "collection_signing_int.pem",
      "collection_signing_root.pem"];
@@ -291,27 +293,27 @@ add_task(function* test_check_signatures
       [RESPONSE_EMPTY_INITIAL],
     "GET:/v1/buckets/blocklists/collections/certificates?":
       [RESPONSE_META_EMPTY_SIG]
   };
 
   // .. and use this map to register handlers for each path
   registerHandlers(emptyCollectionResponses);
 
-  let startHistogram = getHistogramSnapshot("SERVICES_SETTINGS_SUCCESS");
+  let startHistogram = getHistogramSnapshot(histogramId);
 
   // With all of this set up, we attempt a sync. This will resolve if all is
   // well and throw if something goes wrong.
   yield OneCRLBlocklistClient.maybeSync(1000, startTime);
 
-  let endHistogram = getHistogramSnapshot("SERVICES_SETTINGS_SUCCESS");
+  let endHistogram = getHistogramSnapshot(histogramId);
 
-  // ensure that none of the services_settings_success histogram counts are
-  // affected when a succesful sync occurs
+  // ensure that a success histogram is tracked when a succesful sync occurs.
   let expectedIncrements = getDefaultIncrements(startHistogram);
+  expectedIncrements[SERVICE_SETTINGS_UPDATE_SUCCESS] = 1;
   checkHistogramIncrements(startHistogram, endHistogram, expectedIncrements);
 
   // Check that some additions (2 records) to the collection have a valid
   // signature.
 
   // This response adds two entries (RECORD1 and RECORD2) to the collection
   const RESPONSE_TWO_ADDED = {
     comment: "RESPONSE_TWO_ADDED",
@@ -440,25 +442,28 @@ add_task(function* test_check_signatures
       [RESPONSE_COMPLETE_INITIAL],
     // The next request is for the full collection sorted by id. This will be
     // checked against the valid signature - so the sync should succeed.
     "GET:/v1/buckets/blocklists/collections/certificates/records?_sort=id":
       [RESPONSE_COMPLETE_INITIAL_SORTED_BY_ID]
   };
 
   registerHandlers(badSigGoodSigResponses);
+
+  startHistogram = getHistogramSnapshot(histogramId);
+
   yield OneCRLBlocklistClient.maybeSync(5000, startTime);
 
-  endHistogram = getHistogramSnapshot("SERVICES_SETTINGS_SUCCESS");
+  endHistogram = getHistogramSnapshot(histogramId);
 
   // ensure that the failure count is incremented for a succesful sync with an
   // (initial) bad signature - only SERVICES_SETTINGS_SYNC_SIG_FAIL should
   // increment.
   expectedIncrements = getDefaultIncrements(startHistogram);
-  expectedIncrements[SERVICES_SETTINGS_SYNC_SIG_FAIL] = 1;
+  expectedIncrements[SERVICE_SETTINGS_UPDATE_SIGNATURE_FAILURE] = 1;
   checkHistogramIncrements(startHistogram, endHistogram, expectedIncrements);
 
   const badSigGoodOldResponses = {
     // In this test, we deliberately serve a bad signature initially. The
     // subsequent sitnature returned is a valid one for the three item
     // collection.
     "GET:/v1/buckets/blocklists/collections/certificates?":
       [RESPONSE_META_BAD_SIG, RESPONSE_META_EMPTY_SIG],
@@ -490,30 +495,29 @@ add_task(function* test_check_signatures
     "GET:/v1/buckets/blocklists/collections/certificates/records?_sort=-last_modified&_since=4000":
       [RESPONSE_EMPTY_NO_UPDATE],
     // The next request is for the full collection sorted by id. This will be
     // checked against the valid signature - so the sync should succeed.
     "GET:/v1/buckets/blocklists/collections/certificates/records?_sort=id":
       [RESPONSE_COMPLETE_INITIAL_SORTED_BY_ID]
   };
 
-  startHistogram = getHistogramSnapshot("SERVICES_SETTINGS_SUCCESS");
+  startHistogram = getHistogramSnapshot(histogramId);
   registerHandlers(allBadSigResponses);
   try {
     yield OneCRLBlocklistClient.maybeSync(6000, startTime);
     do_throw("Sync should fail (the signature is intentionally bad)");
   } catch (e) {
     yield checkRecordCount(2);
   }
 
   // Ensure that the failure is reflected in the accumulated telemetry:
-  endHistogram = getHistogramSnapshot("SERVICES_SETTINGS_SUCCESS");
-  expectedIncrements[SERVICES_SETTINGS_SYNC_SIG_FAIL] = 2;
-  expectedIncrements[SERVICES_SETTINGS_SYNC_RETRY_SIG_FAIL] = 1;
-  expectedIncrements[SERVICES_SETTINGS_SYNC_ONECRL_FAIL] = 1;
+  endHistogram = getHistogramSnapshot(histogramId);
+  expectedIncrements = getDefaultIncrements(startHistogram);
+  expectedIncrements[SERVICE_SETTINGS_UPDATE_SIGNATURE_RETRY_FAILURE] = 1;
   checkHistogramIncrements(startHistogram, endHistogram, expectedIncrements);
 });
 
 function run_test() {
   // ensure signatures are enforced
   Services.prefs.setBoolPref(PREF_BLOCKLIST_ENFORCE_SIGNING, true);
 
   // get a signature verifier to ensure nsNSSComponent is initialized
--- a/toolkit/components/telemetry/Histograms.json
+++ b/toolkit/components/telemetry/Histograms.json
@@ -8889,17 +8889,57 @@
     "description": "Intercepted fetch sending back same Request object. File bugs in Core::DOM in case of a Telemetry regression."
   },
   "SERVICE_SETTINGS_POLLING_RESULT": {
     "expires_in_version": "never",
     "kind": "enumerated",
     "n_values": 5,
     "alert_emails": ["seceng-telemetry@mozilla.com"],
     "bug_numbers": [1254099],
-    "description": "Services settings polling result information (0=Success, 1=Network error, 2=Server response error"
+    "description": "Services settings polling result information (0=Success, 1=Network error, 2=Server response error)"
+  },
+  "SERVICE_SETTINGS_UPDATE_BLOCKLISTS_ADDONS_RESULT": {
+    "expires_in_version": "never",
+    "kind": "enumerated",
+    "n_values": 20,
+    "alert_emails": ["seceng-telemetry@mozilla.com"],
+    "bug_numbers": [1254099],
+    "description": "Update of addons blocklist from settings server result (0=Success, 1=Unknown failure, 2=Synchronization failure, 3=Conflict error, 4=Signature verification failure, 5=Signature recovery failure, 6=Application failure)"
+  },
+  "SERVICE_SETTINGS_UPDATE_BLOCKLISTS_CERTIFICATES_RESULT": {
+    "expires_in_version": "never",
+    "kind": "enumerated",
+    "n_values": 20,
+    "alert_emails": ["seceng-telemetry@mozilla.com"],
+    "bug_numbers": [1254099],
+    "description": "Update of OneCRL blocklist from settings server result (0=Success, 1=Unknown failure, 2=Synchronization failure, 3=Conflict error, 4=Signature verification failure, 5=Signature recovery failure, 6=Application failure)"
+  },
+  "SERVICE_SETTINGS_UPDATE_BLOCKLISTS_GFX_RESULT": {
+    "expires_in_version": "never",
+    "kind": "enumerated",
+    "n_values": 20,
+    "alert_emails": ["seceng-telemetry@mozilla.com"],
+    "bug_numbers": [1254099],
+    "description": "Update of GFX blocklist from settings server result (0=Success, 1=Unknown failure, 2=Synchronization failure, 3=Conflict error, 4=Signature verification failure, 5=Signature recovery failure, 6=Application failure)"
+  },
+  "SERVICE_SETTINGS_UPDATE_BLOCKLISTS_PLUGINS_RESULT": {
+    "expires_in_version": "never",
+    "kind": "enumerated",
+    "n_values": 20,
+    "alert_emails": ["seceng-telemetry@mozilla.com"],
+    "bug_numbers": [1254099],
+    "description": "Update of plugins blocklist from settings server result (0=Success, 1=Unknown failure, 2=Synchronization failure, 3=Conflict error, 4=Signature verification failure, 5=Signature recovery failure, 6=Application failure)"
+  },
+  "SERVICE_SETTINGS_UPDATE_PINNING_PINS_RESULT": {
+    "expires_in_version": "never",
+    "kind": "enumerated",
+    "n_values": 20,
+    "alert_emails": ["seceng-telemetry@mozilla.com"],
+    "bug_numbers": [1254099],
+    "description": "Update of certicates pinning from settings server result (0=Success, 1=Unknown failure, 2=Synchronization failure, 3=Conflict error, 4=Signature verification failure, 5=Signature recovery failure, 6=Application failure)"
   },
   "E10S_STATUS": {
     "alert_emails": ["firefox-dev@mozilla.org"],
     "expires_in_version": "never",
     "kind": "enumerated",
     "n_values": 12,
     "releaseChannelCollection": "opt-out",
     "bug_numbers": [1241294],
--- a/toolkit/mozapps/extensions/nsBlocklistService.js
+++ b/toolkit/mozapps/extensions/nsBlocklistService.js
@@ -620,25 +620,17 @@ Blocklist.prototype = {
     if (!this._isBlocklistLoaded())
       this._loadBlocklist();
 
     // If kinto update is enabled, do the kinto update
     if (gPref.getBoolPref(PREF_BLOCKLIST_UPDATE_ENABLED)) {
       const updater =
         Components.utils.import("resource://services-common/blocklist-updater.js",
                                 {});
-      updater.checkVersions().then(() => {
-        Services.telemetry
-            .getHistogramById("SERVICES_SETTINGS_SUCCESS")
-            .add(SERVICES_SETTINGS_SYNC_OK);
-      }).catch(() => {
-        Services.telemetry
-            .getHistogramById("SERVICES_SETTINGS_SUCCESS")
-            .add(SERVICES_SETTINGS_SYNC_FAILED);
-      });
+      updater.checkVersions();
     }
   },
 
   onXMLLoad: Task.async(function*(aEvent) {
     let request = aEvent.target;
     try {
       gCertUtils.checkCert(request.channel);
     } catch (e) {