mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-26 22:32:46 +00:00
afae43d44a
Depends on D228159 Differential Revision: https://phabricator.services.mozilla.com/D228160
667 lines
22 KiB
JavaScript
667 lines
22 KiB
JavaScript
/* 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/. */
|
|
|
|
/**
|
|
* This is the telemetry module to report metrics for tools.
|
|
*
|
|
* Comprehensive documentation is in docs/frontend/telemetry.md
|
|
*/
|
|
|
|
"use strict";
|
|
|
|
const TelemetryStopwatch = require("TelemetryStopwatch");
|
|
const {
|
|
getNthPathExcluding,
|
|
} = require("resource://devtools/shared/platform/stack.js");
|
|
const { TelemetryEnvironment } = ChromeUtils.importESModule(
|
|
"resource://gre/modules/TelemetryEnvironment.sys.mjs"
|
|
);
|
|
const WeakMapMap = require("resource://devtools/client/shared/WeakMapMap.js");
|
|
|
|
// Object to be shared among all instances.
|
|
const PENDING_EVENT_PROPERTIES = new WeakMapMap();
|
|
const PENDING_EVENTS = new WeakMapMap();
|
|
|
|
/**
|
|
* Instantiate a new Telemetry helper class.
|
|
*
|
|
* @param {Object} options [optional]
|
|
* @param {Boolean} options.useSessionId [optional]
|
|
* If true, this instance will automatically generate a unique "sessionId"
|
|
* and use it to aggregate all records against this unique session.
|
|
* This helps aggregate all data coming from a single toolbox instance for ex.
|
|
*/
|
|
class Telemetry {
|
|
constructor({ useSessionId = false } = {}) {
|
|
// Note that native telemetry APIs expect a string
|
|
this.sessionId = String(
|
|
useSessionId ? parseInt(this.msSinceProcessStart(), 10) : -1
|
|
);
|
|
|
|
// Bind pretty much all functions so that callers do not need to.
|
|
this.msSystemNow = this.msSystemNow.bind(this);
|
|
this.getHistogramById = this.getHistogramById.bind(this);
|
|
this.getKeyedHistogramById = this.getKeyedHistogramById.bind(this);
|
|
this.recordEvent = this.recordEvent.bind(this);
|
|
this.preparePendingEvent = this.preparePendingEvent.bind(this);
|
|
this.addEventProperty = this.addEventProperty.bind(this);
|
|
this.addEventProperties = this.addEventProperties.bind(this);
|
|
this.toolOpened = this.toolOpened.bind(this);
|
|
this.toolClosed = this.toolClosed.bind(this);
|
|
}
|
|
|
|
get osNameAndVersion() {
|
|
const osInfo = TelemetryEnvironment.currentEnvironment.system.os;
|
|
|
|
if (!osInfo) {
|
|
return "Unknown OS";
|
|
}
|
|
|
|
let osVersion = `${osInfo.name} ${osInfo.version}`;
|
|
|
|
if (osInfo.windowsBuildNumber) {
|
|
osVersion += `.${osInfo.windowsBuildNumber}`;
|
|
}
|
|
|
|
return osVersion;
|
|
}
|
|
|
|
/**
|
|
* Time since the system wide epoch. This is not a monotonic timer but
|
|
* can be used across process boundaries.
|
|
*/
|
|
msSystemNow() {
|
|
return Services.telemetry.msSystemNow();
|
|
}
|
|
|
|
/**
|
|
* The number of milliseconds since process start using monotonic
|
|
* timestamps (unaffected by system clock changes).
|
|
*/
|
|
msSinceProcessStart() {
|
|
return Services.telemetry.msSinceProcessStart();
|
|
}
|
|
|
|
/**
|
|
* Starts a timer associated with a telemetry histogram. The timer can be
|
|
* directly associated with a histogram, or with a pair of a histogram and
|
|
* an object.
|
|
*
|
|
* @param {String} histogramId
|
|
* A string which must be a valid histogram name.
|
|
* @param {Object} obj
|
|
* The telemetry event or ping is associated with this object, meaning
|
|
* that multiple events or pings for the same histogram may be run
|
|
* concurrently, as long as they are associated with different objects.
|
|
* @param {Object} [options.inSeconds=false]
|
|
* Record elapsed time for this histogram in seconds instead of
|
|
* milliseconds. Defaults to false.
|
|
* @returns {Boolean}
|
|
* True if the timer was successfully started, false otherwise. If a
|
|
* timer already exists, it can't be started again.
|
|
*/
|
|
start(histogramId, obj, { inSeconds } = {}) {
|
|
if (TelemetryStopwatch.running(histogramId, obj)) {
|
|
return false;
|
|
}
|
|
|
|
return TelemetryStopwatch.start(histogramId, obj, { inSeconds });
|
|
}
|
|
|
|
/**
|
|
* Starts a timer associated with a keyed telemetry histogram. The timer can
|
|
* be directly associated with a histogram and its key. Similarly to
|
|
* TelemetryStopwatch.start the histogram and its key can be associated
|
|
* with an object. Each key may have multiple associated objects and each
|
|
* object can be associated with multiple keys.
|
|
*
|
|
* @param {String} histogramId
|
|
* A string which must be a valid histogram name.
|
|
* @param {String} key
|
|
* A string which must be a valid histgram key.
|
|
* @param {Object} obj
|
|
* The telemetry event or ping is associated with this object, meaning
|
|
* that multiple events or pings for the same histogram may be run
|
|
* concurrently, as long as they are associated with different objects.
|
|
* @param {Object} [options.inSeconds=false]
|
|
* Record elapsed time for this histogram in seconds instead of
|
|
* milliseconds. Defaults to false.
|
|
*
|
|
* @returns {Boolean}
|
|
* True if the timer was successfully started, false otherwise. If a
|
|
* timer already exists, it can't be started again, and the existing
|
|
* one will be cleared in order to avoid measurements errors.
|
|
*/
|
|
startKeyed(histogramId, key, obj, { inSeconds } = {}) {
|
|
return TelemetryStopwatch.startKeyed(histogramId, key, obj, { inSeconds });
|
|
}
|
|
|
|
/**
|
|
* Stops the timer associated with the given histogram (and object),
|
|
* calculates the time delta between start and finish, and adds the value
|
|
* to the histogram.
|
|
*
|
|
* @param {String} histogramId
|
|
* A string which must be a valid histogram name.
|
|
* @param {Object} obj
|
|
* The telemetry event or ping is associated with this object, meaning
|
|
* that multiple events or pings for the same histogram may be run
|
|
* concurrently, as long as they are associated with different objects.
|
|
* @param {Boolean} canceledOkay
|
|
* Optional parameter which will suppress any warnings that normally
|
|
* fire when a stopwatch is finished after being canceled.
|
|
* Defaults to false.
|
|
*
|
|
* @returns {Boolean}
|
|
* True if the timer was succesfully stopped and the data was added
|
|
* to the histogram, False otherwise.
|
|
*/
|
|
finish(histogramId, obj, canceledOkay) {
|
|
return TelemetryStopwatch.finish(histogramId, obj, canceledOkay);
|
|
}
|
|
|
|
/**
|
|
* Stops the timer associated with the given keyed histogram (and object),
|
|
* calculates the time delta between start and finish, and adds the value
|
|
* to the keyed histogram.
|
|
*
|
|
* @param {String} histogramId
|
|
* A string which must be a valid histogram name.
|
|
* @param {String} key
|
|
* A string which must be a valid histogram key.
|
|
* @param {Object} obj
|
|
* The telemetry event or ping is associated with this object, meaning
|
|
* that multiple events or pings for the same histogram may be run
|
|
* concurrently, as long as they are associated with different objects.
|
|
* @param {Boolean} canceledOkay
|
|
* Optional parameter which will suppress any warnings that normally
|
|
* fire when a stopwatch is finished after being canceled.
|
|
* Defaults to false.
|
|
*
|
|
* @returns {Boolean}
|
|
* True if the timer was succesfully stopped and the data was added
|
|
* to the histogram, False otherwise.
|
|
*/
|
|
finishKeyed(histogramId, key, obj, canceledOkay) {
|
|
return TelemetryStopwatch.finishKeyed(histogramId, key, obj, canceledOkay);
|
|
}
|
|
|
|
/**
|
|
* Log a value to a histogram.
|
|
*
|
|
* @param {String} histogramId
|
|
* Histogram in which the data is to be stored.
|
|
*/
|
|
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: ${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: ${getCaller()}`
|
|
);
|
|
}
|
|
}
|
|
return (
|
|
histogram || {
|
|
add: () => {},
|
|
}
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Telemetry events often need to make use of a number of properties from
|
|
* completely different codepaths. To make this possible we create a
|
|
* "pending event" along with an array of property names that we need to wait
|
|
* for before sending the event.
|
|
*
|
|
* As each property is received via addEventProperty() we check if all
|
|
* properties have been received. Once they have all been received we send the
|
|
* telemetry event.
|
|
*
|
|
* @param {Object} obj
|
|
* The telemetry event or ping is associated with this object, meaning
|
|
* that multiple events or pings for the same histogram may be run
|
|
* concurrently, as long as they are associated with different objects.
|
|
* @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"
|
|
* @param {Array} expected
|
|
* An array of the properties needed before sending the telemetry
|
|
* event e.g.
|
|
* [
|
|
* "host",
|
|
* "width"
|
|
* ]
|
|
*/
|
|
preparePendingEvent(obj, method, object, value, expected = []) {
|
|
const sig = `${method},${object},${value}`;
|
|
|
|
if (expected.length === 0) {
|
|
throw new Error(
|
|
`preparePendingEvent() was called without any expected ` +
|
|
`properties.\n` +
|
|
`CALLER: ${getCaller()}`
|
|
);
|
|
}
|
|
|
|
const data = {
|
|
extra: {},
|
|
expected: new Set(expected),
|
|
};
|
|
|
|
PENDING_EVENTS.set(obj, sig, data);
|
|
|
|
const props = PENDING_EVENT_PROPERTIES.get(obj, sig);
|
|
if (props) {
|
|
for (const [name, val] of Object.entries(props)) {
|
|
this.addEventProperty(obj, method, object, value, name, val);
|
|
}
|
|
PENDING_EVENT_PROPERTIES.delete(obj, sig);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Adds an expected property for either a current or future pending event.
|
|
* This means that if preparePendingEvent() is called before or after sending
|
|
* the event properties they will automatically added to the event.
|
|
*
|
|
* @param {Object} obj
|
|
* The telemetry event or ping is associated with this object, meaning
|
|
* that multiple events or pings for the same histogram may be run
|
|
* concurrently, as long as they are associated with different objects.
|
|
* @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"
|
|
* @param {String} pendingPropName
|
|
* The pending property name
|
|
* @param {String} pendingPropValue
|
|
* The pending property value
|
|
*/
|
|
addEventProperty(
|
|
obj,
|
|
method,
|
|
object,
|
|
value,
|
|
pendingPropName,
|
|
pendingPropValue
|
|
) {
|
|
const sig = `${method},${object},${value}`;
|
|
const events = PENDING_EVENTS.get(obj, sig);
|
|
|
|
// If the pending event has not been created add the property to the pending
|
|
// list.
|
|
if (!events) {
|
|
const props = PENDING_EVENT_PROPERTIES.get(obj, sig);
|
|
|
|
if (props) {
|
|
props[pendingPropName] = pendingPropValue;
|
|
} else {
|
|
PENDING_EVENT_PROPERTIES.set(obj, sig, {
|
|
[pendingPropName]: pendingPropValue,
|
|
});
|
|
}
|
|
return;
|
|
}
|
|
|
|
const { expected, extra } = events;
|
|
|
|
if (expected.has(pendingPropName)) {
|
|
extra[pendingPropName] = pendingPropValue;
|
|
|
|
if (expected.size === Object.keys(extra).length) {
|
|
this._sendPendingEvent(obj, method, object, value);
|
|
}
|
|
} else {
|
|
// The property was not expected, warn and bail.
|
|
throw new Error(
|
|
`An attempt was made to add the unexpected property ` +
|
|
`"${pendingPropName}" to a telemetry event with the ` +
|
|
`signature "${sig}"\n` +
|
|
`CALLER: ${getCaller()}`
|
|
);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Adds expected properties for either a current or future pending event.
|
|
* This means that if preparePendingEvent() is called before or after sending
|
|
* the event properties they will automatically added to the event.
|
|
*
|
|
* @param {Object} obj
|
|
* The telemetry event or ping is associated with this object, meaning
|
|
* that multiple events or pings for the same histogram may be run
|
|
* concurrently, as long as they are associated with different objects.
|
|
* @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"
|
|
* @param {String} pendingObject
|
|
* An object containing key, value pairs that should be added to the
|
|
* event as properties.
|
|
*/
|
|
addEventProperties(obj, method, object, value, pendingObject) {
|
|
for (const [key, val] of Object.entries(pendingObject)) {
|
|
this.addEventProperty(obj, 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 {Object} obj
|
|
* The telemetry event or ping is associated with this object, meaning
|
|
* that multiple events or pings for the same histogram may be run
|
|
* concurrently, as long as they are associated with different objects.
|
|
* @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(obj, method, object, value) {
|
|
const sig = `${method},${object},${value}`;
|
|
const { extra } = PENDING_EVENTS.get(obj, sig);
|
|
|
|
PENDING_EVENTS.delete(obj, sig);
|
|
PENDING_EVENT_PROPERTIES.delete(obj, sig);
|
|
this.recordEvent(method, object, value, extra);
|
|
}
|
|
|
|
/**
|
|
* Send a telemetry event.
|
|
*
|
|
* @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]
|
|
* Optional telemetry event value (a user defined value, providing
|
|
* context for the event) e.g. "console"
|
|
* @param {Object} [extra]
|
|
* Optional telemetry event extra object containing the properties that
|
|
* will be sent with the event e.g.
|
|
* {
|
|
* host: "bottom",
|
|
* width: "1024"
|
|
* }
|
|
*/
|
|
recordEvent(method, object, value = null, extra = null) {
|
|
// Only string values are allowed so cast all values to strings.
|
|
if (extra) {
|
|
for (let [name, val] of Object.entries(extra)) {
|
|
val = val + "";
|
|
|
|
if (val.length > 80) {
|
|
const sig = `${method},${object},${value}`;
|
|
|
|
dump(
|
|
`Warning: The property "${name}" was added to a telemetry ` +
|
|
`event with the signature ${sig} but it's value "${val}" is ` +
|
|
`longer than the maximum allowed length of 80 characters.\n` +
|
|
`The property value has been trimmed to 80 characters before ` +
|
|
`sending.\nCALLER: ${getCaller()}`
|
|
);
|
|
|
|
val = val.substring(0, 80);
|
|
}
|
|
|
|
extra[name] = val;
|
|
}
|
|
}
|
|
// Automatically flag the record with the session ID
|
|
// if the current Telemetry instance relates to a toolbox
|
|
// so that data can be aggregated per toolbox instance.
|
|
// Note that we also aggregate data per about:debugging instance.
|
|
if (!extra) {
|
|
extra = {};
|
|
}
|
|
extra.session_id = this.sessionId;
|
|
if (value !== null) {
|
|
extra.value = value;
|
|
}
|
|
|
|
// Using the Glean API directly insteade of doing string manipulations
|
|
// would be better. See bug 1921793.
|
|
const eventName = `${method}_${object}`.replace(/(_[a-z])/g, c =>
|
|
c[1].toUpperCase()
|
|
);
|
|
Glean.devtoolsMain[eventName]?.record(extra);
|
|
}
|
|
|
|
/**
|
|
* Sends telemetry pings to indicate that a tool has been opened.
|
|
*
|
|
* @param {String} id
|
|
* The ID of the tool opened.
|
|
* @param {Object} obj
|
|
* The telemetry event or ping is associated with this object, meaning
|
|
* that multiple events or pings for the same histogram may be run
|
|
* concurrently, as long as they are associated with different objects.
|
|
*
|
|
* NOTE: This method is designed for tools that send multiple probes on open,
|
|
* one of those probes being a counter and the other a timer. If you
|
|
* only have one probe you should be using another method.
|
|
*/
|
|
toolOpened(id, obj) {
|
|
const charts = getChartsFromToolId(id);
|
|
|
|
if (!charts) {
|
|
return;
|
|
}
|
|
|
|
if (charts.useTimedEvent) {
|
|
this.preparePendingEvent(obj, "tool_timer", id, null, [
|
|
"os",
|
|
"time_open",
|
|
]);
|
|
this.addEventProperty(
|
|
obj,
|
|
"tool_timer",
|
|
id,
|
|
null,
|
|
"time_open",
|
|
this.msSystemNow()
|
|
);
|
|
}
|
|
if (charts.timerHist) {
|
|
this.start(charts.timerHist, obj, { inSeconds: true });
|
|
}
|
|
if (charts.countHist) {
|
|
this.getHistogramById(charts.countHist).add(true);
|
|
}
|
|
if (charts.gleanCounter) {
|
|
charts.gleanCounter.add(1);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Sends telemetry pings to indicate that a tool has been closed.
|
|
*
|
|
* @param {String} id
|
|
* The ID of the tool opened.
|
|
* @param {Object} obj
|
|
* The telemetry event or ping is associated with this object, meaning
|
|
* that multiple events or pings for the same histogram may be run
|
|
* concurrently, as long as they are associated with different objects.
|
|
*
|
|
* NOTE: This method is designed for tools that send multiple probes on open,
|
|
* one of those probes being a counter and the other a timer. If you
|
|
* only have one probe you should be using another method.
|
|
*/
|
|
toolClosed(id, obj) {
|
|
const charts = getChartsFromToolId(id);
|
|
|
|
if (!charts) {
|
|
return;
|
|
}
|
|
|
|
if (charts.useTimedEvent) {
|
|
const sig = `tool_timer,${id},null`;
|
|
const event = PENDING_EVENTS.get(obj, sig);
|
|
const time = this.msSystemNow() - event.extra.time_open;
|
|
|
|
this.addEventProperties(obj, "tool_timer", id, null, {
|
|
time_open: time,
|
|
os: this.osNameAndVersion,
|
|
});
|
|
}
|
|
|
|
if (charts.timerHist) {
|
|
this.finish(charts.timerHist, obj, false);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Returns the telemetry charts for a specific tool.
|
|
*
|
|
* @param {String} id
|
|
* The ID of the tool that has been opened.
|
|
*
|
|
*/
|
|
// eslint-disable-next-line complexity
|
|
function getChartsFromToolId(id) {
|
|
if (!id) {
|
|
return null;
|
|
}
|
|
|
|
let useTimedEvent = null;
|
|
let timerHist = null;
|
|
let countHist = null;
|
|
let gleanCounter = null;
|
|
|
|
id = id.toUpperCase();
|
|
|
|
if (id === "PERFORMANCE") {
|
|
id = "JSPROFILER";
|
|
}
|
|
|
|
switch (id) {
|
|
case "ABOUTDEBUGGING":
|
|
case "BROWSERCONSOLE":
|
|
case "DOM":
|
|
case "INSPECTOR":
|
|
case "JSBROWSERDEBUGGER":
|
|
case "JSDEBUGGER":
|
|
case "JSPROFILER":
|
|
case "MEMORY":
|
|
case "NETMONITOR":
|
|
case "OPTIONS":
|
|
case "RESPONSIVE":
|
|
case "STORAGE":
|
|
case "STYLEEDITOR":
|
|
case "TOOLBOX":
|
|
case "WEBCONSOLE":
|
|
timerHist = `DEVTOOLS_${id}_TIME_ACTIVE_SECONDS`;
|
|
countHist = `DEVTOOLS_${id}_OPENED_COUNT`;
|
|
break;
|
|
case "ACCESSIBILITY":
|
|
timerHist = `DEVTOOLS_${id}_TIME_ACTIVE_SECONDS`;
|
|
gleanCounter = Glean.devtoolsAccessibility.openedCount;
|
|
break;
|
|
case "ACCESSIBILITY_PICKER":
|
|
timerHist = `DEVTOOLS_${id}_TIME_ACTIVE_SECONDS`;
|
|
gleanCounter = Glean.devtoolsAccessibility.pickerUsedCount;
|
|
break;
|
|
case "CHANGESVIEW":
|
|
useTimedEvent = true;
|
|
timerHist = `DEVTOOLS_${id}_TIME_ACTIVE_SECONDS`;
|
|
gleanCounter = Glean.devtoolsChangesview.openedCount;
|
|
break;
|
|
case "ANIMATIONINSPECTOR":
|
|
case "COMPATIBILITYVIEW":
|
|
case "COMPUTEDVIEW":
|
|
case "FONTINSPECTOR":
|
|
case "LAYOUTVIEW":
|
|
case "RULEVIEW":
|
|
useTimedEvent = true;
|
|
timerHist = `DEVTOOLS_${id}_TIME_ACTIVE_SECONDS`;
|
|
countHist = `DEVTOOLS_${id}_OPENED_COUNT`;
|
|
break;
|
|
case "FLEXBOX_HIGHLIGHTER":
|
|
case "GRID_HIGHLIGHTER":
|
|
timerHist = `DEVTOOLS_${id}_TIME_ACTIVE_SECONDS`;
|
|
break;
|
|
default:
|
|
timerHist = `DEVTOOLS_CUSTOM_TIME_ACTIVE_SECONDS`;
|
|
countHist = `DEVTOOLS_CUSTOM_OPENED_COUNT`;
|
|
}
|
|
|
|
return {
|
|
useTimedEvent,
|
|
timerHist,
|
|
countHist,
|
|
gleanCounter,
|
|
};
|
|
}
|
|
|
|
/**
|
|
* 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.
|
|
*/
|
|
function getCaller() {
|
|
return getNthPathExcluding(0, "/telemetry.js");
|
|
}
|
|
|
|
module.exports = Telemetry;
|