遥测

我们使用遥测来获取 DevTools 中不同功能和面板使用情况的指标。这将帮助我们在优先考虑工作时做出更明智、更合理的决策。

向工具添加指标

向工具添加指标的过程大致包括以下步骤:

  1. 向 Firefox 添加探针

  2. 在 DevTools 代码中使用 Histograms.json 探针

  3. 在 DevTools 代码中使用 Scalars.yaml 探针

  4. 在 DevTools 代码中使用 Events.yaml 探针,以便在 Amplitude 中进行分析。

  5. 获得数据团队的批准

1. 向 Firefox 添加探针

第一步涉及在包含 Firefox 可能报告给 Mozilla 的所有数据声明的文件之一中创建探针的条目。

这些文件是:

  • toolkit/components/telemetry/Histograms.json

  • toolkit/components/telemetry/Scalars.yaml

  • toolkit/components/telemetry/Events.yaml

标量允许收集简单值,例如计数、布尔值和字符串,并且应尽可能使用标量而不是直方图。

直方图允许收集多个不同的值,但将其聚合到多个桶中。每个桶都有一个值范围和一个记录了多少值的计数。

事件允许收集许多属性,这些属性与事件发生的类别、方法、对象和值相关联。事件遥测帮助我们讲述用户如何与浏览器交互的故事。

标量和直方图都允许通过键记录数据。这允许更灵活的两级数据收集。

不同的文件格式

数据团队选择 YAML 用于 Scalars.yamlEvents.yaml,因为它易于编写并提供 JSON 中不可用的许多功能,包括注释、可扩展的数据类型、关系锚点、不带引号的字符串以及保留键顺序的映射类型。

虽然我们之前在 histograms.json 中出于类似目的使用了 JSON,但我们在这里使用了 YAML,因为它允许添加注释并且通常更容易编写。

数据团队正在考虑在某个时候将直方图迁移到 YAML 格式。

如果您是第一次添加其中之一,建议您遵循现有条目的样式。

多年来添加了新的数据类型,因此我们的一些探针现在不太适合的情况是完全有可能的。

有关类型(以及一般遥测)的更多信息,请参阅 此页面此其他页面

当然,如有疑问,请随时提问!

Histograms.json 添加探针

我们的条目以 DEVTOOLS_ 为前缀。例如:

  "DEVTOOLS_DOM_OPENED_COUNT": {
    "alert_emails": ["[email protected]"],
    "expires_in_version": "never",
    "kind": "count",
    "bug_numbers": [1343501],
    "description": "Number of times the DevTools DOM Inspector has been opened.",
    "releaseChannelCollection": "opt-out"
  },
  "DEVTOOLS_DOM_TIME_ACTIVE_SECONDS": {
    "alert_emails": ["[email protected]"],
    "expires_in_version": "never",
    "kind": "exponential",
    "bug_numbers": [1343501],
    "high": 10000000,
    "n_buckets": 100,
    "description": "How long has the DOM inspector been active (seconds)"
  },

您可以使用不同类型的探针。这些由 kind 字段指定。通常我们使用 count 来计算工具打开的次数,使用 exponential 来计算面板激活的次数。

Scalars.yaml 添加探针

我们的条目以 devtools. 为前缀。例如:

devtools.toolbar.eyedropper:
  opened:
    bug_numbers:
      - 1247985
      - 1352115
    description: Number of times the DevTools Eyedropper has been opened via the inspector toolbar.
    expires: never
    kind: uint
    notification_emails:
      - [email protected]
    release_channel_collection: opt-out
    record_in_processes:
      - 'main'

devtools.copy.unique.css.selector:
  opened:
    bug_numbers:
      - 1323700
      - 1352115
    description: Number of times the DevTools copy unique CSS selector has been used.
    expires: "57"
    kind: uint
    notification_emails:
      - [email protected]
    release_channel_collection: opt-out
    record_in_processes:
      - 'main'

Events.yaml 添加探针

我们的条目以 devtools. 为前缀。例如:

devtools.main:
  open:
    objects: ["tools"]
    bug_numbers: [1416024]
    notification_emails: ["[email protected]", "[email protected]"]
    record_in_processes: ["main"]
    description: User opens devtools toolbox.
    release_channel_collection: opt-out
    expiry_version: never
    extra_keys:
      entrypoint: How was the toolbox opened? CommandLine, ContextMenu, HamburgerMenu, KeyShortcut, SessionRestore or SystemMenu
      first_panel: The name of the first panel opened.
      host: "Toolbox host (positioning): bottom, side, window or other."
      splitconsole: Indicates whether the split console was open.
      width: Toolbox width (px).

2. 在 DevTools 代码中使用 Histograms.json 探针

Histograms.json 文件中声明探针后,您需要在我们的代码中实际使用它。

首先,您需要在 devtools/client/shared/telemetry.js 中为其指定一个 ID。与 Histograms.json 的情况类似,您需要遵循现有条目的样式。例如:

