研究!rsc公司

关于编程的想法和链接,通过

RSS(RSS)

透明遥测的设计
(透明遥测,第2部分)
发布于2023年2月8日,星期三。PDF格式

我认为开源软件项目需要找到一种开源友好的方式来进行遥测。这篇文章是描述透明遥测,这一挑战的一个可能答案。有关基本原理和背景的更多信息,请参阅上一篇文章.有关其他用例,请参见下一个帖子.

透明遥测由五部分组成:

这篇文章依次详细介绍了这些部分。我编写了本地计数器收集的实现为了说服自己,它可以做得足够便宜,但在这篇文章发表时,如今,该体系中没有任何其他部分以任何形式存在。我希望该系统能在2023年建成,我希望其他开源项目也会感兴趣采用这种方法或启发他人探索。

计数

Go工具链程序(以及Go发行版中提供的其他程序,喜欢 工具 编译 工具 兽医,以及其他围棋团队维护程序,如戈普斯政府检查)使用简单的API在本地文件中收集计数器值:

包裹计数器func新建(名称字符串)*计数器func(c*计数器)Inc()func NewStack(名称字符串,帧int)*堆栈函数*堆栈增量()

使用创建基本命名计数器计数器。新建,通常分配给全局变量(这没有初始化时间开销),然后在程序运行时使用股份有限公司方法。

例如,假设我们要监视典型的构建缓存未命中率对于 建造命令。每个命令调用可以在运行期间跟踪未命中率然后增加一桶直方图使用指数空间存储桶:0%、<0.1%、<0.2%、<0.5%、<1%、<2%、<5%、<10%、<20%、<50%和<100%。一周后,这11个计数器记录构建缓存未命中率的分布在该系统上经验丰富。

堆栈计数器类似,但构造函数也接受要录制的最大帧数。每个帧由导入路径、函数名、,和相对于函数开头的行号,例如cmd/compile/internal/base。错误+10.计数器名称是传递的名称的串联构造函数和给定的帧数。例如,一个增量的计数器名称的结果NewStack(“缺少标准导入”, 5)可能是

缺少标准导入cmd/compile/internal/types2.(*检查器).importPackage+39cmd/compile/internal/types2.(*Checker).collectObjects+54cmd/compile/internal/types2.(*Checker).checkFiles+18cmd/compile/internal/types2.(*检查器)。文件+0cmd/compile/internal/types2.(*Config)。检查+2

相对于函数开头的行号在源代码中不相关的编辑中是相当稳定的,使识别相同的堆栈跟踪成为可能甚至跨程序的不同版本。

透明遥测的关键特性之一是上传的报告只包含收集服务器已知的字符串。使用导入路径而不是完整文件路径允许跨不同系统进行聚合更重要的是避免暴露细节,如完整路径到生成Go编译器源代码时存储的目录。另一个重要注意事项是Go工具的修改副本可能包含意外字符串:我们不想知道添加了类型2.Check With ChatGPT到调用堆栈。这个问题只通过保存堆栈跟踪来解决从特定的未修改的、已发布的工具版本。版本信息和任何可以使用构建嵌入二进制文件中的信息.(相比之下,.NET报告完整的文件系统路径,然后记录这是开发人员的责任至“避免无意中泄露信息通过不在“路径名为的目录”中构建软件暴露个人或敏感信息”。遥测系统中不公开私人数据的负担不应放在用户身上。)

计数器文件存储在目录中<用户>/go/遥测/本地/,哪里<用户>是用户配置目录,据报道操作系统。用户配置目录.每个文件都由程序的名称、版本、构建工具链版本、,GOOS和GOARCH,以及本周开始的日期。例如:

/用户/rsc/库/应用程序支持/go/temetry/local/编译-go1.21.1-darwin-arm64-2023-01-04.v1.countgopls@v0.11.0-go1.21.1-linux-386-223-01-04.v1.count...

版本和构建工具链版本仅记录为开发用于未版本化的工具,例如在开发Go本身时。

按周汇总计数器有两个重要目的。首先,它应该有助于减少对隐私的担忧明确表示无法重建任何类型的细粒度用户行为跟踪。第二,每周报告计数器减少持久性引起的统计噪声使用变化,如周末。我见过的每一个长期监控仪表板都开始了通过计算要删除的数据的7天或28天平均值这种高频噪音。客户端删除它可以提供更多的隐私和更干净的报告。

当Go遥测首次创建地方的目录,它随机选择系统一周的开始。我们示例中的系统选择了从周三开始的几周。随机选择周开始将服务器负载分散到一周内并及时报告新问题:如果周二发布了新的Go发行版,七分之一的系统立即安装将在周三的上传中包含周二的操作。

