直方图

在 Firefox 中,遥测系统收集 Firefox 性能、硬件、使用情况和自定义设置的各种度量,并将其提交到 Mozilla。单个客户端收集的遥测数据可以通过集成的 about:telemetry 浏览器页面进行检查,而整个用户群体的聚合报告则可在 telemetry.mozilla.org 上公开获取。

重要

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

以下部分说明如何向遥测添加新的度量。

概述

遥测直方图是收集数值度量(如多个计数或计时)的有效方法。它们通过通用 API 收集,并与 主 Ping 自动提交。

提示

在添加新的直方图之前,您应该考虑使用其他收集机制。例如,如果需要跟踪单个标量值(例如数字、布尔值或字符串),则应使用 标量

下面的直方图取自 Firefox 的 about:telemetry 页面。它显示了一个用于跟踪插件关闭时间的直方图以及在单个 Firefox 会话中收集的数据。计时数据被分组到桶中,其中蓝色条形的高度表示每个桶中项目的数量。例如,最高的条形表示有 63 个插件关闭时间在 129 毫秒到 204 毫秒之间。

../../../../_images/sampleHistogram.png

about:telemetry 页面上的直方图仅显示直方图中非空的桶,除了第一个非空桶左侧的桶和最后一个非空桶右侧的桶。

选择直方图类型

添加新直方图的第一步是选择最能代表正在测量的数据的直方图类型。上面使用的示例直方图是“指数”直方图。

注意

只有 flagcount 直方图具有默认值。所有其他直方图最初为空,只有在记录值时才会提交。

boolean

这些直方图仅记录布尔值。在单个浏览器会话期间,可以在同一直方图中记录多个布尔条目,例如,如果直方图正在测量对话框中用户选择的选项“是”或“否”,则每次显示对话框时都会添加一个新的布尔值。

linear

线性直方图类似于枚举直方图,只是每个桶与一个值范围相关联,而不是单个枚举值。每个桶覆盖的值范围从前一个桶线性增加,例如,一个桶可能计算 0 到 9 之间的值出现的次数,下一个桶将覆盖 10 到 19 的值,下一个 20 到 29 的值,依此类推。如果存储在直方图中的最小值和最大值之间没有数量级差异,则此桶类型很有用,例如,如果要存储的值是 0-100% 的百分比。

注意

如果需要一个桶为 < 0、1、2 … N > 的线性直方图,则应声明一个枚举直方图。添加此限制是为了防止开发人员在指定线性直方图中桶的数量时犯常见的“多算一个”错误。

exponential

指数直方图类似于线性直方图,但每个桶覆盖的值范围呈指数增长。例如,考虑 I/O 操作的计时,其持续时间通常可能在 0 毫秒到 50 毫秒之间,但极端情况下的持续时间可能为秒或分钟。对于此类测量,您希望在正常范围内进行更细粒度的分桶,但在极大值附近进行更粗粒度的分桶。指数直方图符合此要求,因为它在最小值附近具有“窄”桶,在最大值附近具有明显“宽”桶。

categorical

分类直方图类似于枚举直方图。但是,您不是指定 n_buckets,而是在 labels 字段中指定一个字符串数组。从 JavaScript 中,标签值或其索引可以作为字符串传递给 histogram.add()。从 C++ 中,您可以使用 AccumulateCategorical 并传递来自相应 Telemetry::LABEL_* 枚举的值,或者在特殊情况下使用字符串值。

注意

您可以在以后向分类直方图添加新的标签,直到配置的最大值。分类直方图默认支持最多 50 个标签,但您可以使用 n_values 属性将其设置得更高。如果以后需要添加超过最大值的标签,则需要使用新的直方图名称。有关详细信息,请参阅 更改直方图

enumerated

此直方图类型旨在存储“枚举”值,当您无法指定标签并且因此无法使用 categorical 直方图时。枚举直方图由固定数量的(由 n_values 指定)组成,每个桶都与一个连续的整数值(桶的标签)相关联,从 0n_values。每个桶对应一个枚举值,并计算其特定枚举值被记录的次数;除了 n_values 桶,它计算所有大于或等于 n_values 的值。

例如,如果要跟踪 SSL 握手类型的相对受欢迎程度,则可以使用此类型的直方图。每当浏览器启动 SSL 握手时,它都会记录一个有限数量的枚举值之一,这些值唯一地标识握手类型。

注意

n_values 设置为略大于所需的值,以便将来允许新的枚举值。如果以后需要添加更多枚举,请参阅 更改直方图

flag

已弃用(请使用布尔 标量)。

此直方图类型允许您记录单个值(01,默认为 0)。如果需要跟踪某个功能在 Firefox 会话期间是否曾经使用过,则此类型很有用。您只需要添加一行代码,在使用该功能时设置该标志,因为直方图初始化为 0/false(未设置标志)的默认值。因此,不允许记录值为 0 并会断言。

设置标志后,标志直方图将忽略任何更改,因此一旦设置标志,就无法取消设置。

count

已弃用(请使用 uint 标量)。

当您想记录某个内容的计数时,使用此直方图类型。它只存储单个值,默认为 0

