1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/browser/devtools/shared/telemetry.js Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,262 @@ 1.4 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.5 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.6 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.7 + 1.8 +/** 1.9 + * Telemetry. 1.10 + * 1.11 + * To add metrics for a tool: 1.12 + * 1.13 + * 1. Create boolean, flag and exponential entries in 1.14 + * toolkit/components/telemetry/Histograms.json. Each type is optional but it 1.15 + * is best if all three can be included. 1.16 + * 1.17 + * 2. Add your chart entries to browser/devtools/shared/telemetry.js 1.18 + * (Telemetry.prototype._histograms): 1.19 + * mytoolname: { 1.20 + * histogram: "DEVTOOLS_MYTOOLNAME_OPENED_BOOLEAN", 1.21 + * userHistogram: "DEVTOOLS_MYTOOLNAME_OPENED_PER_USER_FLAG", 1.22 + * timerHistogram: "DEVTOOLS_MYTOOLNAME_TIME_ACTIVE_SECONDS" 1.23 + * }, 1.24 + * 1.25 + * 3. Include this module at the top of your tool. Use: 1.26 + * let Telemetry = require("devtools/shared/telemetry") 1.27 + * 1.28 + * 4. Create a telemetry instance in your tool's constructor: 1.29 + * this._telemetry = new Telemetry(); 1.30 + * 1.31 + * 5. When your tool is opened call: 1.32 + * this._telemetry.toolOpened("mytoolname"); 1.33 + * 1.34 + * 6. When your tool is closed call: 1.35 + * this._telemetry.toolClosed("mytoolname"); 1.36 + * 1.37 + * Note: 1.38 + * You can view telemetry stats for your local Firefox instance via 1.39 + * about:telemetry. 1.40 + * 1.41 + * You can view telemetry stats for large groups of Firefox users at 1.42 + * metrics.mozilla.com. 1.43 + */ 1.44 + 1.45 +const TOOLS_OPENED_PREF = "devtools.telemetry.tools.opened.version"; 1.46 + 1.47 +this.Telemetry = function() { 1.48 + // Bind pretty much all functions so that callers do not need to. 1.49 + this.toolOpened = this.toolOpened.bind(this); 1.50 + this.toolClosed = this.toolClosed.bind(this); 1.51 + this.log = this.log.bind(this); 1.52 + this.logOncePerBrowserVersion = this.logOncePerBrowserVersion.bind(this); 1.53 + this.destroy = this.destroy.bind(this); 1.54 + 1.55 + this._timers = new Map(); 1.56 +}; 1.57 + 1.58 +module.exports = Telemetry; 1.59 + 1.60 +let {Cc, Ci, Cu} = require("chrome"); 1.61 +let {Services} = Cu.import("resource://gre/modules/Services.jsm", {}); 1.62 +let {XPCOMUtils} = Cu.import("resource://gre/modules/XPCOMUtils.jsm", {}); 1.63 + 1.64 +Telemetry.prototype = { 1.65 + _histograms: { 1.66 + toolbox: { 1.67 + timerHistogram: "DEVTOOLS_TOOLBOX_TIME_ACTIVE_SECONDS" 1.68 + }, 1.69 + options: { 1.70 + histogram: "DEVTOOLS_OPTIONS_OPENED_BOOLEAN", 1.71 + userHistogram: "DEVTOOLS_OPTIONS_OPENED_PER_USER_FLAG", 1.72 + timerHistogram: "DEVTOOLS_OPTIONS_TIME_ACTIVE_SECONDS" 1.73 + }, 1.74 + webconsole: { 1.75 + histogram: "DEVTOOLS_WEBCONSOLE_OPENED_BOOLEAN", 1.76 + userHistogram: "DEVTOOLS_WEBCONSOLE_OPENED_PER_USER_FLAG", 1.77 + timerHistogram: "DEVTOOLS_WEBCONSOLE_TIME_ACTIVE_SECONDS" 1.78 + }, 1.79 + browserconsole: { 1.80 + histogram: "DEVTOOLS_BROWSERCONSOLE_OPENED_BOOLEAN", 1.81 + userHistogram: "DEVTOOLS_BROWSERCONSOLE_OPENED_PER_USER_FLAG", 1.82 + timerHistogram: "DEVTOOLS_BROWSERCONSOLE_TIME_ACTIVE_SECONDS" 1.83 + }, 1.84 + inspector: { 1.85 + histogram: "DEVTOOLS_INSPECTOR_OPENED_BOOLEAN", 1.86 + userHistogram: "DEVTOOLS_INSPECTOR_OPENED_PER_USER_FLAG", 1.87 + timerHistogram: "DEVTOOLS_INSPECTOR_TIME_ACTIVE_SECONDS" 1.88 + }, 1.89 + ruleview: { 1.90 + histogram: "DEVTOOLS_RULEVIEW_OPENED_BOOLEAN", 1.91 + userHistogram: "DEVTOOLS_RULEVIEW_OPENED_PER_USER_FLAG", 1.92 + timerHistogram: "DEVTOOLS_RULEVIEW_TIME_ACTIVE_SECONDS" 1.93 + }, 1.94 + computedview: { 1.95 + histogram: "DEVTOOLS_COMPUTEDVIEW_OPENED_BOOLEAN", 1.96 + userHistogram: "DEVTOOLS_COMPUTEDVIEW_OPENED_PER_USER_FLAG", 1.97 + timerHistogram: "DEVTOOLS_COMPUTEDVIEW_TIME_ACTIVE_SECONDS" 1.98 + }, 1.99 + layoutview: { 1.100 + histogram: "DEVTOOLS_LAYOUTVIEW_OPENED_BOOLEAN", 1.101 + userHistogram: "DEVTOOLS_LAYOUTVIEW_OPENED_PER_USER_FLAG", 1.102 + timerHistogram: "DEVTOOLS_LAYOUTVIEW_TIME_ACTIVE_SECONDS" 1.103 + }, 1.104 + fontinspector: { 1.105 + histogram: "DEVTOOLS_FONTINSPECTOR_OPENED_BOOLEAN", 1.106 + userHistogram: "DEVTOOLS_FONTINSPECTOR_OPENED_PER_USER_FLAG", 1.107 + timerHistogram: "DEVTOOLS_FONTINSPECTOR_TIME_ACTIVE_SECONDS" 1.108 + }, 1.109 + jsdebugger: { 1.110 + histogram: "DEVTOOLS_JSDEBUGGER_OPENED_BOOLEAN", 1.111 + userHistogram: "DEVTOOLS_JSDEBUGGER_OPENED_PER_USER_FLAG", 1.112 + timerHistogram: "DEVTOOLS_JSDEBUGGER_TIME_ACTIVE_SECONDS" 1.113 + }, 1.114 + jsbrowserdebugger: { 1.115 + histogram: "DEVTOOLS_JSBROWSERDEBUGGER_OPENED_BOOLEAN", 1.116 + userHistogram: "DEVTOOLS_JSBROWSERDEBUGGER_OPENED_PER_USER_FLAG", 1.117 + timerHistogram: "DEVTOOLS_JSBROWSERDEBUGGER_TIME_ACTIVE_SECONDS" 1.118 + }, 1.119 + styleeditor: { 1.120 + histogram: "DEVTOOLS_STYLEEDITOR_OPENED_BOOLEAN", 1.121 + userHistogram: "DEVTOOLS_STYLEEDITOR_OPENED_PER_USER_FLAG", 1.122 + timerHistogram: "DEVTOOLS_STYLEEDITOR_TIME_ACTIVE_SECONDS" 1.123 + }, 1.124 + shadereditor: { 1.125 + histogram: "DEVTOOLS_SHADEREDITOR_OPENED_BOOLEAN", 1.126 + userHistogram: "DEVTOOLS_SHADEREDITOR_OPENED_PER_USER_FLAG", 1.127 + timerHistogram: "DEVTOOLS_SHADEREDITOR_TIME_ACTIVE_SECONDS" 1.128 + }, 1.129 + jsprofiler: { 1.130 + histogram: "DEVTOOLS_JSPROFILER_OPENED_BOOLEAN", 1.131 + userHistogram: "DEVTOOLS_JSPROFILER_OPENED_PER_USER_FLAG", 1.132 + timerHistogram: "DEVTOOLS_JSPROFILER_TIME_ACTIVE_SECONDS" 1.133 + }, 1.134 + netmonitor: { 1.135 + histogram: "DEVTOOLS_NETMONITOR_OPENED_BOOLEAN", 1.136 + userHistogram: "DEVTOOLS_NETMONITOR_OPENED_PER_USER_FLAG", 1.137 + timerHistogram: "DEVTOOLS_NETMONITOR_TIME_ACTIVE_SECONDS" 1.138 + }, 1.139 + tilt: { 1.140 + histogram: "DEVTOOLS_TILT_OPENED_BOOLEAN", 1.141 + userHistogram: "DEVTOOLS_TILT_OPENED_PER_USER_FLAG", 1.142 + timerHistogram: "DEVTOOLS_TILT_TIME_ACTIVE_SECONDS" 1.143 + }, 1.144 + paintflashing: { 1.145 + histogram: "DEVTOOLS_PAINTFLASHING_OPENED_BOOLEAN", 1.146 + userHistogram: "DEVTOOLS_PAINTFLASHING_OPENED_PER_USER_FLAG", 1.147 + timerHistogram: "DEVTOOLS_PAINTFLASHING_TIME_ACTIVE_SECONDS" 1.148 + }, 1.149 + scratchpad: { 1.150 + histogram: "DEVTOOLS_SCRATCHPAD_OPENED_BOOLEAN", 1.151 + userHistogram: "DEVTOOLS_SCRATCHPAD_OPENED_PER_USER_FLAG", 1.152 + timerHistogram: "DEVTOOLS_SCRATCHPAD_TIME_ACTIVE_SECONDS" 1.153 + }, 1.154 + responsive: { 1.155 + histogram: "DEVTOOLS_RESPONSIVE_OPENED_BOOLEAN", 1.156 + userHistogram: "DEVTOOLS_RESPONSIVE_OPENED_PER_USER_FLAG", 1.157 + timerHistogram: "DEVTOOLS_RESPONSIVE_TIME_ACTIVE_SECONDS" 1.158 + }, 1.159 + developertoolbar: { 1.160 + histogram: "DEVTOOLS_DEVELOPERTOOLBAR_OPENED_BOOLEAN", 1.161 + userHistogram: "DEVTOOLS_DEVELOPERTOOLBAR_OPENED_PER_USER_FLAG", 1.162 + timerHistogram: "DEVTOOLS_DEVELOPERTOOLBAR_TIME_ACTIVE_SECONDS" 1.163 + }, 1.164 + custom: { 1.165 + histogram: "DEVTOOLS_CUSTOM_OPENED_BOOLEAN", 1.166 + userHistogram: "DEVTOOLS_CUSTOM_OPENED_PER_USER_FLAG", 1.167 + timerHistogram: "DEVTOOLS_CUSTOM_TIME_ACTIVE_SECONDS" 1.168 + } 1.169 + }, 1.170 + 1.171 + /** 1.172 + * Add an entry to a histogram. 1.173 + * 1.174 + * @param {String} id 1.175 + * Used to look up the relevant histogram ID and log true to that 1.176 + * histogram. 1.177 + */ 1.178 + toolOpened: function(id) { 1.179 + let charts = this._histograms[id] || this._histograms.custom; 1.180 + 1.181 + if (charts.histogram) { 1.182 + this.log(charts.histogram, true); 1.183 + } 1.184 + if (charts.userHistogram) { 1.185 + this.logOncePerBrowserVersion(charts.userHistogram, true); 1.186 + } 1.187 + if (charts.timerHistogram) { 1.188 + this._timers.set(charts.timerHistogram, new Date()); 1.189 + } 1.190 + }, 1.191 + 1.192 + toolClosed: function(id) { 1.193 + let charts = this._histograms[id]; 1.194 + 1.195 + if (!charts || !charts.timerHistogram) { 1.196 + return; 1.197 + } 1.198 + 1.199 + let startTime = this._timers.get(charts.timerHistogram); 1.200 + 1.201 + if (startTime) { 1.202 + let time = (new Date() - startTime) / 1000; 1.203 + this.log(charts.timerHistogram, time); 1.204 + this._timers.delete(charts.timerHistogram); 1.205 + } 1.206 + }, 1.207 + 1.208 + /** 1.209 + * Log a value to a histogram. 1.210 + * 1.211 + * @param {String} histogramId 1.212 + * Histogram in which the data is to be stored. 1.213 + * @param value 1.214 + * Value to store. 1.215 + */ 1.216 + log: function(histogramId, value) { 1.217 + if (histogramId) { 1.218 + let histogram; 1.219 + 1.220 + try { 1.221 + let histogram = Services.telemetry.getHistogramById(histogramId); 1.222 + histogram.add(value); 1.223 + } catch(e) { 1.224 + dump("Warning: An attempt was made to write to the " + histogramId + 1.225 + " histogram, which is not defined in Histograms.json\n"); 1.226 + } 1.227 + } 1.228 + }, 1.229 + 1.230 + /** 1.231 + * Log info about usage once per browser version. This allows us to discover 1.232 + * how many individual users are using our tools for each browser version. 1.233 + * 1.234 + * @param {String} perUserHistogram 1.235 + * Histogram in which the data is to be stored. 1.236 + */ 1.237 + logOncePerBrowserVersion: function(perUserHistogram, value) { 1.238 + let currentVersion = appInfo.version; 1.239 + let latest = Services.prefs.getCharPref(TOOLS_OPENED_PREF); 1.240 + let latestObj = JSON.parse(latest); 1.241 + 1.242 + let lastVersionHistogramUpdated = latestObj[perUserHistogram]; 1.243 + 1.244 + if (typeof lastVersionHistogramUpdated == "undefined" || 1.245 + lastVersionHistogramUpdated !== currentVersion) { 1.246 + latestObj[perUserHistogram] = currentVersion; 1.247 + latest = JSON.stringify(latestObj); 1.248 + Services.prefs.setCharPref(TOOLS_OPENED_PREF, latest); 1.249 + this.log(perUserHistogram, value); 1.250 + } 1.251 + }, 1.252 + 1.253 + destroy: function() { 1.254 + for (let [histogram, time] of this._timers) { 1.255 + time = (new Date() - time) / 1000; 1.256 + 1.257 + this.log(histogram, time); 1.258 + this._timers.delete(histogram); 1.259 + } 1.260 + } 1.261 +}; 1.262 + 1.263 +XPCOMUtils.defineLazyGetter(this, "appInfo", function() { 1.264 + return Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULAppInfo); 1.265 +});