dom: {
  histogram: "DEVTOOLS_DOM_OPENED_COUNT",
  timerHistogram: "DEVTOOLS_DOM_TIME_ACTIVE_SECONDS"
},

… 将对应于我们在上一节中声明的探针。

然后,在每个需要遥测的工具上包含该模块

let Telemetry = require("devtools/client/shared/telemetry");

在工具构造函数上创建一个遥测实例

this._telemetry = new Telemetry({ useSessionId: true });

useSessionId 允许在随机唯一的“session_id”额外属性后面聚合所有记录。例如,这有助于聚合为一个特定的工具箱实例记录的所有数据。

并使用该实例报告例如工具打开…

this._telemetry.toolOpened("mytoolname", this);

… 或关闭

this._telemetry.toolClosed("mytoolname", this);

请注意,mytoolname 是我们在 telemetry.js 模块中声明的 ID。

3. 在 DevTools 代码中使用 Scalars.yaml 探针

Scalars.yaml 文件中声明探针后,您需要在我们的代码中实际使用它。

首先,您需要在 devtools/client/shared/telemetry.js 中为其指定一个 ID。您需要遵循现有小写直方图条目的样式。例如:

toolbareyedropper: {
  scalar: "devtools.toolbar.eyedropper.opened", // Note that the scalar is lowercase
},
copyuniquecssselector: {
  scalar: "devtools.copy.unique.css.selector.opened",
},

… 将对应于我们在上一节中声明的探针。

然后,在每个需要遥测的工具上包含该模块

let Telemetry = require("devtools/client/shared/telemetry");

在工具构造函数上创建一个遥测实例

this._telemetry = new Telemetry();

并使用该实例报告例如工具打开…

this._telemetry.toolOpened("mytoolname", this);

注意

  • mytoolname 是我们在 Scalars.yaml 模块中声明的 ID。

  • 因为我们没有在 Scalars.yaml 中记录工具打开的时间,所以我们不关心 toolClosed。当然,如果 telemetry.jshistograms.json 中定义了相应的 timerHistogram 字段,则也应添加 toolClosed

4. 在 DevTools 代码中使用 Events.yaml 探针

Events.yaml 文件中声明探针后,您需要在我们的代码中实际使用它。

至关重要的是要理解事件遥测有一个字符串标识符,该标识符由 categorymethodobject(名称)和事件发生时所在的 value 构成。此键指向一个“extra”对象,其中包含有关事件的更多信息(我们将在本节后面给出示例)。

由于这些“extra”对象可以来自完全独立的代码路径,因此我们可以发送事件并将其保留在挂起状态,直到收到所有预期的额外属性。

首先,在每个需要遥测的工具中包含遥测模块

let Telemetry = require("devtools/client/shared/telemetry");

在工具构造函数上创建一个遥测实例

this._telemetry = new Telemetry();

并使用该实例报告例如工具打开…

// If you already have all the properties for the event you can send the
// telemetry event using:
// this._telemetry.recordEvent(method, object, value, extra) e.g.
this._telemetry.recordEvent("open", "tools", null, {
  "entrypoint": "ContextMenu",
  "first_panel": "Inspector",
  "host": "bottom",
  "splitconsole": false,
  "width": 1024,
});

// If your "extra" properties are in different code paths you will need to
// create a "pending event." These events contain a list of expected properties
// that can be populated before or after creating the pending event.

// Use the category, method, object, value combinations above to add a
// property... we do this before creating the pending event simply to
// demonstrate that properties can be sent before the pending event is created.
this._telemetry.addEventProperty(
  this, "open", "tools", null, "entrypoint", "ContextMenu");

// In this example `"open", "tools", null` make up the
// signature of the event and needs to be sent with all properties.

// Create the pending event using
// this._telemetry.preparePendingEvent(this, method, object, value,
// expectedPropertyNames) e.g.
this._telemetry.preparePendingEvent(this, "open", "tools", null,
  ["entrypoint", "first_panel", "host", "splitconsole", "width", "session_id"]
);

// Use the category, method, object, value combinations above to add each
// property.
this._telemetry.addEventProperty(
  this, "open", "tools", null, "first_panel", "inspector");
this._telemetry.addEventProperty(
  this, "open", "tools", null, "host", "bottom");
this._telemetry.addEventProperty(
  this, "open", "tools", null, "splitconsole", false);
this._telemetry.addEventProperty(
  this, "open", "tools", null, "width", 1024);

// You can also add properties in batches using e.g.:
this._telemetry.addEventProperties(this, "open", "tools", null, {
  "first_panel": "inspector",
  "host": "bottom",
  "splitconsole": false,
  "width": 1024
});

注意

  • mytoolname 是我们在 Scalars.yaml 模块中声明的 ID。

  • 因为我们没有在 Scalars.yaml 中记录工具打开的时间,所以我们不关心 toolClosed。当然,如果 telemetry.jshistograms.json 中定义了相应的 timerHistogram 字段,则也应添加 toolClosed

关于顶级面板的说明