键控直方图

键控直方图是上述直方图类型之一的集合,由字符串键索引。例如,当您想根据名称细分某些计数时,这很有用,例如,使用哪个搜索引擎进行搜索的频率。请注意,当您需要记录一小部分已知键时,使用单独的普通直方图效率更高。

警告

键控直方图目前在 直方图更改检测器 中不受支持。

声明直方图

直方图应在 Histograms.json 文件中声明。这些声明在 编译时 检查其正确性,并用于生成 C++ 代码。

以下是 Histograms.json 中名为 MEMORY_RESIDENT 的直方图的示例直方图声明,该直方图跟踪进程使用的驻留内存量

{
  "MEMORY_RESIDENT": {
    "record_in_processes": ["main", "content"],
    "alert_emails": ["[email protected]"],
    "expires_in_version": "never",
    "kind": "exponential",
    "low": 32768,
    "high": 1048576,
    "n_buckets": 50,
    "bug_numbers": [12345],
    "description": "Resident memory size (KB)"
  }
}

以毫秒或微秒为单位跟踪时间的直方图,其名称应分别以"_MS""_US"作为后缀。标志类型的直方图的名称应以"_FLAG"作为后缀。

直方图声明中可能包含的字段如下所示。

record_in_processes

必填。此字段是一个列表,包含此直方图可以在其中记录的进程。当前支持的值为

  • main

  • content

  • gpu

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

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

alert_emails

必填。此字段是一个电子邮件地址列表,当直方图的分布在一个构建 ID 到另一个构建 ID 之间发生显著变化时,应通知这些地址。这对于检测回归很有用。请注意,所有警报都会自动发送到 mozilla.dev.telemetry-alerts。

expires_in_version

必填。直方图失效的版本号;例如,值“30”表示直方图从 Firefox 30 开始停止记录。类型为"N"的版本号会自动转换为"N.0a1",以便在开发通道中使直方图失效。对于永不失效的直方图,可以使用"never"作为值,如上例所示。将数据累积到已失效的直方图中实际上是一个无操作,不会记录任何内容。

kind

必填。上一节中描述的直方图类型之一。不同的直方图类型要求声明中存在不同的字段。

keyed

可选,布尔值,默认为false。确定这是否是一个键控直方图

keys

可选,字符串列表。仅对键控直方图有效。定义一个区分大小写的允许键列表,这些键可用于此直方图。该列表限制为 30 个键,每个键的最大长度为 20 个字符。使用列表中不存在的键时,累积将被丢弃,并且会向浏览器控制台打印警告。

low

可选,默认值为1。此字段表示直方图中预期的最小值。请注意,所有直方图都会自动获得一个标签为0的桶,用于统计低于low值的数值。如果直方图未指定low值,则它将始终具有一个"0"桶(用于负值或零值)和一个"1"桶(用于1到下一个桶之间的值)。

high

线性直方图和指数直方图必填。线性或指数直方图中要存储的最大值。任何记录的值都大于此最大值,都将在最后一个桶中统计。

n_buckets

线性直方图和指数直方图必填。线性或指数直方图中的桶数。

注意

n_buckets的最大值为 100。桶越多,我们的用户和我们的管道承担的存储和传输成本就越大。

n_values

枚举直方图必填。类似于 n_buckets,它表示枚举中元素的数量。

注意

n_values的最大值为 100。值越多,我们的用户和我们的管道承担的存储和传输成本就越大。

labels

分类直方图必填。这是一个字符串数组,是此直方图中不同值的标签。标签限制为 C++ 友好的字符子集(^[a-z][a-z0-9_]+[a-z0-9]$)。此字段限制为 100 个字符串,每个字符串的最大长度为 20 个字符。

bug_numbers

所有新直方图必填。这是一个整数数组,至少应包含添加探测器的错误号,以及影响其行为的其他错误号。

description

必填。直方图跟踪数据的描述,例如 _“驻留内存大小”_。

releaseChannelCollection

可选。这是以下之一:

  • "opt-in":(默认值)此直方图在预发布通道上默认提交,除非用户选择退出。

  • "opt-out":此直方图在发布和预发布通道上默认提交,除非用户选择退出。

警告

由于它们是默认收集的,因此在数据收集审查期间,选择退出探测器需要满足比选择加入探测器更高的“用户利益”阈值。

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

products

必填。此字段是此直方图可以在其上记录的产品列表。当前支持的值为

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

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

record_into_store

可选。此字段是此直方图应记录到的存储列表。如果省略此字段,则默认为[main]

更改直方图

在直方图发布后更改直方图声明非常棘手。许多工具(如聚合器)假设直方图不会更改。当前建议的程序是更改直方图的名称。

  • 更改现有直方图时,建议的模式是使用版本化的名称(PROBEPROBE_2PROBE_3 等)。

  • 对于枚举直方图,建议将“n_buckets”设置为略大于所需的值,因为将来可能会向枚举中添加新元素。

唯一的例外是分类直方图。可以通过添加标签来更改它们,直到达到配置的最大值(默认为 50,或n_values的值)。如果需要更改配置的最大值,则必须如上所述更改直方图名称。

