标量

标量指标可用于跟踪单个值。与收集每个测量的直方图不同,标量仅跟踪单个值,后面的值会完全替换前面的值。

从历史上看,我们开始重载直方图机制以收集标量数据,例如标志值、计数、标签等。标量测量类型是收集此类标量数据的建议方法。序列化后的标量数据与主 ping一起提交。在工件构建和构建更快的流程中支持添加标量。

重要

Firefox 中每个新的或更改的数据收集都需要来自数据管理员的数据收集审查

API

可以通过nsITelemetry 接口C++ API管理标量探针。

JS API

特权 JavaScript 代码中的探针可以使用以下函数来操作标量

Services.telemetry.scalarAdd(aName, aValue);
Services.telemetry.scalarSet(aName, aValue);
Services.telemetry.scalarSetMaximum(aName, aValue);

Services.telemetry.keyedScalarAdd(aName, aKey, aValue);
Services.telemetry.keyedScalarSet(aName, aKey, aValue);
Services.telemetry.keyedScalarSetMaximum(aName, aKey, aValue);

例如,如果对不支持的操作执行标量类型上的操作(例如,在字符串类型的标量上调用 scalarSetMaximum),则这些函数可能会抛出异常。请查看代码文档以获取更多信息。

registerScalars()

Services.telemetry.registerScalars(category, scalarData);

从附加组件注册新的标量。

  • category - (必填,字符串) 标量注册的唯一类别(请参阅限制)。

  • scalarData - (必填,对象) 形式为{scalarName1: scalar1Data, ...}的对象,其中包含多个标量的注册数据;scalarName1限制;每个标量都是具有以下属性的对象

    • kind - (必填,无符号整数) 标量类型之一 (nsITelemetry::SCALAR_TYPE_*).

    • keyed - (可选,布尔值) 是否为键控标量。默认为 false。

    • record_on_release - (可选,布尔值) 是否在发布时记录此数据。默认为 false。

    • expired - (可选,布尔值) 此标量条目是否已过期。这允许在不报错的情况下记录它,但它将被丢弃。默认为 false。

对于从附加组件记录的标量,注册在运行时发生。任何新的标量都必须首先通过此函数注册,然后才能记录。

注册后,可以通过常用的标量 JS API 记录标量。如果累积发生在注册后内容进程中,并且定义仍然必须到达此进程,则它将被丢弃:解决此问题的一种方法是向内容进程发送 IPC 消息,并在收到此消息后开始累积数据。累积的数据将在processes.dynamic.scalars下提交到主 ping 有效负载中。

注意

在动态标量中累积仅适用于内容子进程和父进程。所有累积(父进程和内容子进程)都汇总在一起。

此处注册的新标量与通过Scalars.yaml注册的标量具有相同的限制,例如类别名称的长度或允许的字符。

当附加组件更新时,它们可能会重新注册所有标量。在这种情况下,对已注册标量的任何更改都会被忽略。唯一的例外是过期;使用expired: true重新注册的标量将不再被记录。

示例

Services.telemetry.registerScalars("myAddon.category", {
  "counter_scalar": {
    kind: Ci.nsITelemetry.SCALAR_TYPE_COUNT,
    keyed: false,
    record_on_release: false
  },
});
// Now scalars can be recorded.
Services.telemetry.scalarSet("myAddon.category.counter_scalar", 37);

C++ API

本机代码中的探针可以使用在Telemetry.h中声明的更方便的辅助函数

void ScalarAdd(mozilla::Telemetry::ScalarID aId, uint32_t aValue);
void ScalarSet(mozilla::Telemetry::ScalarID aId, uint32_t aValue);
void ScalarSet(mozilla::Telemetry::ScalarID aId, const nsAString& aValue);
void ScalarSet(mozilla::Telemetry::ScalarID aId, bool aValue);
void ScalarSetMaximum(mozilla::Telemetry::ScalarID aId, uint32_t aValue);

