Bug 1458203 - telemetry.js: Rename all log* methods r?jdescottes draft
authorMichael Ratcliffe <mratcliffe@mozilla.com>
Wed, 09 May 2018 09:53:49 +0100
changeset 793651 1b39ee8c7a02b8d43593c095c661e6143730958b
parent 793594 b52b2eb81d1e52d259d55d948281c7f6ddf1270c
child 793652 c263bba6582c4a41965931a288d21ec560595f98
child 794183 733500951f66085b57cedb102ae215227c2450fc
push id109458
push userbmo:mratcliffe@mozilla.com
push dateThu, 10 May 2018 15:28:05 +0000
reviewersjdescottes
bugs1458203
milestone62.0a1
Bug 1458203 - telemetry.js: Rename all log* methods r?jdescottes MozReview-Commit-ID: 9QcRGkXCq71
devtools/client/accessibility/accessibility-panel.js
devtools/client/accessibility/components/Description.js
devtools/client/framework/devtools.js
devtools/client/framework/toolbox-tabs-order-manager.js
devtools/client/framework/toolbox.js
devtools/client/inspector/grids/grid-inspector.js
devtools/client/inspector/inspector.js
devtools/client/performance/modules/logic/telemetry.js
devtools/client/shared/telemetry.js
devtools/client/webide/content/webide.js
devtools/client/webide/modules/app-manager.js
devtools/shared/gcli/source/lib/gcli/mozui/inputter.js
devtools/startup/devtools-startup.js
--- a/devtools/client/accessibility/accessibility-panel.js
+++ b/devtools/client/accessibility/accessibility-panel.js
@@ -158,17 +158,17 @@ AccessibilityPanel.prototype = {
   },
 
   selectAccessible(accessibleFront) {
     this.postContentMessage("selectAccessible", this._walker, accessibleFront);
   },
 
   selectAccessibleForNode(nodeFront, reason) {
     if (reason) {
-      this._telemetry.logKeyedScalar(
+      this._telemetry.keyedScalarAdd(
         "devtools.accessibility.select_accessible_for_node", reason, 1);
     }
 
     this.postContentMessage("selectNodeAccessible", this._walker, nodeFront);
   },
 
   highlightAccessible(accessibleFront) {
     this.postContentMessage("highlightAccessible", this._walker, accessibleFront);
--- a/devtools/client/accessibility/components/Description.js
+++ b/devtools/client/accessibility/components/Description.js
@@ -66,17 +66,17 @@ class Description extends Component {
       this.onCanBeEnabledChange);
   }
 
   onEnable() {
     let { accessibility, dispatch } = this.props;
     this.setState({ enabling: true });
 
     if (gTelemetry) {
-      gTelemetry.logCountScalar(A11Y_SERVICE_ENABLED_COUNT, 1);
+      gTelemetry.scalarAdd(A11Y_SERVICE_ENABLED_COUNT, 1);
     }
 
     dispatch(enable(accessibility))
       .then(() => this.setState({ enabling: false }))
       .catch(() => this.setState({ enabling: false }));
   }
 
   onCanBeEnabledChange(canBeEnabled) {
--- a/devtools/client/framework/devtools.js
+++ b/devtools/client/framework/devtools.js
@@ -502,17 +502,17 @@ DevTools.prototype = {
    *        Indicates the time at which the user event related to the toolbox
    *        opening started. This is a `Cu.now()` timing.
    */
   logToolboxOpenTime(toolId, startTime) {
     let delay = Cu.now() - startTime;
 
     let telemetryKey = this._firstShowToolbox ?
       "DEVTOOLS_COLD_TOOLBOX_OPEN_DELAY_MS" : "DEVTOOLS_WARM_TOOLBOX_OPEN_DELAY_MS";
-    this._telemetry.logKeyed(telemetryKey, toolId, delay);
+    this._telemetry.getKeyedHistogramById(telemetryKey).add(toolId, delay);
 
     this._telemetry.addEventProperty(
       "devtools.main", "open", "tools", null, "first_panel",
       this.makeToolIdHumanReadable(toolId));
   },
 
   makeToolIdHumanReadable(toolId) {
     if (/^[0-9a-fA-F]{40}_temporary-addon/.test(toolId)) {
--- a/devtools/client/framework/toolbox-tabs-order-manager.js
+++ b/devtools/client/framework/toolbox-tabs-order-manager.js
@@ -139,17 +139,17 @@ class ToolboxTabsOrderManager {
             .filter(definition => !tabs.some(tab => tab.dataset.id === definition.id))
             .map(definition => definition.extensionId || definition.id);
       const pref = tabIds.concat(overflowedTabIds).join(",");
       Services.prefs.setCharPref(PREFERENCE_NAME, pref);
 
       // Log which tabs reordered. The question we want to answer is:
       // "How frequently are the tabs re-ordered, also which tabs get re-ordered?"
       const toolId = this.dragTarget.dataset.extensionId || this.dragTarget.dataset.id;
-      this.telemetry.logKeyedScalar(TABS_REORDERED_SCALAR, toolId, 1);
+      this.telemetry.keyedScalarAdd(TABS_REORDERED_SCALAR, toolId, 1);
     }
 
     this.dragTarget.ownerDocument.removeEventListener("mousemove", this.onMouseMove);
     this.dragTarget.ownerDocument.removeEventListener("mouseout", this.onMouseOut);
     this.dragTarget.ownerDocument.removeEventListener("mouseup", this.onMouseUp);
 
     this.toolboxContainerElement.classList.remove("tabs-reordering");
     this.dragTarget.style.left = null;
--- a/devtools/client/framework/toolbox.js
+++ b/devtools/client/framework/toolbox.js
@@ -714,22 +714,22 @@ Toolbox.prototype = {
     }
   },
 
   _pingTelemetry: function() {
     this.telemetry.toolOpened("toolbox");
 
     this.telemetry.logOncePerBrowserVersion(SCREENSIZE_HISTOGRAM,
                                              system.getScreenDimensions());
-    this.telemetry.log(HOST_HISTOGRAM, this._getTelemetryHostId());
+    this.telemetry.getHistogramById(HOST_HISTOGRAM).add(this._getTelemetryHostId());
 
     // Log current theme. The question we want to answer is:
     // "What proportion of users use which themes?"
     let currentTheme = Services.prefs.getCharPref("devtools.theme");
-    this.telemetry.logKeyedScalar(CURRENT_THEME_SCALAR, currentTheme, 1);
+    this.telemetry.keyedScalarAdd(CURRENT_THEME_SCALAR, currentTheme, 1);
 
     this.telemetry.preparePendingEvent(
       "devtools.main", "open", "tools", null,
       ["entrypoint", "first_panel", "host", "splitconsole", "width"]
     );
     this.telemetry.addEventProperty(
       "devtools.main", "open", "tools", null, "host", this._getTelemetryHostString()
     );
@@ -2128,18 +2128,17 @@ Toolbox.prototype = {
     // Ignore the timing if the panel is still loading
     if (!panel) {
       return;
     }
     await panel.once("reloaded");
     let delay = this.win.performance.now() - start;
 
     let telemetryKey = "DEVTOOLS_TOOLBOX_PAGE_RELOAD_DELAY_MS";
-    let histogram = Services.telemetry.getKeyedHistogramById(telemetryKey);
-    histogram.add(toolId, delay);
+    this.telemetry.getKeyedHistogramById(telemetryKey).add(toolId, delay);
   },
 
   /**
    * Refresh the host's title.
    */
   _refreshHostTitle: function() {
     let title;
     if (this.target.name && this.target.name != this.target.url) {
@@ -2464,17 +2463,17 @@ Toolbox.prototype = {
     this._buildDockOptions();
     this._addKeysToWindow();
 
     // We blurred the tools at start of switchHost, but also when clicking on
     // host switching button. We now have to restore the focus.
     this.focusTool(this.currentToolId, true);
 
     this.emit("host-changed");
-    this.telemetry.log(HOST_HISTOGRAM, this._getTelemetryHostId());
+    this.telemetry.getHistogramById(HOST_HISTOGRAM).add(this._getTelemetryHostId());
 
     this.component.setCurrentHostType(hostType);
   },
 
   /**
    * Test the availability of a tool (both globally registered tools and
    * additional tools registered to this toolbox) by tool id.
    *
--- a/devtools/client/inspector/grids/grid-inspector.js
+++ b/devtools/client/inspector/grids/grid-inspector.js
@@ -260,17 +260,17 @@ class GridInspector {
       // This call might fail if called asynchrously after the toolbox is finished
       // closing.
       return;
     }
 
     // Log how many CSS Grid elements DevTools sees.
     if (gridFronts.length > 0 &&
         currentUrl != this.inspector.previousURL) {
-      this.telemetry.log(CSS_GRID_COUNT_HISTOGRAM_ID, gridFronts.length);
+      this.telemetry.getHistogramById(CSS_GRID_COUNT_HISTOGRAM_ID).add(gridFronts.length);
       this.inspector.previousURL = currentUrl;
     }
 
     let grids = [];
     for (let i = 0; i < gridFronts.length; i++) {
       let grid = gridFronts[i];
 
       let nodeFront = grid.containerNodeFront;
--- a/devtools/client/inspector/inspector.js
+++ b/devtools/client/inspector/inspector.js
@@ -269,17 +269,17 @@ Inspector.prototype = {
     // Accessibility panel initializes the Inspector.
     if (this.show3PaneTooltip && this.toolbox.currentToolId === "inspector") {
       this.threePaneTooltip = new ThreePaneOnboardingTooltip(this.toolbox, this.panelDoc);
     }
 
     // Log the 3 pane inspector setting on inspector open. The question we want to answer
     // is:
     // "What proportion of users use the 3 pane vs 2 pane inspector on inspector open?"
-    this.telemetry.logKeyedScalar(THREE_PANE_ENABLED_SCALAR, this.is3PaneModeEnabled, 1);
+    this.telemetry.keyedScalarAdd(THREE_PANE_ENABLED_SCALAR, this.is3PaneModeEnabled, 1);
 
     this.emit("ready");
     return this;
   },
 
   _onBeforeNavigate: function() {
     this._defaultNode = null;
     this.selection.setNodeFront(null);
@@ -2132,45 +2132,45 @@ Inspector.prototype = {
   /**
    * Copy a unique selector of the selected Node to the clipboard.
    */
   copyUniqueSelector: function() {
     if (!this.selection.isNode()) {
       return;
     }
 
-    this.telemetry.logScalar("devtools.copy.unique.css.selector.opened", 1);
+    this.telemetry.scalarSet("devtools.copy.unique.css.selector.opened", 1);
     this.selection.nodeFront.getUniqueSelector().then(selector => {
       clipboardHelper.copyString(selector);
     }).catch(console.error);
   },
 
   /**
    * Copy the full CSS Path of the selected Node to the clipboard.
    */
   copyCssPath: function() {
     if (!this.selection.isNode()) {
       return;
     }
 
-    this.telemetry.logScalar("devtools.copy.full.css.selector.opened", 1);
+    this.telemetry.scalarSet("devtools.copy.full.css.selector.opened", 1);
     this.selection.nodeFront.getCssPath().then(path => {
       clipboardHelper.copyString(path);
     }).catch(console.error);
   },
 
   /**
    * Copy the XPath of the selected Node to the clipboard.
    */
   copyXPath: function() {
     if (!this.selection.isNode()) {
       return;
     }
 
-    this.telemetry.logScalar("devtools.copy.xpath.opened", 1);
+    this.telemetry.scalarSet("devtools.copy.xpath.opened", 1);
     this.selection.nodeFront.getXPath().then(path => {
       clipboardHelper.copyString(path);
     }).catch(console.error);
   },
 
   /**
    * Initiate gcli screenshot command on selected node.
    */
--- a/devtools/client/performance/modules/logic/telemetry.js
+++ b/devtools/client/performance/modules/logic/telemetry.js
@@ -1,15 +1,14 @@
 /* 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 http://mozilla.org/MPL/2.0/. */
 "use strict";
 
 const Telemetry = require("devtools/client/shared/telemetry");
-const flags = require("devtools/shared/flags");
 const EVENTS = require("devtools/client/performance/events");
 
 const EVENT_MAP_FLAGS = new Map([
   [EVENTS.RECORDING_IMPORTED, "DEVTOOLS_PERFTOOLS_RECORDING_IMPORT_FLAG"],
   [EVENTS.RECORDING_EXPORTED, "DEVTOOLS_PERFTOOLS_RECORDING_EXPORT_FLAG"],
 ]);
 
 const RECORDING_FEATURES = [
@@ -26,20 +25,16 @@ function PerformanceTelemetry(emitter) {
   this.onViewSelected = this.onViewSelected.bind(this);
 
   for (let [event] of EVENT_MAP_FLAGS) {
     this._emitter.on(event, this.onFlagEvent.bind(this, event));
   }
 
   this._emitter.on(EVENTS.RECORDING_STATE_CHANGE, this.onRecordingStateChange);
   this._emitter.on(EVENTS.UI_DETAILS_VIEW_SELECTED, this.onViewSelected);
-
-  if (flags.testing) {
-    this.recordLogs();
-  }
 }
 
 PerformanceTelemetry.prototype.destroy = function() {
   if (this._previousView) {
     this._telemetry.stopTimer(SELECTED_VIEW_HISTOGRAM_NAME, this._previousView);
   }
 
   this._telemetry.destroy();
@@ -47,76 +42,46 @@ PerformanceTelemetry.prototype.destroy =
     this._emitter.off(event, this.onFlagEvent);
   }
   this._emitter.off(EVENTS.RECORDING_STATE_CHANGE, this.onRecordingStateChange);
   this._emitter.off(EVENTS.UI_DETAILS_VIEW_SELECTED, this.onViewSelected);
   this._emitter = null;
 };
 
 PerformanceTelemetry.prototype.onFlagEvent = function(eventName, ...data) {
-  this._telemetry.log(EVENT_MAP_FLAGS.get(eventName), true);
+  this._telemetry.getHistogramById(EVENT_MAP_FLAGS.get(eventName)).add(true);
 };
 
 PerformanceTelemetry.prototype.onRecordingStateChange = function(status, model) {
   if (status != "recording-stopped") {
     return;
   }
 
   if (model.isConsole()) {
-    this._telemetry.log("DEVTOOLS_PERFTOOLS_CONSOLE_RECORDING_COUNT", true);
+    this._telemetry.getHistogramById("DEVTOOLS_PERFTOOLS_CONSOLE_RECORDING_COUNT")
+                   .add(true);
   } else {
-    this._telemetry.log("DEVTOOLS_PERFTOOLS_RECORDING_COUNT", true);
+    this._telemetry.getHistogramById("DEVTOOLS_PERFTOOLS_RECORDING_COUNT")
+                   .add(true);
   }
 
-  this._telemetry.log("DEVTOOLS_PERFTOOLS_RECORDING_DURATION_MS", model.getDuration());
+  this._telemetry.getHistogramById("DEVTOOLS_PERFTOOLS_RECORDING_DURATION_MS")
+                 .add(model.getDuration());
 
   let config = model.getConfiguration();
   for (let k in config) {
     if (RECORDING_FEATURES.includes(k)) {
-      this._telemetry.logKeyed("DEVTOOLS_PERFTOOLS_RECORDING_FEATURES_USED", k,
-                               config[k]);
+      this._telemetry
+          .getKeyedHistogramById("DEVTOOLS_PERFTOOLS_RECORDING_FEATURES_USED")
+          .add(k, config[k]);
     }
   }
 };
 
 PerformanceTelemetry.prototype.onViewSelected = function(viewName) {
   if (this._previousView) {
     this._telemetry.stopTimer(SELECTED_VIEW_HISTOGRAM_NAME, this._previousView);
   }
   this._previousView = viewName;
   this._telemetry.startTimer(SELECTED_VIEW_HISTOGRAM_NAME);
 };
 
-/**
- * Utility to record histogram calls to this instance.
- * Should only be used in testing mode; throws otherwise.
- */
-PerformanceTelemetry.prototype.recordLogs = function() {
-  if (!flags.testing) {
-    throw new Error("Can only record telemetry logs in tests.");
-  }
-
-  let originalLog = this._telemetry.log;
-  let originalLogKeyed = this._telemetry.logKeyed;
-  this._log = {};
-
-  this._telemetry.log = (histo, data) => {
-    let results = this._log[histo] = this._log[histo] || [];
-    results.push(data);
-    originalLog(histo, data);
-  };
-
-  this._telemetry.logKeyed = (histo, key, data) => {
-    let results = this._log[histo] = this._log[histo] || [];
-    results.push([key, data]);
-    originalLogKeyed(histo, key, data);
-  };
-};
-
-PerformanceTelemetry.prototype.getLogs = function() {
-  if (!flags.testing) {
-    throw new Error("Can only get telemetry logs in tests.");
-  }
-
-  return this._log;
-};
-
 exports.PerformanceTelemetry = PerformanceTelemetry;
--- a/devtools/client/shared/telemetry.js
+++ b/devtools/client/shared/telemetry.js
@@ -18,20 +18,21 @@ const TOOLS_OPENED_PREF = "devtools.tele
 const PENDING_EVENTS = new Map();
 const PENDING_EVENT_PROPERTIES = new Map();
 
 class Telemetry {
   constructor() {
     // Bind pretty much all functions so that callers do not need to.
     this.toolOpened = this.toolOpened.bind(this);
     this.toolClosed = this.toolClosed.bind(this);
-    this.log = this.log.bind(this);
-    this.logScalar = this.logScalar.bind(this);
-    this.logCountScalar = this.logCountScalar.bind(this);
-    this.logKeyedScalar = this.logKeyedScalar.bind(this);
+    this.getHistogramById = this.getHistogramById.bind(this);
+    this.getKeyedHistogramById = this.getKeyedHistogramById.bind(this);
+    this.scalarSet = this.scalarSet.bind(this);
+    this.scalarAdd = this.scalarAdd.bind(this);
+    this.keyedScalarAdd = this.keyedScalarAdd.bind(this);
     this.logOncePerBrowserVersion = this.logOncePerBrowserVersion.bind(this);
     this.recordEvent = this.recordEvent.bind(this);
     this.setEventRecordingEnabled = this.setEventRecordingEnabled.bind(this);
     this.preparePendingEvent = this.preparePendingEvent.bind(this);
     this.addEventProperty = this.addEventProperty.bind(this);
     this.destroy = this.destroy.bind(this);
 
     this._timers = new Map();
@@ -201,26 +202,26 @@ class Telemetry {
    * @param  {String} id
    *         Used to look up the relevant histogram ID and log true to that
    *         histogram.
    */
   toolOpened(id) {
     let charts = this.histograms[id] || this.histograms.custom;
 
     if (charts.histogram) {
-      this.log(charts.histogram, true);
+      this.getHistogramById(charts.histogram).add(true);
     }
     if (charts.timerHistogram) {
       this.startTimer(charts.timerHistogram);
     }
     if (charts.scalar) {
-      this.logScalar(charts.scalar, 1);
+      this.scalarSet(charts.scalar, 1);
     }
     if (charts.countScalar) {
-      this.logCountScalar(charts.countScalar, 1);
+      this.scalarAdd(charts.countScalar, 1);
     }
   }
 
   /**
    * Record that an action occurred.  Aliases to `toolOpened`, so it's just for
    * readability at the call site for cases where we aren't actually opening
    * tools.
    */
@@ -256,56 +257,80 @@ class Telemetry {
    * @param String key [optional]
    *        Optional key for a keyed histogram.
    */
   stopTimer(histogramId, key) {
     let startTime = this._timers.get(histogramId);
     if (startTime) {
       let time = (new Date() - startTime) / 1000;
       if (!key) {
-        this.log(histogramId, time);
+        this.getHistogramById(histogramId).add(time);
       } else {
-        this.logKeyed(histogramId, key, time);
+        this.getKeyedHistogramById(histogramId).add(key, time);
       }
       this._timers.delete(histogramId);
     }
   }
 
   /**
    * Log a value to a histogram.
    *
    * @param  {String} histogramId
    *         Histogram in which the data is to be stored.
-   * @param  value
-   *         Value to store.
    */
-  log(histogramId, value) {
-    if (!histogramId) {
-      return;
+  getHistogramById(histogramId) {
+    let histogram = null;
+
+    if (histogramId) {
+      try {
+        histogram = Services.telemetry.getHistogramById(histogramId);
+      } catch (e) {
+        dump(`Warning: An attempt was made to write to the ${histogramId} ` +
+            `histogram, which is not defined in Histograms.json\n` +
+            `CALLER: ${this.getCaller()}`);
+      }
     }
 
-    try {
-      let histogram = Services.telemetry.getHistogramById(histogramId);
-      histogram.add(value);
-    } catch (e) {
-      dump(`Warning: An attempt was made to write to the ${histogramId} ` +
-           `histogram, which is not defined in Histograms.json\n` +
-           `CALLER: ${this.getCaller()}`);
+    return histogram || {
+      add: () => {}
+    };
+  }
+
+  /**
+   * Get a keyed histogram.
+   *
+   * @param  {String} histogramId
+   *         Histogram in which the data is to be stored.
+   */
+  getKeyedHistogramById(histogramId) {
+    let histogram = null;
+
+    if (histogramId) {
+      try {
+        histogram = Services.telemetry.getKeyedHistogramById(histogramId);
+      } catch (e) {
+        dump(`Warning: An attempt was made to write to the ${histogramId} ` +
+             `histogram, which is not defined in Histograms.json\n` +
+             `CALLER: ${this.getCaller()}`);
+      }
     }
+    return histogram || {
+      add: () => {}
+    };
   }
 
   /**
    * Log a value to a scalar.
    *
    * @param  {String} scalarId
    *         Scalar in which the data is to be stored.
    * @param  value
    *         Value to store.
    */
-  logScalar(scalarId, value) {
+  scalarSet(scalarId, value) {
     if (!scalarId) {
       return;
     }
 
     try {
       if (isNaN(value) && typeof value !== "boolean") {
         dump(`Warning: An attempt was made to write a non-numeric and ` +
              `non-boolean value ${value} to the ${scalarId} scalar. Only ` +
@@ -325,17 +350,17 @@ class Telemetry {
   /**
    * Log a value to a count scalar.
    *
    * @param  {String} scalarId
    *         Scalar in which the data is to be stored.
    * @param  value
    *         Value to store.
    */
-  logCountScalar(scalarId, value) {
+  scalarAdd(scalarId, value) {
     if (!scalarId) {
       return;
     }
 
     try {
       if (isNaN(value)) {
         dump(`Warning: An attempt was made to write a non-numeric value ` +
              `${value} to the ${scalarId} scalar. Only numeric values are ` +
@@ -357,17 +382,17 @@ class Telemetry {
    *
    * @param  {String} scalarId
    *         Scalar in which the data is to be stored.
    * @param  {String} key
    *         The key within the  scalar.
    * @param  value
    *         Value to store.
    */
-  logKeyedScalar(scalarId, key, value) {
+  keyedScalarAdd(scalarId, key, value) {
     if (!scalarId) {
       return;
     }
 
     try {
       if (isNaN(value)) {
         dump(`Warning: An attempt was made to write a non-numeric value ` +
              `${value} to the ${scalarId} scalar. Only numeric values are ` +
@@ -380,44 +405,16 @@ class Telemetry {
     } catch (e) {
       dump(`Warning: An attempt was made to write to the ${scalarId} ` +
            `scalar, which is not defined in Scalars.yaml\n` +
            `CALLER: ${this.getCaller()}`);
     }
   }
 
   /**
-   * Log a value to a keyed histogram.
-   *
-   * @param  {String} histogramId
-   *         Histogram in which the data is to be stored.
-   * @param  {String} key
-   *         The key within the single histogram.
-   * @param  [value]
-   *         Optional value to store.
-   */
-  logKeyed(histogramId, key, value) {
-    if (histogramId) {
-      try {
-        let histogram = Services.telemetry.getKeyedHistogramById(histogramId);
-
-        if (typeof value === "undefined") {
-          histogram.add(key);
-        } else {
-          histogram.add(key, value);
-        }
-      } catch (e) {
-        dump(`Warning: An attempt was made to write to the ${histogramId} ` +
-             `histogram, which is not defined in Histograms.json\n` +
-             `CALLER: ${this.getCaller()}`);
-      }
-    }
-  }
-
-  /**
    * Log info about usage once per browser version. This allows us to discover
    * how many individual users are using our tools for each browser version.
    *
    * @param  {String} perUserHistogram
    *         Histogram in which the data is to be stored.
    */
   logOncePerBrowserVersion(perUserHistogram, value) {
     let currentVersion = Services.appinfo.version;
@@ -426,17 +423,17 @@ class Telemetry {
 
     let lastVersionHistogramUpdated = latestObj[perUserHistogram];
 
     if (typeof lastVersionHistogramUpdated == "undefined" ||
         lastVersionHistogramUpdated !== currentVersion) {
       latestObj[perUserHistogram] = currentVersion;
       latest = JSON.stringify(latestObj);
       Services.prefs.setCharPref(TOOLS_OPENED_PREF, latest);
-      this.log(perUserHistogram, value);
+      this.getHistogramById(perUserHistogram).add(value);
     }
   }
 
   /**
    * Event telemetry is disabled by default. Use this method to enable it for
    * a particular category.
    *
    * @param {String} category
@@ -581,16 +578,43 @@ class Telemetry {
    */
   addEventProperties(category, method, object, value, pendingObject) {
     for (let [key, val] of Object.entries(pendingObject)) {
       this.addEventProperty(category, method, object, value, key, val);
     }
   }
 
   /**
+   * A private method that is not to be used externally. This method is used to
+   * prepare a pending telemetry event for sending and then send it via
+   * recordEvent().
+   *
+   * @param {String} category
+   *        The telemetry event category (a group name for events and helps to
+   *        avoid name conflicts) e.g. "devtools.main"
+   * @param {String} method
+   *        The telemetry event method (describes the type of event that
+   *        occurred e.g. "open")
+   * @param {String} object
+   *        The telemetry event object name (the name of the object the event
+   *        occurred on) e.g. "tools" or "setting"
+   * @param {String|null} value
+   *        The telemetry event value (a user defined value, providing context
+   *        for the event) e.g. "console"
+   */
+  _sendPendingEvent(category, method, object, value) {
+    const sig = `${category},${method},${object},${value}`;
+    const { extra } = PENDING_EVENTS.get(sig);
+
+    PENDING_EVENTS.delete(sig);
+    PENDING_EVENT_PROPERTIES.delete(sig);
+    this.recordEvent(category, method, object, value, extra);
+  }
+
+  /**
    * Send a telemetry event.
    *
    * @param {String} category
    *        The telemetry event category (a group name for events and helps to
    *        avoid name conflicts) e.g. "devtools.main"
    * @param {String} method
    *        The telemetry event method (describes the type of event that
    *        occurred e.g. "open")
@@ -623,43 +647,16 @@ class Telemetry {
                         `of 80 characters\n` +
                         `CALLER: ${this.getCaller()}`);
       }
     }
     Services.telemetry.recordEvent(category, method, object, value, extra);
   }
 
   /**
-   * A private method that is not to be used externally. This method is used to
-   * prepare a pending telemetry event for sending and then send it via
-   * recordEvent().
-   *
-   * @param {String} category
-   *        The telemetry event category (a group name for events and helps to
-   *        avoid name conflicts) e.g. "devtools.main"
-   * @param {String} method
-   *        The telemetry event method (describes the type of event that
-   *        occurred e.g. "open")
-   * @param {String} object
-   *        The telemetry event object name (the name of the object the event
-   *        occurred on) e.g. "tools" or "setting"
-   * @param {String|null} value
-   *        The telemetry event value (a user defined value, providing context
-   *        for the event) e.g. "console"
-   */
-  _sendPendingEvent(category, method, object, value) {
-    const sig = `${category},${method},${object},${value}`;
-    const { extra } = PENDING_EVENTS.get(sig);
-
-    PENDING_EVENTS.delete(sig);
-    PENDING_EVENT_PROPERTIES.delete(sig);
-    this.recordEvent(category, method, object, value, extra);
-  }
-
-  /**
    * Displays the first caller and calling line outside of this file in the
    * event of an error. This is the line that made the call that produced the
    * error.
    */
   getCaller() {
     return getNthPathExcluding(0, "/telemetry.js");
   }
 
--- a/devtools/client/webide/content/webide.js
+++ b/devtools/client/webide/content/webide.js
@@ -534,17 +534,17 @@ var UI = {
       this.logActionState(action, false);
     }
     this._actionsToLog.clear();
   },
 
   logActionState: function(action, state) {
     let histogramId = "DEVTOOLS_WEBIDE_CONNECTION_" +
                       action.toUpperCase() + "_USED";
-    this._telemetry.log(histogramId, state);
+    this._telemetry.getHistogramById(histogramId).add(state);
   },
 
   /** ******** PROJECTS **********/
 
   openProject: function() {
     let project = AppManager.selectedProject;
 
     if (!project) {
--- a/devtools/client/webide/modules/app-manager.js
+++ b/devtools/client/webide/modules/app-manager.js
@@ -430,20 +430,21 @@ var AppManager = exports.AppManager = {
         } catch (e) {
           reject(e);
         }
       }, reject);
     });
 
     // Record connection result in telemetry
     let logResult = result => {
-      this._telemetry.log("DEVTOOLS_WEBIDE_CONNECTION_RESULT", result);
+      this._telemetry.getHistogramById("DEVTOOLS_WEBIDE_CONNECTION_RESULT")
+                     .add(result);
       if (runtime.type) {
-        this._telemetry.log("DEVTOOLS_WEBIDE_" + runtime.type +
-                            "_CONNECTION_RESULT", result);
+        this._telemetry.getHistogramById(
+          `DEVTOOLS_WEBIDE_${runtime.type}_CONNECTION_RESULT`).add(result);
       }
     };
     deferred.then(() => logResult(true), () => logResult(false));
 
     // If successful, record connection time in telemetry
     deferred.then(() => {
       const timerId = "DEVTOOLS_WEBIDE_CONNECTION_TIME_SECONDS";
       this._telemetry.startTimer(timerId);
@@ -459,35 +460,42 @@ var AppManager = exports.AppManager = {
     return deferred;
   },
 
   async _recordRuntimeInfo() {
     if (!this.connected) {
       return;
     }
     let runtime = this.selectedRuntime;
-    this._telemetry.logKeyed("DEVTOOLS_WEBIDE_CONNECTED_RUNTIME_TYPE",
-                             runtime.type || "UNKNOWN", true);
-    this._telemetry.logKeyed("DEVTOOLS_WEBIDE_CONNECTED_RUNTIME_ID",
-                             runtime.id || "unknown", true);
+    this._telemetry
+        .getKeyedHistogramById("DEVTOOLS_WEBIDE_CONNECTED_RUNTIME_TYPE")
+        .add(runtime.type || "UNKNOWN", true);
+    this._telemetry
+        .getKeyedHistogramById("DEVTOOLS_WEBIDE_CONNECTED_RUNTIME_ID")
+        .add(runtime.id || "unknown", true);
     if (!this.deviceFront) {
       this.update("runtime-telemetry");
       return;
     }
     let d = await this.deviceFront.getDescription();
-    this._telemetry.logKeyed("DEVTOOLS_WEBIDE_CONNECTED_RUNTIME_PROCESSOR",
-                             d.processor, true);
-    this._telemetry.logKeyed("DEVTOOLS_WEBIDE_CONNECTED_RUNTIME_OS",
-                             d.os, true);
-    this._telemetry.logKeyed("DEVTOOLS_WEBIDE_CONNECTED_RUNTIME_PLATFORM_VERSION",
-                             d.platformversion, true);
-    this._telemetry.logKeyed("DEVTOOLS_WEBIDE_CONNECTED_RUNTIME_APP_TYPE",
-                             d.apptype, true);
-    this._telemetry.logKeyed("DEVTOOLS_WEBIDE_CONNECTED_RUNTIME_VERSION",
-                             d.version, true);
+    this._telemetry
+      .getKeyedHistogramById("DEVTOOLS_WEBIDE_CONNECTED_RUNTIME_PROCESSOR")
+      .add(d.processor, true);
+    this._telemetry
+      .getKeyedHistogramById("DEVTOOLS_WEBIDE_CONNECTED_RUNTIME_OS")
+      .add(d.os, true);
+    this._telemetry
+      .getKeyedHistogramById("DEVTOOLS_WEBIDE_CONNECTED_RUNTIME_PLATFORM_VERSION")
+      .add(d.platformversion, true);
+    this._telemetry
+        .getKeyedHistogramById("DEVTOOLS_WEBIDE_CONNECTED_RUNTIME_APP_TYPE")
+        .add(d.apptype, true);
+    this._telemetry
+        .getKeyedHistogramById("DEVTOOLS_WEBIDE_CONNECTED_RUNTIME_VERSION")
+        .add(d.version, true);
     this.update("runtime-telemetry");
   },
 
   isMainProcessDebuggable: function() {
     // Fx <39 exposes chrome tab actors on RootActor
     // Fx >=39 exposes a dedicated actor via getProcess request
     return this.connection.client &&
            this.connection.client.mainRoot &&
--- a/devtools/shared/gcli/source/lib/gcli/mozui/inputter.js
+++ b/devtools/shared/gcli/source/lib/gcli/mozui/inputter.js
@@ -559,17 +559,18 @@ Inputter.prototype._handleDownArrow = fu
  */
 Inputter.prototype._handleReturn = function() {
   // Deny RETURN unless the command might work
   if (this.requisition.status === Status.VALID) {
     this._scrollingThroughHistory = false;
     this.history.add(this.element.value);
 
     let name = this.requisition.commandAssignment.value.name;
-    this._telemetry.logKeyed("DEVTOOLS_GCLI_COMMANDS_KEYED", name);
+    this._telemetry.getKeyedHistogramById("DEVTOOLS_GCLI_COMMANDS_KEYED")
+        .add(name);
 
     return this.requisition.exec().then(() => {
       this.textChanged();
     });
   }
 
   // If we can't execute the command, but there is a menu choice to use
   // then use it.
--- a/devtools/startup/devtools-startup.js
+++ b/devtools/startup/devtools-startup.js
@@ -330,17 +330,17 @@ DevToolsStartup.prototype = {
   pingOnboardingTelemetry() {
     // Only ping telemetry once per profile.
     let alreadyLoggedPref = "devtools.onboarding.telemetry.logged";
     if (Services.prefs.getBoolPref(alreadyLoggedPref)) {
       return;
     }
 
     let scalarId = "devtools.onboarding.is_devtools_user";
-    this.telemetry.logScalar(scalarId, this.isDevToolsUser());
+    this.telemetry.scalarSet(scalarId, this.isDevToolsUser());
     Services.prefs.setBoolPref(alreadyLoggedPref, true);
   },
 
   /**
    * Register listeners to all possible entry points for Developer Tools.
    * But instead of implementing the actual actions, defer to DevTools codebase.
    * In most cases, it only needs to call this.initDevTools which handles the rest.
    * We do that to prevent loading any DevTools module until the user intent to use them.
@@ -856,17 +856,17 @@ DevToolsStartup.prototype = {
     if (this.recorded) {
       return;
     }
 
     // Only save the first call for each firefox run as next call
     // won't necessarely start the tool. For example key shortcuts may
     // only change the currently selected tool.
     try {
-      this.telemetry.log("DEVTOOLS_ENTRY_POINT", reason);
+      this.telemetry.getHistogramById("DEVTOOLS_ENTRY_POINT").add(reason);
     } catch (e) {
       dump("DevTools telemetry entry point failed: " + e + "\n");
     }
     this.recorded = true;
   },
 
   // Used by tests and the toolbox to register the same key shortcuts in toolboxes loaded
   // in a window window.