toolkit/components/telemetry/UITelemetry.jsm

Fri, 16 Jan 2015 18:13:44 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Fri, 16 Jan 2015 18:13:44 +0100
branch
TOR_BUG_9701
changeset 14
925c144e1f1f
permissions
-rw-r--r--

Integrate suggestion from review to improve consistency with existing code.

     1 /* This Source Code Form is subject to the terms of the Mozilla Public
     2  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
     3  * You can obtain one at http://mozilla.org/MPL/2.0/. */
     5 "use strict";
     7 const Cu = Components.utils;
     9 const PREF_BRANCH = "toolkit.telemetry.";
    10 const PREF_ENABLED = PREF_BRANCH + "enabled";
    12 this.EXPORTED_SYMBOLS = [
    13   "UITelemetry",
    14 ];
    16 Cu.import("resource://gre/modules/Services.jsm", this);
    18 /**
    19  * UITelemetry is a helper JSM used to record UI specific telemetry events.
    20  *
    21  * It implements nsIUITelemetryObserver, defined in nsIAndroidBridge.idl.
    22  */
    23 this.UITelemetry = {
    24   _enabled: undefined,
    25   _activeSessions: {},
    26   _measurements: [],
    28   // Lazily decide whether telemetry is enabled.
    29   get enabled() {
    30     if (this._enabled !== undefined) {
    31       return this._enabled;
    32     }
    34     // Set an observer to watch for changes at runtime.
    35     Services.prefs.addObserver(PREF_ENABLED, this, false);
    36     Services.obs.addObserver(this, "profile-before-change", false);
    38     // Pick up the current value.
    39     try {
    40       this._enabled = Services.prefs.getBoolPref(PREF_ENABLED);
    41     } catch (e) {
    42       this._enabled = false;
    43     }
    45     return this._enabled;
    46   },
    48   observe: function(aSubject, aTopic, aData) {
    49     if (aTopic == "profile-before-change") {
    50       Services.obs.removeObserver(this, "profile-before-change");
    51       Services.prefs.removeObserver(PREF_ENABLED, this);
    52       this._enabled = undefined;
    53       return;
    54     }
    56     if (aTopic == "nsPref:changed") {
    57       switch (aData) {
    58         case PREF_ENABLED:
    59           let on = Services.prefs.getBoolPref(PREF_ENABLED);
    60           this._enabled = on;
    62           // Wipe ourselves if we were just disabled.
    63           if (!on) {
    64             this._activeSessions = {};
    65             this._measurements = [];
    66           }
    67           break;
    68       }
    69     }
    70   },
    72   /**
    73    * This exists exclusively for testing -- our events are not intended to
    74    * be retrieved via an XPCOM interface.
    75    */
    76   get wrappedJSObject() {
    77     return this;
    78   },
    80   /**
    81    * Holds the functions that provide UITelemetry's simple
    82    * measurements. Those functions are mapped to unique names,
    83    * and should be registered with addSimpleMeasureFunction.
    84    */
    85   _simpleMeasureFunctions: {},
    87   /**
    88    * Adds a single event described by a timestamp, an action, and the calling
    89    * method.
    90    *
    91    * Optionally provide a string 'extras', which will be recorded as part of
    92    * the event.
    93    *
    94    * All extant sessions will be recorded by name for each event.
    95    */
    96   addEvent: function(aAction, aMethod, aTimestamp, aExtras) {
    97     if (!this.enabled) {
    98       return;
    99     }
   101     let sessions = Object.keys(this._activeSessions);
   102     let aEvent = {
   103       type: "event",
   104       action: aAction,
   105       method: aMethod,
   106       sessions: sessions,
   107       timestamp: aTimestamp,
   108     };
   110     if (aExtras) {
   111       aEvent.extras = aExtras;
   112     }
   114     this._recordEvent(aEvent);
   115   },
   117   /**
   118    * Begins tracking a session by storing a timestamp for session start.
   119    */
   120   startSession: function(aName, aTimestamp) {
   121     if (!this.enabled) {
   122       return;
   123     }
   125     if (this._activeSessions[aName]) {
   126       // Do not overwrite a previous event start if it already exists.
   127       return;
   128     }
   129     this._activeSessions[aName] = aTimestamp;
   130   },
   132   /**
   133    * Tracks the end of a session with a timestamp.
   134    */
   135   stopSession: function(aName, aReason, aTimestamp) {
   136     if (!this.enabled) {
   137       return;
   138     }
   140     let sessionStart = this._activeSessions[aName];
   141     delete this._activeSessions[aName];
   143     if (!sessionStart) {
   144       return;
   145     }
   147     let aEvent = {
   148       type: "session",
   149       name: aName,
   150       reason: aReason,
   151       start: sessionStart,
   152       end: aTimestamp,
   153     };
   155     this._recordEvent(aEvent);
   156   },
   158   _recordEvent: function(aEvent) {
   159     this._measurements.push(aEvent);
   160   },
   162   /**
   163    * Called by TelemetryPing to populate the simple measurement
   164    * blob. This function will iterate over all functions added
   165    * via addSimpleMeasureFunction and return an object with the
   166    * results of those functions.
   167    */
   168   getSimpleMeasures: function() {
   169     if (!this.enabled) {
   170       return {};
   171     }
   173     let result = {};
   174     for (let name in this._simpleMeasureFunctions) {
   175       result[name] = this._simpleMeasureFunctions[name]();
   176     }
   177     return result;
   178   },
   180   /**
   181    * Allows the caller to register functions that will get called
   182    * for simple measures during a Telemetry ping. aName is a unique
   183    * identifier used as they key for the simple measurement in the
   184    * object that getSimpleMeasures returns.
   185    *
   186    * This function throws an exception if aName already has a function
   187    * registered for it.
   188    */
   189   addSimpleMeasureFunction: function(aName, aFunction) {
   190     if (!this.enabled) {
   191       return;
   192     }
   194     if (aName in this._simpleMeasureFunctions) {
   195       throw new Error("A simple measurement function is already registered for " + aName);
   196     }
   198     if (!aFunction || typeof aFunction !== 'function') {
   199       throw new Error("addSimpleMeasureFunction called with non-function argument.");
   200     }
   202     this._simpleMeasureFunctions[aName] = aFunction;
   203   },
   205   removeSimpleMeasureFunction: function(aName) {
   206     delete this._simpleMeasureFunctions[aName];
   207   },
   209   getUIMeasurements: function() {
   210     if (!this.enabled) {
   211       return [];
   212     }
   214     return this._measurements.slice();
   215   }
   216 };

mercurial