void ScalarAdd(mozilla::Telemetry::ScalarID aId, const nsAString& aKey, uint32_t aValue);
void ScalarSet(mozilla::Telemetry::ScalarID aId, const nsAString& aKey, uint32_t aValue);
void ScalarSet(mozilla::Telemetry::ScalarID aId, const nsAString& aKey, bool aValue);
void ScalarSetMaximum(mozilla::Telemetry::ScalarID aId, const nsAString& aKey, uint32_t aValue);

警告

标量操作旨在价格低廉,而不是免费。如果您希望在性能敏感的代码段中操作标量,请在本地存储操作,并在性能敏感的代码段(“热路径”)完成后再更改标量。

YAML 定义文件

标量探针需要在Scalars.yaml定义文件中注册,原因是为了验证和透明度。

定义文件中的探针以固定深度的两级结构表示

# The following is a category.
a.category.hierarchy:
  a_probe_name:
    kind: uint
    ...
  another_probe:
    kind: string
    ...
  ...
category2:
  probe:
    kind: int
    ...

类别和探针名称需要遵循一些规则

  • 它们各自不能超过 40 个字符;

  • 类别名称必须是字母数字 + .,不能以数字或.开头或结尾;

  • 探针名称必须是字母数字 + _,不能以数字或_开头或结尾。

探针可以定义如下

a.category.hierarchy:
  a_scalar:
    bug_numbers:
      - 1276190
    description: A nice one-line description.
    expires: never
    kind: uint
    notification_emails:
      - [email protected]

必填字段

  • bug_numbers:表示探针引入的错误编号的无符号整数列表。

  • description:描述探针收集什么数据以及何时收集数据的单行或多行字符串。

  • expires:标量过期的版本号,例如“30”;类型为“N”的版本号会自动转换为“N.0a1”,以便在开发渠道中使标量过期。作用于过期标量的遥测探针会将警告打印到浏览器控制台中。对于永不过期的标量,可以使用值never

  • kind:表示标量类型的字符串。允许的值为uintstringboolean

  • notification_emails:一组电子邮件地址,用于通知即将过期的探针警报。更重要的是,数据管理员使用这些地址来验证探针是否仍然有用。

  • products:标量可以在其上记录的产品列表。当前支持的值为

    • firefox - 在 Firefox 桌面版中收集,以便通过 Firefox 遥测提交。

    • thunderbird - 在 Thunderbird 中收集,以便通过 Thunderbird 遥测提交。

  • record_in_processes:允许标量在其中记录的进程列表。当前支持的值为

    • main;

    • content;

    • gpu;

    • all_children(在所有子进程中记录);

    • all(在所有进程中记录)。

可选字段

  • release_channel_collection:可以是opt-in(默认值)或opt-out。对于前者,标量默认在预发布渠道上提交,除非用户已选择退出。对于后者,标量默认在发布和预发布渠道上提交,除非用户已选择退出。

  • keyed:确定这是否为键控标量的布尔值。默认为false

  • keys:字符串列表。仅对键控标量有效。定义允许用于此标量的键的不区分大小写的列表。列表限制为 100 个键,每个键的最大长度为 72 个字符。使用列表中不存在的键时,会返回错误。

  • record_into_store:应将此标量记录到的存储列表。默认为[main]

字符串类型限制

为了防止滥用,字符串标量的内容长度限制为 50 个字符。尝试设置更长的字符串会导致错误,并且不会设置任何字符串。

键控标量

键控标量是由字符串键索引的uintboolean标量类型的集合,该键可以包含 UTF8 字符,并且长度不能超过 72 个字符。键控标量最多可以包含 100 个键。例如,当您希望按名称细分某些计数时,此标量类型很有用,例如使用哪个搜索引擎进行搜索的频率。

不支持键控string标量。

仅当事先不知道键集时,才应使用键控标量。如果键来自一组已知的字符串,则如果合适,优先考虑其他选项,例如分类直方图或将测量拆分为单独的标量。

多进程注意事项