这些文件使用自定义二进制格式,该格式以简单的键值头开头重复输入文件名的信息:

周:2023-01-04程序:编译Go版本:go1.21.1GOOS:达尔文GOARCH:臂64

标头后面是计数器,位于适合内存映射到的磁盘散列表中程序的每个运行实例。这些实例使用无锁原子操作来增加计数器并维护文件,保持与遥测相关的开销非常低。该设计还避免了计数器递增时死锁或无限延迟的可能性,即使程序的一个实例挂起或出现其他错误。一种工具,也许叫做 工具 遥测,将这些二进制文件之一转换为JSON由其他感兴趣的程序进行处理。

请注意,存储在磁盘上的原始数据只是计数器的名称及其相关的64位总计。没有事件日志或任何更详细的跟踪。直接维护计数器的决定,而不是从更详细的跟踪中派生它们主要是考虑磁盘空间和更新延迟。然而,从来没有任何类型的事件日志或跟踪还减少了本地收集对隐私的影响。

本地web服务器(可能 工具 遥测 -http协议)将显示本地计数器并能够绘制图表计数器数据随时间变化以供用户检查(当然,粒度仅为1周)。

配置

透明遥测中的数据采集始于数据的原因正在收集:将要计算的特定图形,以及该图形所需的特定误差范围。通过该图形配置,透明遥测服务器可以计算报告配置,并声明要报告以何种采样率生成该图。

例如,Go构建缓存未命中率的图形化配置我们在上一节中考虑的图表可能如下所示:

title:去构建缓存未命中率类型:直方图错误:1%计数器:go/buildcache/miss:{0,0.1,0.2,0.5,1,2,5,10,20,50100}

对于99%置信水平下1%的误差幅度,我们需要约16000个样本.服务器将跟踪报告系统的估计数量每周调整采样率,以产生正确数量的样本。如果有一百万个报告系统,那么采样率为16000样本为1.6%,因此相应的报告配置将采样每个计数器都具有该概率。

更改收集的内容可以保护隐私因此,我们必须确保对更改进行适当审查。举一个隐私错误的例子,假设一个开发人员错误地认为了解导入最多的标准库包并使用创建了导入路径的直方图计数器。新建(“导入:”+路径)。增量().我不认为这是一个有用的直方图,但隐私错误在于直方图将包括专用用户导入路径和标准库路径。然而,该错误的影响仅限于本地收集,因为图形和报告配置不会提及计数器,如导入:my.company/private/package,所以他们永远不会被报告。

Go工具链的开发人员可能希望添加计数器仅用于本地使用的工具链,以了解它们是否将有助于报告。该决定不应因程序而负担过重,因为风险相对较低。也许我们的标准代码审查过程就足够了,与清晰的文档相匹配计数器是也不适合引入的。

服务器图形配置的更改值得更多关注,因为正如我们所看到的,是图形配置决定了报告哪些计数器。要求进行这样的更改可能是有道理的一个负责所有权和维护的小组的审查在问题跟踪器或Gerrit服务器上。

最后,请注意图形配置中缺少任何类型的通配符。不可能要求从以下开始的所有计数器导入:,这意味着导入:my.company/private/package永远不会被报告,因为图形配置永远不会按名称显式列出该计数器。(任何这样做的尝试都会被公共配置审查过程捕获。)

报告

当计数器文件的一周结束时,工具链程序(甚至长时间运行的程序)自动开始将计数器写入下周的文件。记住,“周”是指从7天开始的一段时间在为每个Go安装随机选择的工作日:在一些机器上,星期是星期日到星期六,其他机器使用星期二到星期一,依此类推。在一周结束后的某个时候,一个报告程序(可能是命令,也许也是戈普斯)将通知已完成的计数器周并开始报告过程。

报告程序使用报告配置找出应报告的计数器。它将用作Go模块(也许遥测.go.dev/config).在浏览器中访问同一个页面会打印出一个漂亮的HTML页面列出已收集的所有计数器,并对每个计数器进行注释收集时的日期范围以及收款的理由。如果认为不再需要某些计数器或者收集起来有问题,它可以从配置中删除,程序将立即停止报告。同样,如果系统因某种原因必须关闭,提供空配置将停止所有报告。

报告配置将是JSON对应的转到Go类型报告配置定义为:

类型ReportConfig结构{GOOS[]字符串GOARCH[]字符串GoVersion[]字符串程序[]ProgramConfig}键入ProgramConfig结构{名称字符串版本[]字符串计数器[]CounterConfig堆栈[]计数器配置}类型CounterConfig结构{名称字符串利率浮动64}

