diff options
Diffstat (limited to 'toolkit/devtools/shared/telemetry.js')
-rw-r--r-- | toolkit/devtools/shared/telemetry.js | 315 |
1 files changed, 315 insertions, 0 deletions
diff --git a/toolkit/devtools/shared/telemetry.js b/toolkit/devtools/shared/telemetry.js new file mode 100644 index 000000000..5221da113 --- /dev/null +++ b/toolkit/devtools/shared/telemetry.js @@ -0,0 +1,315 @@ +/* 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/. */ + +/** + * Telemetry. + * + * To add metrics for a tool: + * + * 1. Create boolean, flag and exponential entries in + * toolkit/components/telemetry/Histograms.json. Each type is optional but it + * is best if all three can be included. + * + * 2. Add your chart entries to browser/devtools/shared/telemetry.js + * (Telemetry.prototype._histograms): + * mytoolname: { + * histogram: "DEVTOOLS_MYTOOLNAME_OPENED_BOOLEAN", + * userHistogram: "DEVTOOLS_MYTOOLNAME_OPENED_PER_USER_FLAG", + * timerHistogram: "DEVTOOLS_MYTOOLNAME_TIME_ACTIVE_SECONDS" + * }, + * + * 3. Include this module at the top of your tool. Use: + * let Telemetry = require("devtools/shared/telemetry") + * + * 4. Create a telemetry instance in your tool's constructor: + * this._telemetry = new Telemetry(); + * + * 5. When your tool is opened call: + * this._telemetry.toolOpened("mytoolname"); + * + * 6. When your tool is closed call: + * this._telemetry.toolClosed("mytoolname"); + * + * Note: + * You can view telemetry stats for your local Firefox instance via + * about:telemetry. + * + * You can view telemetry stats for large groups of Firefox users at + * telemetry.mozilla.org. + */ + +const TOOLS_OPENED_PREF = "devtools.telemetry.tools.opened.version"; + +this.Telemetry = function() { + // 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.logOncePerBrowserVersion = this.logOncePerBrowserVersion.bind(this); + this.destroy = this.destroy.bind(this); + + this._timers = new Map(); +}; + +module.exports = Telemetry; + +let {Cc, Ci, Cu} = require("chrome"); +let {Services} = Cu.import("resource://gre/modules/Services.jsm", {}); +let {XPCOMUtils} = Cu.import("resource://gre/modules/XPCOMUtils.jsm", {}); + +Telemetry.prototype = { + _histograms: { + toolbox: { + histogram: "DEVTOOLS_TOOLBOX_OPENED_BOOLEAN", + userHistogram: "DEVTOOLS_TOOLBOX_OPENED_PER_USER_FLAG", + timerHistogram: "DEVTOOLS_TOOLBOX_TIME_ACTIVE_SECONDS" + }, + options: { + histogram: "DEVTOOLS_OPTIONS_OPENED_BOOLEAN", + userHistogram: "DEVTOOLS_OPTIONS_OPENED_PER_USER_FLAG", + timerHistogram: "DEVTOOLS_OPTIONS_TIME_ACTIVE_SECONDS" + }, + webconsole: { + histogram: "DEVTOOLS_WEBCONSOLE_OPENED_BOOLEAN", + userHistogram: "DEVTOOLS_WEBCONSOLE_OPENED_PER_USER_FLAG", + timerHistogram: "DEVTOOLS_WEBCONSOLE_TIME_ACTIVE_SECONDS" + }, + browserconsole: { + histogram: "DEVTOOLS_BROWSERCONSOLE_OPENED_BOOLEAN", + userHistogram: "DEVTOOLS_BROWSERCONSOLE_OPENED_PER_USER_FLAG", + timerHistogram: "DEVTOOLS_BROWSERCONSOLE_TIME_ACTIVE_SECONDS" + }, + inspector: { + histogram: "DEVTOOLS_INSPECTOR_OPENED_BOOLEAN", + userHistogram: "DEVTOOLS_INSPECTOR_OPENED_PER_USER_FLAG", + timerHistogram: "DEVTOOLS_INSPECTOR_TIME_ACTIVE_SECONDS" + }, + ruleview: { + histogram: "DEVTOOLS_RULEVIEW_OPENED_BOOLEAN", + userHistogram: "DEVTOOLS_RULEVIEW_OPENED_PER_USER_FLAG", + timerHistogram: "DEVTOOLS_RULEVIEW_TIME_ACTIVE_SECONDS" + }, + computedview: { + histogram: "DEVTOOLS_COMPUTEDVIEW_OPENED_BOOLEAN", + userHistogram: "DEVTOOLS_COMPUTEDVIEW_OPENED_PER_USER_FLAG", + timerHistogram: "DEVTOOLS_COMPUTEDVIEW_TIME_ACTIVE_SECONDS" + }, + layoutview: { + histogram: "DEVTOOLS_LAYOUTVIEW_OPENED_BOOLEAN", + userHistogram: "DEVTOOLS_LAYOUTVIEW_OPENED_PER_USER_FLAG", + timerHistogram: "DEVTOOLS_LAYOUTVIEW_TIME_ACTIVE_SECONDS" + }, + fontinspector: { + histogram: "DEVTOOLS_FONTINSPECTOR_OPENED_BOOLEAN", + userHistogram: "DEVTOOLS_FONTINSPECTOR_OPENED_PER_USER_FLAG", + timerHistogram: "DEVTOOLS_FONTINSPECTOR_TIME_ACTIVE_SECONDS" + }, + animationinspector: { + histogram: "DEVTOOLS_ANIMATIONINSPECTOR_OPENED_BOOLEAN", + userHistogram: "DEVTOOLS_ANIMATIONINSPECTOR_OPENED_PER_USER_FLAG", + timerHistogram: "DEVTOOLS_ANIMATIONINSPECTOR_TIME_ACTIVE_SECONDS" + }, + jsdebugger: { + histogram: "DEVTOOLS_JSDEBUGGER_OPENED_BOOLEAN", + userHistogram: "DEVTOOLS_JSDEBUGGER_OPENED_PER_USER_FLAG", + timerHistogram: "DEVTOOLS_JSDEBUGGER_TIME_ACTIVE_SECONDS" + }, + jsbrowserdebugger: { + histogram: "DEVTOOLS_JSBROWSERDEBUGGER_OPENED_BOOLEAN", + userHistogram: "DEVTOOLS_JSBROWSERDEBUGGER_OPENED_PER_USER_FLAG", + timerHistogram: "DEVTOOLS_JSBROWSERDEBUGGER_TIME_ACTIVE_SECONDS" + }, + styleeditor: { + histogram: "DEVTOOLS_STYLEEDITOR_OPENED_BOOLEAN", + userHistogram: "DEVTOOLS_STYLEEDITOR_OPENED_PER_USER_FLAG", + timerHistogram: "DEVTOOLS_STYLEEDITOR_TIME_ACTIVE_SECONDS" + }, + shadereditor: { + histogram: "DEVTOOLS_SHADEREDITOR_OPENED_BOOLEAN", + userHistogram: "DEVTOOLS_SHADEREDITOR_OPENED_PER_USER_FLAG", + timerHistogram: "DEVTOOLS_SHADEREDITOR_TIME_ACTIVE_SECONDS" + }, + webaudioeditor: { + histogram: "DEVTOOLS_WEBAUDIOEDITOR_OPENED_BOOLEAN", + userHistogram: "DEVTOOLS_WEBAUDIOEDITOR_OPENED_PER_USER_FLAG", + timerHistogram: "DEVTOOLS_WEBAUDIOEDITOR_TIME_ACTIVE_SECONDS" + }, + canvasdebugger: { + histogram: "DEVTOOLS_CANVASDEBUGGER_OPENED_BOOLEAN", + userHistogram: "DEVTOOLS_CANVASDEBUGGER_OPENED_PER_USER_FLAG", + timerHistogram: "DEVTOOLS_CANVASDEBUGGER_TIME_ACTIVE_SECONDS" + }, + jsprofiler: { + histogram: "DEVTOOLS_JSPROFILER_OPENED_BOOLEAN", + userHistogram: "DEVTOOLS_JSPROFILER_OPENED_PER_USER_FLAG", + timerHistogram: "DEVTOOLS_JSPROFILER_TIME_ACTIVE_SECONDS" + }, + netmonitor: { + histogram: "DEVTOOLS_NETMONITOR_OPENED_BOOLEAN", + userHistogram: "DEVTOOLS_NETMONITOR_OPENED_PER_USER_FLAG", + timerHistogram: "DEVTOOLS_NETMONITOR_TIME_ACTIVE_SECONDS" + }, + storage: { + histogram: "DEVTOOLS_STORAGE_OPENED_BOOLEAN", + userHistogram: "DEVTOOLS_STORAGE_OPENED_PER_USER_FLAG", + timerHistogram: "DEVTOOLS_STORAGE_TIME_ACTIVE_SECONDS" + }, + tilt: { + histogram: "DEVTOOLS_TILT_OPENED_BOOLEAN", + userHistogram: "DEVTOOLS_TILT_OPENED_PER_USER_FLAG", + timerHistogram: "DEVTOOLS_TILT_TIME_ACTIVE_SECONDS" + }, + paintflashing: { + histogram: "DEVTOOLS_PAINTFLASHING_OPENED_BOOLEAN", + userHistogram: "DEVTOOLS_PAINTFLASHING_OPENED_PER_USER_FLAG", + timerHistogram: "DEVTOOLS_PAINTFLASHING_TIME_ACTIVE_SECONDS" + }, + scratchpad: { + histogram: "DEVTOOLS_SCRATCHPAD_OPENED_BOOLEAN", + userHistogram: "DEVTOOLS_SCRATCHPAD_OPENED_PER_USER_FLAG", + timerHistogram: "DEVTOOLS_SCRATCHPAD_TIME_ACTIVE_SECONDS" + }, + responsive: { + histogram: "DEVTOOLS_RESPONSIVE_OPENED_BOOLEAN", + userHistogram: "DEVTOOLS_RESPONSIVE_OPENED_PER_USER_FLAG", + timerHistogram: "DEVTOOLS_RESPONSIVE_TIME_ACTIVE_SECONDS" + }, + eyedropper: { + histogram: "DEVTOOLS_EYEDROPPER_OPENED_BOOLEAN", + userHistogram: "DEVTOOLS_EYEDROPPER_OPENED_PER_USER_FLAG", + }, + menueyedropper: { + histogram: "DEVTOOLS_MENU_EYEDROPPER_OPENED_BOOLEAN", + userHistogram: "DEVTOOLS_MENU_EYEDROPPER_OPENED_PER_USER_FLAG", + }, + pickereyedropper: { + histogram: "DEVTOOLS_PICKER_EYEDROPPER_OPENED_BOOLEAN", + userHistogram: "DEVTOOLS_PICKER_EYEDROPPER_OPENED_PER_USER_FLAG", + }, + developertoolbar: { + histogram: "DEVTOOLS_DEVELOPERTOOLBAR_OPENED_BOOLEAN", + userHistogram: "DEVTOOLS_DEVELOPERTOOLBAR_OPENED_PER_USER_FLAG", + timerHistogram: "DEVTOOLS_DEVELOPERTOOLBAR_TIME_ACTIVE_SECONDS" + }, + webide: { + histogram: "DEVTOOLS_WEBIDE_OPENED_BOOLEAN", + userHistogram: "DEVTOOLS_WEBIDE_OPENED_PER_USER_FLAG", + timerHistogram: "DEVTOOLS_WEBIDE_TIME_ACTIVE_SECONDS" + }, + custom: { + histogram: "DEVTOOLS_CUSTOM_OPENED_BOOLEAN", + userHistogram: "DEVTOOLS_CUSTOM_OPENED_PER_USER_FLAG", + timerHistogram: "DEVTOOLS_CUSTOM_TIME_ACTIVE_SECONDS" + } + }, + + /** + * Add an entry to a histogram. + * + * @param {String} id + * Used to look up the relevant histogram ID and log true to that + * histogram. + */ + toolOpened: function(id) { + let charts = this._histograms[id] || this._histograms.custom; + + if (charts.histogram) { + this.log(charts.histogram, true); + } + if (charts.userHistogram) { + this.logOncePerBrowserVersion(charts.userHistogram, true); + } + if (charts.timerHistogram) { + this.startTimer(charts.timerHistogram); + } + }, + + toolClosed: function(id) { + let charts = this._histograms[id]; + + if (!charts || !charts.timerHistogram) { + return; + } + + this.stopTimer(charts.timerHistogram); + }, + + /** + * Record the start time for a timing-based histogram entry. + * + * @param String histogramId + * Histogram in which the data is to be stored. + */ + startTimer: function(histogramId) { + this._timers.set(histogramId, new Date()); + }, + + /** + * Stop the timer and log elasped time for a timing-based histogram entry. + * + * @param String histogramId + * Histogram in which the data is to be stored. + */ + stopTimer: function(histogramId) { + let startTime = this._timers.get(histogramId); + if (startTime) { + let time = (new Date() - startTime) / 1000; + this.log(histogramId, 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: function(histogramId, value) { + if (histogramId) { + 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"); + } + } + }, + + /** + * 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: function(perUserHistogram, value) { + let currentVersion = appInfo.version; + let latest = Services.prefs.getCharPref(TOOLS_OPENED_PREF); + let latestObj = JSON.parse(latest); + + 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); + } + }, + + destroy: function() { + for (let histogramId of this._timers.keys()) { + this.stopTimer(histogramId); + } + } +}; + +XPCOMUtils.defineLazyGetter(this, "appInfo", function() { + return Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULAppInfo); +}); |