直方图值

您可以累积到直方图的值受其内部表示的限制。

遥测直方图不记录负值,而是在记录之前将其钳位到 0。

遥测直方图不记录大于 2^31 的值,而是在记录之前将其钳位到 INT_MAX。

添加 JavaScript 探测器

遥测探测器是测量和存储直方图中值的代码。特权 JavaScript 代码中的探测器可以使用nsITelemetry接口获取直方图对象的引用。通过在直方图对象上调用add来记录直方图中的新值。

let histogram = Services.telemetry.getHistogramById("PLACES_AUTOCOMPLETE_1ST_RESULT_TIME_MS");
histogram.add(measuredDuration);

let keyed = Services.telemetry.getKeyedHistogramById("TAG_SEEN_COUNTS");
keyed.add("blink");

请注意,如果nsITelemetry.getHistogramById()使用无效的直方图 ID 调用,则会引发NS_ERROR_FAILURE JavaScript 异常。add()函数如果失败不会引发异常,而是会在浏览器控制台中打印错误。

警告

使用 Artifact 构建无法添加新的遥测探测器。需要完整的构建。

对于测量时间的直方图,可以使用 TelemetryStopwatch 来避免手动处理日期。

TelemetryStopwatch.start("SEARCH_SERVICE_INIT2_MS");
TelemetryStopwatch.finish("SEARCH_SERVICE_INIT2_MS");

TelemetryStopwatch.start("FX_TAB_SWITCH_TOTAL_MS");
TelemetryStopwatch.cancel("FX_TAB_SWITCH_TOTAL_MS");

添加 C++ 探测器

本机代码中的探测器也可以使用nsITelemetry接口,但Telemetry.h中声明的辅助函数更方便。

#include "mozilla/Telemetry.h"

/**
 * Adds sample to a histogram defined in Histograms.json
 *
 * @param id - histogram id
 * @param sample - value to record.
 */
void Accumulate(HistogramID id, uint32_t sample);

/**
 * Adds samples to a histogram defined in Histograms.json
 *
 * @param id - histogram id
 * @param samples - values to record.
 */
void Accumulate(HistogramID id, const nsTArray<uint32_t>& samples);

/**
 * Adds sample to a keyed histogram defined in Histograms.h
 *
 * @param id - keyed histogram id
 * @param key - the string key
 * @param sample - (optional) value to record, defaults to 1.
 */
void Accumulate(HistogramID id, const nsCString& key, uint32_t sample = 1);

/**
 * Adds time delta in milliseconds to a histogram defined in Histograms.json
 *
 * @param id - histogram id
 * @param start - start time
 * @param end - (optional) end time, defaults to TimeStamp::Now().
 */
void AccumulateTimeDelta(HistogramID id, TimeStamp start, TimeStamp end = TimeStamp::Now());

/**
 * Adds time delta in milliseconds to a keyed histogram defined in Histograms.json
 *
 * @param id - histogram id
 * @param key - the string key
 * @param start - start time
 * @param end - (optional) end time, defaults to TimeStamp::Now().
 */
void AccumulateTimeDelta(HistogramID id, const cs TimeStamp start, TimeStamp end = TimeStamp::Now());

/** Adds time delta in milliseconds to a histogram defined in TelemetryHistogramEnums.h
 *
 * @param id - histogram id
 * @param key - the string key
 * @param start - start time
 * @param end - (optional) end time, defaults to TimeStamp::Now().
 */
void AccumulateTimeDelta(HistogramID id, const nsCString& key, TimeStamp start, TimeStamp end = TimeStamp::Now());

Histograms.json中声明的直方图名称会转换为mozilla::Telemetry命名空间中的常量。

mozilla::Telemetry::Accumulate(mozilla::Telemetry::STARTUP_CRASH_DETECTED, true);

警告

遥测累积旨在廉价,而不是免费。如果希望在性能敏感的代码段中累积值,请在本地存储累积值,并在性能敏感的代码段(“热路径”)完成后累积。

Telemetry.h头文件还声明了辅助类AutoTimerAutoCounter。这些类型的对象在超出范围时会自动记录直方图值。

nsresult
nsPluginHost::StopPluginInstance(nsNPAPIPluginInstance* aInstance)
{
  Telemetry::AutoTimer<Telemetry::PLUGIN_SHUTDOWN_MS> timer;
  ...
  return NS_OK;
}

如果在编译时不知道 HistogramID,则可以使用RuntimeAutoTimerRuntimeAutoCounter类,它们的行为类似于模板参数化的AutoTimerAutoCounter类。

void
FunctionWithTiming(Telemetry::HistogramID aTelemetryID)
{
  ...
  Telemetry::RuntimeAutoTimer timer(aTelemetryID);
  ...
}

int32_t
FunctionWithCounter(Telemetry::HistogramID aTelemetryID)
{
  ...
  Telemetry::RuntimeAutoCounter myCounter(aTelemetryID);
  ++myCounter;
  myCounter += 42;
  ...
}

如果可能,请在热路径上优先使用模板参数化的AutoTimerAutoCounter