这个报告配置列出了已知的GOOS、GOARCH和Go版本可以报告。这确保了程序测试与实验,未知的操作系统、体系结构或Go版本不会意外收集。类似地程序配置列出了应从中收集的程序以及它们的特定版本,如果它们独立于主Go工具链(比如地鼠政府检查).这个计数器配置列出正在收集的特定计数器以及他们各自的采样率。

报告器首先选择一个介于0和1之间的随机浮点数X。如果X≥0.1,则报告器停止,甚至没有下载配置。例如,如果报告者选择X=0.2,它会立即停止。此步骤对任何计数器或堆栈施加10%的采样率硬限制,它安排了一个特定的Go安装甚至不会下载收集配置平均每两个月不止一次。

假设X<0.1,报告者下载并读取集合配置。然后,它读取所有的per-program计数器文件并对其进行过滤仅包括与GOOS、GOARCH、Go版本、程序名匹配的,和程序版本。它进一步筛选所选报告,以删除配置的速率小于X。例如,如果报告者选择X=0.05,它将报告计数器配置了速率0.1,但没有配置速率0.01的计数器。如果特定程序没有采样计数器,则会从报告中删除该程序。如果报告没有程序,则根本不会发送报告。

在像Go's这样的大型部署中,典型的报告率将低于0.02(2%),每个系统的平均值每年大约一份周报,或者更少。透明遥测的一个好特性是因为越来越多的系统启用了它,每个系统报告的数据越来越少。

[更新,2023-02-24:10%的硬限制和2%的预期报告率基于opt-out遥测技术,安装了数百万台设备。该设计具有更改为opt-in,这将提高这些概率。]

当有报告要发送时,报告程序准备与Go类型相对应的JSON报告定义为:

类型报表结构{配置字符串周字符串LastWeek字符串X浮点64程序[]程序}键入ProgramReport结构{程序字符串版本字符串GoVersion字符串GOOS字符串GOARCH字符串计数器[]计数器堆栈[]计数器}类型计数器结构{名称字符串计数int64堆栈[]字符串}

这个报告配置字段列出配置版本用于生成报告,因此分析可以确定应用的采样率。

在间歇使用Go的系统上,报告程序可能无法运行一周结束后的几天或更长时间。这个报告字段标识本报告涵盖的周,通过给它的第一天日期格式格式。如果上次使用Go已经超过七天了,目前已有数周历史的本地报告将不会上传。这使得服务器可以在七天后对给定一周的遥测数据进行“结算”。

在任何数据收集系统中,量化有多少数据被丢弃是很重要的。(这就是为什么,例如,批准要合成的属性错过的配置文件事件功能,如_丢失外部代码.)在透明遥测中,如果系统使用一周但下周就不用了,系统将没有机会(随机决定)报告第一周的数据。间歇性使用的系统数量可能很低,不需要担心统计数据对结果有显著影响,但最好是测量一下,而不是猜测。这个上周现场报告在报告前一周报告系统上次收集计数器的时间。在常用系统上,上周总是比.在Go用法中长时间停顿后,上周将比,表明该系统从未考虑过报告计数器上周.如果大量报告存在多周的缺口,我们可以得出结论,上周的数据可能较少比之前估计的准确。同样,这通常不太可能发生,但可能会在之后发生年终假日等假期。最好有一个明确的信号数字不是可信的,而是令人费解的为什么它们看起来不同。这个上周字段也使其成为可能估计更长时间内的活动用户数量时间段,例如4周或52周,这可能有助于理解整体用法。

注意,不同程序的计数器集都上传在一起,例如,如果命令花费了令人惊讶的长时间要运行生成编译链接程序在同一记录中。还要注意,记录中没有持久标识符这样可以将一周的上传与另一周的上载链接起来。

服务器必须观察TCP会话中的源IP地址上传报告,但服务器不会将该地址与数据一起记录,可以通过检查报表服务器源代码来确认这一事实(服务器将像Go的其余部分一样是开源的)或参考一项明确的隐私政策,如用于Go模块后视镜的,取决于你更倾向于信任软件工程师还是律师。公司还可以运行自己的HTTP代理来屏蔽单个系统的IP地址并安排员工制度设置GOTELEMETRY公司发送至该代理人的地址。允许Go模块代理进行代理上传也是有意义的,以便现有的GOPROXY公司设置也适用于重定向上传并屏蔽系统的IP地址。

回想一下,本地二进制计数器文件存储在<用户>/go/遥测/本地/.上传报告时,会将上传的确切JSON写入<user>/go/遥测/上传/,以上传日期命名(2006年1月2日json).这两个目录(包括其命名)的目的是系统的整体操作尽可能透明。预计一份典型的报告将少于1000个计数器,JSON格式需要约50 kB。假设本地计数的计数器数量是本地计数的两倍比上传的多,即二进制格式的2000个计数器,这是另一个100 kB。无限期保存本地表单的存储成本低于100 kB/周或5 MB/年。每年上传一次或两次只会再增加100 kB/年。类似以下命令 清洁的 -遥测将删除所有这些。