在同一类型的不同进程(例如,多个内容进程)中记录数据时,用户负责防止标量操作之间的竞争条件。竞争条件可能发生,因为标量更改会从每个子进程发送到父进程,然后合并到最终存储位置。由于进程之间没有同步,因此如果从多个子进程发送,则setMaximum之类的操作可能会产生不同的结果。

处理器脚本

标量定义文件在编译时进行处理和检查以确保正确性。如果它符合规范,则处理器脚本会生成两个 C++ 头文件,由 Telemetry C++ 核心包含。

gen_scalar_data.py

构建系统会调用此脚本,根据标量定义生成 TelemetryScalarData.h C++ 头文件。此头文件包含一个保存标量名称和版本字符串的数组,以及一个包含表示所有标量的 ScalarInfo 结构的数组。

gen_scalar_enum.py

构建系统会调用此脚本,根据标量定义生成 TelemetryScalarEnums.h C++ 头文件。此头文件包含一个枚举类,其中包含所有用于通过 C++ API 从代码访问标量的标量标识符。

添加新的探针

进行标量测量是一个两步过程

  1. 将探针定义添加到标量注册表中;

  2. 使用 API 记录到标量中。

注册标量

让我们首先在 Scalars.yaml 定义文件中注册两个探针:一个简单的布尔标量和一个带键的无符号标量。

# The following section contains the demo scalars.
profile:
  was_reset:
    bug_numbers:
      - 1301364
    description: True if the profile was reset.
    expires: "60"
    kind: boolean
    notification_emails:
      - [email protected]
    release_channel_collection: opt-out
    record_in_processes:
      - 'main'

ui:
  download_button_activated:
    bug_numbers:
      - 1301364
    description: >
      The number of times the download button was activated, per
      input type (e.g. 'mouse_click', 'touchscreen', ...).
    expires: "60"
    kind: uint
    keyed: true
    notification_emails:
      - [email protected]
    release_channel_collection: opt-in
    record_in_processes:
      - 'main'

这两个标量具有不同的收集策略,并且都限制为仅在主进程中记录。例如,ui.download_button_activated 只能由在运行 Firefox 预发布版本的用户的记录。

使用 JS API

从特权 JavaScript 代码更改演示标量非常简单

// Set the scalar value: trying to use a non-boolean value doesn't throw
// but rather prints a warning to the browser console
Services.telemetry.scalarSet("profile.was_reset", true);

// This call increments the value stored in "mouse_click" within the
// "ui.download_button_activated" scalar, by 1.
Services.telemetry.keyedScalarAdd("ui.download_button_activated", "mouse_click", 1);

更多用法示例可以在涵盖 JS 标量 API子进程标量 的测试中找到。

使用 C++ API

原生代码也可以通过包含 Telemetry.h 头文件来利用标量。

Telemetry::ScalarSet(Telemetry::ScalarID::PROFILE_WAS_RESET, false);

Telemetry::ScalarAdd(Telemetry::ScalarID::UI_DOWNLOAD_BUTTON_ACTIVATED,
                     u"touchscreen"_ns, 1);

ScalarID 枚举由构建过程自动生成,此处 提供了一个示例。

其他示例可以在标量 C++ API 的 测试覆盖范围 中找到。

版本历史

  • Firefox 134:移除 operating_systems错误 1925369)。

  • Firefox 79:移除 geckoview 支持(参见 错误 1620395)。

  • Firefox 50:初始标量支持(错误 1276195)。

  • Firefox 51:添加带键标量(错误 1277806)。

  • Firefox 53:添加子进程标量(错误 1278556)。

  • Firefox 58

    • 添加了从附加组件记录新标量的支持(错误 1393801)。

    • 忽略为类别重新注册现有标量,而不是失败(错误 1409323)。

  • Firefox 60:启用在工件构建和构建加速工作流中添加标量的支持(错误 1425909)。

  • Firefox 66:将 cpp_guard 替换为 operating_systems错误 1482912)`