diff options
Diffstat (limited to 'devtools/client/shared/telemetry.js')
-rw-r--r-- | devtools/client/shared/telemetry.js | 341 |
1 files changed, 341 insertions, 0 deletions
diff --git a/devtools/client/shared/telemetry.js b/devtools/client/shared/telemetry.js new file mode 100644 index 000000000..64a299581 --- /dev/null +++ b/devtools/client/shared/telemetry.js @@ -0,0 +1,341 @@ +/* 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 count, 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 devtools/client/shared/telemetry.js + * (Telemetry.prototype._histograms): + * mytoolname: { + * histogram: "DEVTOOLS_MYTOOLNAME_OPENED_COUNT", + * timerHistogram: "DEVTOOLS_MYTOOLNAME_TIME_ACTIVE_SECONDS" + * }, + * + * 3. Include this module at the top of your tool. Use: + * let Telemetry = require("devtools/client/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. + */ + +"use strict"; + +const TOOLS_OPENED_PREF = "devtools.telemetry.tools.opened.version"; + +function Telemetry() { + // 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; + +var Services = require("Services"); + +Telemetry.prototype = { + _histograms: { + toolbox: { + histogram: "DEVTOOLS_TOOLBOX_OPENED_COUNT", + timerHistogram: "DEVTOOLS_TOOLBOX_TIME_ACTIVE_SECONDS" + }, + options: { + histogram: "DEVTOOLS_OPTIONS_OPENED_COUNT", + timerHistogram: "DEVTOOLS_OPTIONS_TIME_ACTIVE_SECONDS" + }, + webconsole: { + histogram: "DEVTOOLS_WEBCONSOLE_OPENED_COUNT", + timerHistogram: "DEVTOOLS_WEBCONSOLE_TIME_ACTIVE_SECONDS" + }, + browserconsole: { + histogram: "DEVTOOLS_BROWSERCONSOLE_OPENED_COUNT", + timerHistogram: "DEVTOOLS_BROWSERCONSOLE_TIME_ACTIVE_SECONDS" + }, + inspector: { + histogram: "DEVTOOLS_INSPECTOR_OPENED_COUNT", + timerHistogram: "DEVTOOLS_INSPECTOR_TIME_ACTIVE_SECONDS" + }, + ruleview: { + histogram: "DEVTOOLS_RULEVIEW_OPENED_COUNT", + timerHistogram: "DEVTOOLS_RULEVIEW_TIME_ACTIVE_SECONDS" + }, + computedview: { + histogram: "DEVTOOLS_COMPUTEDVIEW_OPENED_COUNT", + timerHistogram: "DEVTOOLS_COMPUTEDVIEW_TIME_ACTIVE_SECONDS" + }, + fontinspector: { + histogram: "DEVTOOLS_FONTINSPECTOR_OPENED_COUNT", + timerHistogram: "DEVTOOLS_FONTINSPECTOR_TIME_ACTIVE_SECONDS" + }, + animationinspector: { + histogram: "DEVTOOLS_ANIMATIONINSPECTOR_OPENED_COUNT", + timerHistogram: "DEVTOOLS_ANIMATIONINSPECTOR_TIME_ACTIVE_SECONDS" + }, + jsdebugger: { + histogram: "DEVTOOLS_JSDEBUGGER_OPENED_COUNT", + timerHistogram: "DEVTOOLS_JSDEBUGGER_TIME_ACTIVE_SECONDS" + }, + jsbrowserdebugger: { + histogram: "DEVTOOLS_JSBROWSERDEBUGGER_OPENED_COUNT", + timerHistogram: "DEVTOOLS_JSBROWSERDEBUGGER_TIME_ACTIVE_SECONDS" + }, + styleeditor: { + histogram: "DEVTOOLS_STYLEEDITOR_OPENED_COUNT", + timerHistogram: "DEVTOOLS_STYLEEDITOR_TIME_ACTIVE_SECONDS" + }, + shadereditor: { + histogram: "DEVTOOLS_SHADEREDITOR_OPENED_COUNT", + timerHistogram: "DEVTOOLS_SHADEREDITOR_TIME_ACTIVE_SECONDS" + }, + webaudioeditor: { + histogram: "DEVTOOLS_WEBAUDIOEDITOR_OPENED_COUNT", + timerHistogram: "DEVTOOLS_WEBAUDIOEDITOR_TIME_ACTIVE_SECONDS" + }, + canvasdebugger: { + histogram: "DEVTOOLS_CANVASDEBUGGER_OPENED_COUNT", + timerHistogram: "DEVTOOLS_CANVASDEBUGGER_TIME_ACTIVE_SECONDS" + }, + performance: { + histogram: "DEVTOOLS_JSPROFILER_OPENED_COUNT", + timerHistogram: "DEVTOOLS_JSPROFILER_TIME_ACTIVE_SECONDS" + }, + memory: { + histogram: "DEVTOOLS_MEMORY_OPENED_COUNT", + timerHistogram: "DEVTOOLS_MEMORY_TIME_ACTIVE_SECONDS" + }, + netmonitor: { + histogram: "DEVTOOLS_NETMONITOR_OPENED_COUNT", + timerHistogram: "DEVTOOLS_NETMONITOR_TIME_ACTIVE_SECONDS" + }, + storage: { + histogram: "DEVTOOLS_STORAGE_OPENED_COUNT", + timerHistogram: "DEVTOOLS_STORAGE_TIME_ACTIVE_SECONDS" + }, + paintflashing: { + histogram: "DEVTOOLS_PAINTFLASHING_OPENED_COUNT", + timerHistogram: "DEVTOOLS_PAINTFLASHING_TIME_ACTIVE_SECONDS" + }, + scratchpad: { + histogram: "DEVTOOLS_SCRATCHPAD_OPENED_COUNT", + timerHistogram: "DEVTOOLS_SCRATCHPAD_TIME_ACTIVE_SECONDS" + }, + "scratchpad-window": { + histogram: "DEVTOOLS_SCRATCHPAD_WINDOW_OPENED_COUNT", + timerHistogram: "DEVTOOLS_SCRATCHPAD_WINDOW_TIME_ACTIVE_SECONDS" + }, + responsive: { + histogram: "DEVTOOLS_RESPONSIVE_OPENED_COUNT", + timerHistogram: "DEVTOOLS_RESPONSIVE_TIME_ACTIVE_SECONDS" + }, + eyedropper: { + histogram: "DEVTOOLS_EYEDROPPER_OPENED_COUNT", + }, + menueyedropper: { + histogram: "DEVTOOLS_MENU_EYEDROPPER_OPENED_COUNT", + }, + pickereyedropper: { + histogram: "DEVTOOLS_PICKER_EYEDROPPER_OPENED_COUNT", + }, + toolbareyedropper: { + histogram: "DEVTOOLS_TOOLBAR_EYEDROPPER_OPENED_COUNT", + }, + developertoolbar: { + histogram: "DEVTOOLS_DEVELOPERTOOLBAR_OPENED_COUNT", + timerHistogram: "DEVTOOLS_DEVELOPERTOOLBAR_TIME_ACTIVE_SECONDS" + }, + aboutdebugging: { + histogram: "DEVTOOLS_ABOUTDEBUGGING_OPENED_COUNT", + timerHistogram: "DEVTOOLS_ABOUTDEBUGGING_TIME_ACTIVE_SECONDS" + }, + webide: { + histogram: "DEVTOOLS_WEBIDE_OPENED_COUNT", + timerHistogram: "DEVTOOLS_WEBIDE_TIME_ACTIVE_SECONDS" + }, + webideProjectEditor: { + histogram: "DEVTOOLS_WEBIDE_PROJECT_EDITOR_OPENED_COUNT", + timerHistogram: "DEVTOOLS_WEBIDE_PROJECT_EDITOR_TIME_ACTIVE_SECONDS" + }, + webideProjectEditorSave: { + histogram: "DEVTOOLS_WEBIDE_PROJECT_EDITOR_SAVE_COUNT", + }, + webideNewProject: { + histogram: "DEVTOOLS_WEBIDE_NEW_PROJECT_COUNT", + }, + webideImportProject: { + histogram: "DEVTOOLS_WEBIDE_IMPORT_PROJECT_COUNT", + }, + custom: { + histogram: "DEVTOOLS_CUSTOM_OPENED_COUNT", + timerHistogram: "DEVTOOLS_CUSTOM_TIME_ACTIVE_SECONDS" + }, + reloadAddonInstalled: { + histogram: "DEVTOOLS_RELOAD_ADDON_INSTALLED_COUNT", + }, + reloadAddonReload: { + histogram: "DEVTOOLS_RELOAD_ADDON_RELOAD_COUNT", + }, + }, + + /** + * 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.timerHistogram) { + this.startTimer(charts.timerHistogram); + } + }, + + /** + * 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. + */ + actionOccurred(id) { + this.toolOpened(id); + }, + + 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. + * @param String key [optional] + * Optional key for a keyed histogram. + */ + stopTimer: function (histogramId, key) { + let startTime = this._timers.get(histogramId); + if (startTime) { + let time = (new Date() - startTime) / 1000; + if (!key) { + this.log(histogramId, time); + } else { + this.logKeyed(histogramId, 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: 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 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 + * Value to store. + */ + logKeyed: function (histogramId, key, value) { + if (histogramId) { + try { + let histogram = Services.telemetry.getKeyedHistogramById(histogramId); + 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"); + } + } + }, + + /** + * 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 = Services.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); + } + } +}; |