上传前至少等待一周的隐私功能任何事情(在发送任何数据之前,给人们足够的时间选择退出)意味着诸如构建容器之类的临时机器将永远不会被计算在内。为了更好的隐私权而进行的权衡似乎值得失去对这些机器的可见性。

发布

每天,上传服务器都会进行前24小时的上传并更新在图形配置中定义的已发布图形。

它还发布了前24小时上传的完整原始JSON,在七个不同的数据集中对应七个不同可能的周(从星期日、星期一、星期二……开始)那天可能会有报道。例如,2023-01-18发布的文件将是:

2023-01-04周上传-2023-01-17.v1.报告2023-01-05周-发布-2023-01-17.v1.报告2023-01-06周-发布-2023-01-17.v1.报告2023-01-07周-发布-2023-01-17.v1.报告2023-01-08周-发布-2023-01-17.v1.报告2023-01-09周-发布-2023-01-17.v1.报告2023-01-10周上传-2023-01-17.v1.报告

由于采样,收集的上传量将相当小并且不会像活动安装的数量那样增长。估计每个上传的报告有50 kB,目标是每周约16000个报告,每周的报告总计只有800 MB(在该周的七个不同开始日进行拆分)。使用Brotli压缩应至少将占用空间减少10倍,每周最多80MB,或全年最多4GB的上传量。

选择退出

[更新,2023-02-24:设计已经更改为opt-in.出于历史目的,本节未经修改。]

此设计的明确目标是构建一个由于两个原因,默认启用是合理的。首先,绝大多数用户不会更改任何默认设置。在默认取消收款的系统中,opt-In比率往往非常低,结果偏向于超级用户他们很了解这个系统。第二,opt-in复选框的存在在我看来经常被用作收集的理由数据远远超出了需要。以尽可能少的理由建立选择退出系统为目标选择退出导致了这种最小的设计。此外,由于设计收集了固定数量的样本,选择更多的系统意味着从任何给定系统中收集的信息更少,减少对每个单独系统的隐私影响。

默认情况下启用系统需要适当的通知安装系统的用户。正如我们对默认模块代理和校验和数据库所做的那样,第一个Go发行版的发行说明中将张贴通知可以进行遥测并显示在下载链接旁边go.dev软件政府/国防部.

一些用户希望根据一般原则退出,无论系统有多小,这应该尽可能简单,类似于:

go env-w GOTELEMETRY=关闭

像所有人一样 环境价值 -w个命令,这将配置适用于所有已安装Go工具链的per-user设置,现在和未来:明天安装的新Go工具链也会尊重环境。

此外,一些Linux发行版可能希望安装期间提示用户或无条件禁用遥测。我们也应该让它变得容易。建议书#57179介绍go.env公司配置的Go工具链根目录中的文件per-toolchain设置。这将在Go 1.21中发货。要禁用遥测的Linux发行版可以包括go.env公司文件包含GOTELEMETRY=关闭.

opt-out系统中的另一个黑暗模式是报告在用户有机会退出之前提供信息。例如,有人告诉我一个流行的开发工具显示了遥测复选框,在安装过程中预先检查,为用户提供取消选中该框的机会。但此时,安装了几个屏幕,遥测已经发送,允许该工具背后的公司跟踪安装计数和选择退出率由于遥测突然停止,以及跟踪选择退出的系统的IP和MAC地址等详细信息。在该系统中,为了避免发送任何遥测数据,您必须设置一个环境变量然后从命令行调用安装程序。我在任何地方都找不到这个故事的具体证据,所以我不确定这个系统是否仍然这样运行或者曾经做过。无论如何,我强烈反对这种伎俩违反了退出决定的全部精神。

透明遥测在安装后至少等待一周在发送任何报告甚至获取集合配置之前。这应该有足够的时间跑步 环境价值 -w个选择退出。

总结

重复介绍性职位,透明遥测具有以下关键属性:

接下来的步骤

有关遥测技术的更多背景知识以及为什么它很重要,请参阅介绍性职位.有关更多用例,请参阅下一个帖子.

尽管这些帖子使用Go作为示例系统,使用透明遥测,我希望这些想法能够应用也可以被其他开源项目采用,在各自独立的收集系统中。

我发布这些内容是为了开始讨论围棋工具链如何采用遥测以某种形式帮助Go工具链开发人员做得更好关于Go开发和维护的决策。我已经编写了一个本地计数器收集的实现说服自己它可以做得足够便宜,但如今,该体系中没有任何其他部分以任何形式存在。我希望这个系统能在2023年建成。