选项卡的代码使用它们的 ID 在您在面板之间切换时自动报告遥测,因此您无需在顶级面板上显式调用 toolOpenedtoolClosed

您仍然需要在子面板或 about:debugging 等未作为选项卡打开的工具上调用这些函数。

测试

如果缺少 ID,遥测模块将向标准输出打印警告。强烈建议确保此功能正常工作,因为该模块会将未声明 ID 的使用情况归因于通用 custom 桶。这对获得准确的结果不利!

要查看这些警告,您需要将 browser.dom.window.dump.enabled 浏览器首选项设置为 true(在 about:config 中)(并重新启动浏览器)。

然后,尝试执行触发遥测调用的操作(例如打开工具)。假设我们在报告工具已打开时输入错误

this._telemetry.toolOpened('mytoolnmae', this);
                                  ^^^^ typo, should be *mytoolname*

将向标准输出报告错误

Warning: An attempt was made to write to the mytoolnmae histogram, which is not defined in Histograms.json

因此,请注意错误。

测试事件遥测

这最好通过示例来说明

/* Any copyright is dedicated to the Public Domain.
 * http://creativecommons.org/publicdomain/zero/1.0/ */

"use strict";

const { Toolbox } = require("devtools/client/framework/toolbox");
const { TelemetryTestUtils } = ChromeUtils.importESModule("resource://testing-common/TelemetryTestUtils.sys.mjs");

const URL = "data:text/html;charset=utf8,browser_toolbox_telemetry_close.js";
const { RIGHT, BOTTOM } = Toolbox.HostType;
const DATA = [
  {
    category: "devtools.main",
    method: "close",
    object: "tools",
    value: null,
    extra: {
      host: "right",
      width: w => w > 0,
    }
  },
  {
    category: "devtools.main",
    method: "close",
    object: "tools",
    value: null,
    extra: {
      host: "bottom",
      width: w => w > 0,
    }
  }
];

add_task(async function() {
  // Let's reset the counts.
  Services.telemetry.clearEvents();

  // Ensure no events have been logged
  TelemetryTestUtils.assertNumberOfEvents(0);

  await openAndCloseToolbox("webconsole", SIDE);
  await openAndCloseToolbox("webconsole", BOTTOM);

  checkResults();
});

async function openAndCloseToolbox(toolId, host) {
  const tab = await addTab(URL);
  const toolbox = await gDevTools.showToolboxForTab(tab, { toolId });

  await toolbox.switchHost(host);
  await toolbox.destroy();
}

function checkResults() {
  TelemetryTestUtils.assertEvents(DATA, {category: "devtools.main", method: "close", object: "tools"});
}

编译它

如果您已编辑 Histograms.jsonEvents.yaml,则需要进行完整的 Firefox 构建,因为它们是在构建时处理的,并且会对其运行各种检查以确保它们有效。

./mach build

如果您使用 mach build faster 或构件构建,则不会执行这些检查,并且当在那里运行这些检查时,您的 Try 构建将失败(“崩溃”)。

节省您的时间,并在本地运行检查。

注意:对 Scalars.yaml 的更改会在执行构件构建时处理。

4. 获得数据团队的批准

在更改进入 mozilla-central 之前,需要获得此批准。

要获得批准,请将您的补丁附加到 Bugzilla 中的错误,并设置两个标记

  • 一个用于数据管理员的 review? 标记。

  • 一个用于 hkirschner 的 needinfo? 标记(我们的产品经理,因此他证明我们正在使用这些数据)。

请务必清楚地解释新探针的用途。例如,“我们正在寻求批准以跟踪用于调试 Web API ABCD 的新面板的打开次数”比在没有背景信息的情况下只是请求反馈要好得多。

此审查不应该花费太长时间:如果存在问题,他们应该告诉您如何解决。如果您在几天后没有看到任何活动迹象,您可以在 #developers 中询问。

请注意,此审查除了正常的同事审查之外。

单击 此处 以获取更多详细信息。

访问现有数据

本地数据

转到 about:telemetry 以查看与您的本地实例相关的统计信息。

全局数据

从大量 Firefox 用户聚合的数据可在 telemetry.mozilla.org 获取。

报告使用 SQL 编写。例如,以下报告比较了 一些 DevTools 面板的使用情况

如果您想更好地了解人们如何使用这些工具,建议您通过编写自己的报告来探索此数据集。

最简单的入门方法是派生现有报告并对其进行修改,以熟悉语法,因为用于海量数据表的 SQL 与用于简单博客引擎的 SQL 非常不同,您会发现一些可能看起来不熟悉的新的运算符。

还建议采取小步骤并经常运行查询,以便在错误过于复杂难以解决之前检测到它们,尤其是在您(尚未)熟悉此操作时。

系统会中断缓慢的查询,因此不必担心“提取过多数据”或“使用过多资源”。内置了保护措施以避免您的代码占用遥测数据库。

有趣的是,如果您居住在欧洲,您可能很幸运,因为该网站在欧洲工作时间比在太平洋工作时间响应更快,因为似乎欧洲与之交互的人数较少。