添加新的指标类型¶
本文档介绍如何在 FOG 中添加新的指标类型。只有在 Glean SDK 中添加了新的指标类型,并且 Firefox 桌面版需要该类型时,才需要执行此操作。
IPC¶
有关 IPC 设计的详细信息,包括禁止操作的列表,请参阅 FOG IPC 文档。
添加新的指标类型时,主要的 IPC 注意事项包括
哪些操作默认情况下是被禁止的,因为它们不是可交换的?
大多数
set
样式的操作无法在多个进程之间进行合理的协调。但是,通过使用
permit_non_commutative_operations_over_ipc
指标元数据属性,仍然可以使用这些“默认情况下禁止”的操作。
此指标在非主进程中将具有什么部分表示形式?换句话说,它在 IPC 负载 中将占用什么形状的存储空间?
例如,计数器可以将所有部分计数聚合到单个“部分和”中。因此,它在 IPC 负载中的表示形式 仅为每个计数器的一个数字。
相反,计时分布的桶安排仅核心知道,因此它无法组合子进程中的样本计数。相反,我们以最高分辨率(纳秒)记录持续时间,并通过 IPC 发送一系列高精度样本。
为了在指标类型中实现 IPC 支持,我们将 FOG 的 Rust 指标实现分为三个部分
一个名为
MetricTypeMetric
的总括enum
。它具有
Child
和Parent
变体。如果存在仅偶尔需要支持的不可交换操作,则还需要
UnorderedChild
变体。它将通过 Rust 代码生成的with_unordered_ipc
构造函数构建。
它具有 IPC 意识,并负责
如果在非父进程上,则将部分表示形式存储在 IPC 负载中,并在调用禁止的非测试 API 时记录错误。(或者如果调用测试 API 则发生恐慌。)
如果在父进程上,则在其内部 Rust 语言绑定指标上分派 API 调用。
父进程实现由 RLB 提供。
为了进行测试,它将标识此特定指标的
MetricId
以跨进程方式存储。为了进行测试,它公开了
child_metric()
函数以创建其Child
等效项。为了进行测试,以及如果它支持非父进程中的操作,它公开了
metric_id()
函数以访问存储的MetricId
。
MetricTypeIpc
是非父进程实现。如果它确实支持非父进程中的操作,则它将标识此特定指标的
MetricId
以跨进程方式存储。
镜像¶
FOG 可以通过 Glean Interface For Firefox Telemetry 将 Glean 指标镜像到 Telemetry 探测器。
此指标类型可以镜像吗?是否应该镜像?
如果是,请为其添加一个相应的 Telemetry 探测器进行镜像,并在 GIFFT 文档 中记录兼容性。
GIFFT 测试¶
如果添加了 GIFFT 镜像,请不要忘记测试镜像是否正常工作。可以通过向 toolkit/components/glean/tests/xpcshell/test_GIFFT.js
添加任务来实现。
GIFFT C++ 状态:典型的锁定和关闭¶
某些指标类型(labeled_*
、timespan
、timing_distribution
)需要在 C++ 中保持状态才能使 GIFFT 工作。Ping 也保持状态以支持 testBeforeNextSubmit()
。如果新的指标类型需要 C++ 中的状态,则当前最先进的技术是 StaticDataMutex
锁定的 UniquePtr
到 nsTHashTable
。对内部映射的访问受锁保护,并通过单个访问函数进行控制和延迟初始化。例如,请参阅 Ping 的 GetCallbackMapLock()
。
清除此状态以避免泄漏非常重要。(请参阅 bug 1752417。)但是,检测可能会随时调用指标 API。
因此,GIFFT 在 AppShutdownTelemetry
关闭阶段 之后明确停止支持这些需要状态的操作。这是因为在下一个阶段(XPCOMWillShutdown
)我们将清除状态。
Rust¶
FOG 使用 Rust 语言绑定 API(glean
仓)并在其之上添加了一层 IPC。
IPC 添加和 glean-core 特性实现位于 fog
仓的 private
模块 中。
每个指标类型都有自己的文件,模仿 glean_core
和 glean
中的结构。当然,除非该指标是带标签的指标类型。然后子指标类型有自己的文件,并且需要通过为新类型实现 Sealed
(按照 api/src/private/labeled.rs
中的模式)来为其添加“标签”。
目前,指标类型上的每个方法都是公开的,包括测试方法,并且至少是通过 指标特性 公开的全部方法。
为了支持 IPC 和 MLA FFI(见下文),我们通过 MetricId 识别指标实例,并将它们存储在 metrics.rs
的 __glean_metric_maps
模块 中的映射中。这项工作由 rust.py
和 rust(_pings).jinja2
扩展到 glean_parser
完成,这些扩展可以在 build_scripts/glean_parser_ext/
文件夹 中找到。
对于新的指标类型,你不应该需要编辑这些文件,因为为此类型对 glean_parser
进行的原始修改应该已经生成了正确的代码。
Rust 测试¶
你应该能够在 Rust 单元测试中对基本功能进行冒烟测试。你可以在指标类型实现文件中直接执行此操作。
C++ 和 JS¶
C++ 和 JS API 的实现位于 Rust API 之上。我们将它们一起处理,因为尽管它们是不同的语言,但它们都在 C++ 中实现,并且共享大部分实现。
总体设计是在多语言架构 (MLA) 的 FFI 之上构建 C++ API,然后在 C++ API 之上构建 JS API。这使得像 Glean Interface For Firefox Telemetry (GIFFT) 这样的仅针对 C++ 和 JS 的功能可以在 C++ 层中更简单地实现。不鼓励对此进行例外(JS 直接使用 FFI 的情况)。
每个指标类型都有六个部分需要涵盖
1. MLA FFI¶
使用我们方便的宏,在
api/src/ffi/
中的 Rust API 之上定义指标类型的多语言架构 FFI 层。
2. C++ 实现¶
在
bindings/private/
中的mozilla::glean::impl
中实现一个名为XMetric
(例如CounterMetric
)的类型。其方法名称应与 Rust API 中的方法名称相同,转换为
CamelCase
。它们都应该是公开的。
将 FFI 的
test_have
和test_get
函数多路复用到单个TestGetValue
函数中,该函数返回一个包装 C++ 类型(最适合指标类型)的mozilla::Maybe
。
将新的指标类型包含在
bindings/MetricTypes.h
中。将新文件包含在
moz.build
中。头文件应添加到EXPORTS.mozilla.glean.bindings
中,而.cpp
文件应添加到UNIFIED_SOURCES
中。
3. IDL¶
将公共 API(包括其文档)复制到 dom/webidl/GleanMetrics.webidl 中,名称为
GleanX
(例如GleanCounter
)。继承自
GleanMetric
。此处的命名风格为
lowerCamelCase
。如果指标方法是保留字,则在其前面加上
_
。Web IDL 绑定使用 自己的类型映射。如果您选择最接近 C++ 类型的映射,将会使您的工作更轻松。
在
dom/bindings/Bindings.conf
中添加新的映射。'GleanX': { 'nativeType': 'mozilla::glean::GleanX', 'headerFile': 'mozilla/glean/bindings/X.h', },
如果不这样做,您将收到一个构建错误,提示
fatal error: 'mozilla/dom/GleanX.h' file not found
。
4. JS 实现¶
在与 toolkit/components/glean/bindings/private/ 中的
XMetric
相同的头文件和.cpp
文件中实现GleanX
(例如GleanCounter
)类型。它应该拥有一个
XMetric
的实例,并将方法实现委托给它。在
GleanX
的定义中,成员标识符恢复为CamelCase
。仅测试方法可以在失败时抛出
DataError
。查看 Web IDL 绑定文档,了解有关可选、可空和非基本类型的帮助。
6. 测试¶
两种语言意味着两个测试套件。
将一个永不过期的仅测试指标添加到
test_metrics.yaml
中。您可以随意使用聪明的名称,但请确保明确表明它是仅测试指标。
**C++ 测试 (GTest)** - 在
gtest/TestFog.cpp
中添加一个小型测试用例。有关更多详细信息,请参阅 测试文档。
**JS 测试 (xpcshell)** - 在
xpcshell/test_Glean.js
和xpcshell/test_JOG.js
中添加一个小型测试用例。如果您的指标类型支持 IPC 操作,也请在这些测试文件的IPC
变体中添加用例。有关更多详细信息,请参阅 测试文档。
7. API 文档¶
指标 API 文档集中在 Glean SDK 手册 中。
您需要针对 SDK 创建一个拉取请求,为特定指标类型的 API 文档添加 C++ 和 JS 示例。
在两个示例的顶部添加一个通知,说明这些 API 仅在 Firefox 桌面版中可用。
<div data-lang="C++" class="tab">
> **Note**: C++ APIs are only available in Firefox Desktop.
```cpp
#include "mozilla/glean/GleanMetrics.h"
mozilla::glean::category_name::metric_name.Api(args);
```
There are test APIs available too:
```cpp
#include "mozilla/glean/GleanMetrics.h"
ASSERT_EQ(value, mozilla::glean::category_name::metric_name.TestGetValue().ref());
```
</div>
// and again for <div data-lang="JS">
如果您幸运的话,Rust API 可能已经添加了。否则,您还需要为其编写一个示例。
8. 带标签的指标(如有必要)¶
如果您的新指标类型是带标签的,则需要做更多工作。我假设您已经按照上述步骤实现了非带标签的子指标类型。现在您必须向其添加“带标签性”。
这包含五个部分
Rust¶
如果您的新带标签的指标类型支持 IPC,则需要在名为
labeled_x.rs
的文件中构建一个名为LabeledXMetric
的类型(例如 toolkit/components/glean/api/src/private/labeled_counter.rs),该类型在调用之间存储子指标的标签,以便可以将其提供给 IPC 负载。如果您的新带标签的指标类型不支持 IPC,您仍然需要一个
LabeledXMetric
类型,但此类型可以通过在 toolkit/components/glean/api/src/private/mod.rs 中使用pub use self::x::XMetric as LabeledXMetric;
来重新导出未带标签的类型(例如LabeledBooleanMetric
)。
FFI¶
要添加 Rust 用于存储动态生成的子指标实例的可写存储,请在 toolkit/components/glean/build_scripts/glean_parser_ext/templates/rust.jinja2 的
submetric_maps
mod
中将子指标类型的映射作为列表项添加。按照其他模式,将
fog_{your labeled metric name here}_get()
FFI API 添加到api/src/ffi/mod.rs
中。这是 C++ 和 JS 用于根据 ID 分配和检索子指标实例的内容。最后,通过在 toolkit/components/glean/api/src/ffi/macros.rs 中使用
maybe_labeled_with_metric!
子宏,增强with_metric!
宏以识别您的类型有时是带标签的。
C++¶
按照其他模式,为
Labeled<YourSubMetric, E>::{EnumGet|Get}
和Labeled<YourSubMetric, DynamicLabel>
添加模板特化到 toolkit/components/glean/bindings/private/Labeled.h 中。这将确保 C++ 使用者可以获取或创建子指标实例。
JS¶
已经为您处理,因为所有 JS 类型都继承自
GleanMetric
,并且 JS 模板知道将您的新类型添加到NewSubMetricFromIds(...)
中(如果您好奇,请参阅GleanLabeled::NamedGetter
)。
测试¶
带标签的变体需要与步骤 #6 相同的测试。提示:请确保测试两个具有不同值的标签。
Python 测试¶
我们有一套测试,用于确保代码生成生成相应的代码。您应该为您的新指标类型在 该套件 中添加一个指标。您需要重新生成预期的文件。