使用马赛克图类型监控您的数据

导航至

本文由 InfluxData 实习生 Rashi BoseRose Parker 撰写。

InfluxDB 提供了 多种图表类型可视化,使用户可以轻松监控他们的数据。然而,大多数这些图表类型仅在您的数据可以用数字表示时才有用。如果您想跟踪应用程序的健康状况,或可视化 Kubernetes 中部署的 Pod 的状态随时间的变化,该怎么办?在这两种情况下,状态都是随时间跟踪的,使用几个离散值之一,并且无法在 x/y 图表上绘制。

在本文中,我们将带您了解我们在夏季实习期间创建马赛克图类型的过程。此图表非常适合跟踪随时间变化的状态。

问题是什么?

该项目始于我们的队友 Mark Rushakoff 提出的一个请求,他希望创建一个图表类型,以绘制给定数据类别的值随时间的变化。x 轴代表时间,y 轴代表不同数据系列的名称。最后,该图表将具有彩色框,每种颜色代表一个特定值,框的长度代表该值持续的时间。Mark 向我们提供了他想象中图表样子的草图(如下所示)。在图中,两组数据是 series1 和 series 2,可能值的列表是“foo”、“bar”和“quux”。在图中,每个值都分配了一种颜色,在我们的图表中,这些框将用该颜色填充。在与 Mark 面对面会面以确保项目仍然有价值并澄清一些细节后,我们就开始了。

为什么选择马赛克图?

我们要问自己的第一个问题是这种可视化应该被称为什么。最初的请求没有按名称提及图表类型,所以我们做了一些研究。我们考虑了甘特图、时间线图和状态图等图表类型,但最终确定为马赛克图。马赛克图可视化了两个或多个定性变量的数据——在我们的例子中是系列和值。我们选择这种图表类型是因为即使我们的图表比马赛克图更专业一些,我们也想确保它符合我们选择的任何图表的所有要求。此外,与其他可能性不同,马赛克图是一种足够常见的图表类型,人们应该能够识别出这个名称,并对它的用途有一些概念。

我们的第一次尝试

第一次尝试将我们的可视化类型添加到 Giraffe(我们使用 ReactJS 构建的图表可视化库)时,我们在理解所有不同的变量和文件方面遇到了困难,因此我们认为最简单的方法是尽可能接近地模仿最相似的现有图表类型(直方图和热图)。正如您可能想象的那样,这不是很成功。我们无法可视化任何内容,也不理解我们收到的错误。我们很快意识到,使用这种策略我们将一事无成,因此我们退后一步,试图在添加我们的代码之前弄清楚一切是如何协同工作的。

从头开始

我们做的第一件事是使用 Canvas 元素创建一个简单的马赛克图,以弄清楚我们需要什么形状的数据才能绘制我们的图表。在查看其他图表在 Giraffe 中显示的数据时,我们看到我们收到的数据将包含多个列,包括时间、值、系列名称以及可能的其他数据。每行数据都将有一个时间戳和该确切时间的系列值。对于我们的图表,我们将不得不按时间间隔组织这些数据。由于我们想显示一个值(例如“foo”)持续了多长时间,因此我们需要知道“foo”何时成为该值以及该值何时再次更改。因此,我们需要创建一个表,其中存储每个时间间隔的开始时间和结束时间以及值和系列名称。

在我们更好地了解我们的目标之后,我们开始在我们确信需要的地方添加少量代码,并将马赛克图构建得与其他图表类型相似。

在我们的第一个迭代中,我们在转换函数中硬编码了列名,以对应于我们创建的样本数据集。因此,我们的转换函数仅适用于我们创建的样本数据集。然后,一旦我们确定图表看起来像我们的目标图表,我们就删除了硬编码值,以便该图表可以与任何传入数据集一起使用。

创建虚拟数据集

我们遇到的首批问题之一是我们没有一组适用于我们图表的原始数据。正如我们之前提到的,所有其他图表类型都使用数值。因此,当时 Giraffe 中存在的虚拟数据集不适用于我们的目的。为了解决这个问题,我们编写了一个脚本,通过将数值放入带有字符串名称的范围内,将现有虚拟数据集的值转换为具有字符串值的数据集。这产生了一个数据集,其中每个系列都是一个 CPU,每个值间隔都映射到一个随时间变化的颜色。

可视化图表

在我们获得正确形状的数据后,我们终于可以开始绘制它了。我们首先创建了绘制图表本身的函数。我们设法使可视化显示出来,没有太多麻烦,但图表看起来与预期不同。每个系列的边界点不一致,并且有些颜色褪色了。

为了更好地理解问题所在,我们创建了第二个较小的原始数据集。使用此数据集,我们发现我们的初始转换函数错误地计算了给定系列的结束时间。我们最初将一个系列的结束时间分配为下一个系列的开始时间。然而,这是一种不正确的方法,因为每个系列都是彼此独立运行的,因此一个系列的结束时间发生在下一个系列的开始时间之前。为了解决这个问题,我们将每个系列的最后一个时间间隔的结束时间设置为图表中的最后一个时间戳。

接下来我们做的是绘制轴,因为我们认为这相对简单。我们错了。我们想将 x 轴设置为时间,y 轴设置为显示系列的名称。我们能够轻松地设置 x 轴,但设置 y 轴被证明更加复杂,因为之前没有图表将字符串用于其 y 轴。我们最终通过将系列名称存储在名为 yTicks 的变量中来完成此操作,该变量为非马赛克图层存储一个数字数组。为了实现这一点,我们必须为所有 y 轴函数创建一个特定于马赛克图的案例,包括查找适当边距宽度的函数,因为它们都依赖于 yTicks 是一个数字数组。

一旦我们让所有内容都正确可视化,我们就可以开始添加工具提示了。与 Giraffe 中的所有图表类型一样,马赛克图的工具提示充当图例。因此,我们需要做的第一件事是决定在我们的工具提示中包含哪些信息。对于我们的初次尝试,我们假设用户会发现仅查看他们当前悬停的框的信息(时间范围、系列、值)最有用。事实证明,显示当前时间戳的所有信息更简单,但我们过滤了这些信息,因此它仅显示指定时间戳和系列的信息。

在经过大约两周的第一次迭代之后,我们与 Mark 会面以获得关于我们图表的反馈。我们收到了积极的反馈,唯一的建议是将当前框的持续时间添加到工具提示中。在快速修复之后,我们准备好进入下一步。所有图表类型都首先在 Giraffe 中实现,然后导入到 InfluxDB 中。现在我们有了一个可工作的图表类型,我们可以(暂时)告别 Giraffe,并开始将马赛克图添加到 InfluxDB 中。

将图表类型添加到 InfluxDB

从我们在 Giraffe 中犯的错误中吸取教训后,我们用我们在 InfluxDB 的第一天探索了其他图表类型(特别是散点图)是如何实现的。我们很难理解我们真正需要在哪里引用我们的马赛克图,因为每个图表的实现方式都略有不同。因此,我们决定记下散点图被引用的函数以及原因,然后也慢慢地在这些地方添加马赛克图。在这个过程中,我们添加了很多 console.log 语句,并添加和删除了我们错误地认为我们需要的文件,但我们最终还是弄清楚了。经过一周的工作和构建代码库的思维模型后,我们只创建了两个新文件:一个马赛克图文件来可视化图表,以及一个马赛克选项文件来可视化图表的自定义选项。

回到绘图板

当我们最终在 InfluxDB 中渲染马赛克图时,我们发现我们的图表看起来与我们预期的不一样。我们决定查看查询返回的一小部分数据样本,并使用该数据逐步执行我们的转换函数,以验证该函数返回了正确的输出。在逐步执行代码时,我们发现我们从查询接收到的数据顺序与我们在 Giraffe 中测试的数据顺序不同。在我们在 Giraffe 中的测试数据集中,我们根据系列名称对所有数据进行了排序,因此 series1 的所有值都出现了,然后 series2 的所有值都出现了。在从查询返回的数据中,所有不同系列的值都散布在一起,但它们仍然都按时间顺序排列。为了解决这个问题,我们重写了转换函数,以稍微不同的方式组织数据。当我们按时间间隔收集数据时,我们还根据系列的名称对数据进行了排序。这为我们省去了更改数据处理方式的麻烦。

在解决该问题后,我们意识到我们还有另一个问题。图表现在看起来褪色了,类似于我们在 Giraffe 中将一个系列的结束时间分配为下一个系列的开始时间时的情况。仔细检查后,我们意识到正在使用的数据集为分配给每个时间戳的同一系列具有多个值,因此使用每个不同的值集重新绘制了图表。为了解决这个问题,我们在转换函数中添加了对同一系列的重复时间戳的检查。如果发生这种情况,我们将停止收集数据,并且仅渲染到那时为止收集的数据。

为了确保我们理解更改后传入数据会是什么样子,我们向 Mark 询问了他想要使用马赛克图渲染的数据集。我们使用该数据成功创建了一个马赛克图,并与 Mark 安排了一次会议向他展示。不幸的是,该图表不包含他期望它具有的一些功能,例如向工具提示或 y 轴添加更多数据的功能。在会议之后,我们探索了添加这些功能的可能性,但在再次咨询 Mark 和我们的导师后,我们都得出结论,我们没有足够的时间在截止日期之前添加它们。

总结

一旦我们在 UI 中有了一个可工作的图表,我们就开始为马赛克图设置后端。我们俩之前都没有从事过后端代码,因此我们与我们的导师配对,以更好地理解后端基础设施。我们定义了一个图表应该如何保存的模式,并遵循其他图表的示例来设置用于保存和导出马赛克图的其余代码。虽然我们无法在 UI 中包含 Mark 请求的更改,但我们更新了后端,以便将来允许进行这些更改。

然后我们做了一些最后的清理工作。我们重新设计了前端代码的几个部分,包括马赛克转换函数,以使 UX 流程更加直观,并禁用了任何无法正常运行的功能。在一切都完全符合我们期望之后,我们将马赛克图放在 功能标志 后面,以便可以对其进行正确测试,而不会影响当前用户的体验。该功能不会在我们的实习结束之前发布,因为我们在最后一周完成了它,但我们希望在下一个 InfluxDB 更新中看到马赛克图类型发布!

后续步骤

由于时间限制,我们没有机会添加一些功能。正如我们之前简要提到的,例如,Mark 对同时查看更多数据维度的能力很感兴趣,因此我们很希望能够添加为 y 轴选择多个字段的功能,并为每个系列创建分组,以及向工具提示添加更多字段并探索向每个时间间隔的颜色添加更多维度的可能性。

我们还希望在马赛克自定义选项中加入更多功能,例如在轴上添加前缀和后缀的功能。我们还希望允许用户选择工具提示是仅显示他们悬停的时间间隔的信息,还是显示当前时间戳的所有时间间隔的信息。

此外,默认情况下,每个系列的结束时间当前设置为整个数据集中的最后一个时间戳。我们希望探索将每个系列的结束时间设置为每个系列收集的最后一个时间戳的可能性,以确定哪种行为更受欢迎。

结论

与所有经验一样,如果让我们有机会重新做这个项目,我们会做一些不同的事情。最重要的是,我们会尝试更 регулярно 地与 Mark 会面。如果我们那样做了,我们觉得我们就能够确保我们理解应该如何渲染数据以及图表应该更快地包含哪些功能。

即使我们希望可以缩短达到目前状态所花费的时间,但每次遇到障碍时,我们都学到了宝贵的教训。首先,我们无法包含所有我们想要的功能,这给了我们一个机会去学习如何做出必要的妥协,以便按时交付软件。

我们遇到的另一个问题是我们缺乏使用复杂代码库的经验。通过我们在 Giraffe 中的工作(那里的代码只考虑数值),我们不得不认识到这种隐含的期望,并努力创建一个也可以渲染字符串的图表。这种经验帮助我们改进了识别复杂代码库中潜在假设的方法。

在我们整个项目过程中不断思考的所有问题中,有一个问题反复出现:我们如何量化我们正在做的工作?有些日子,我们会花费数小时仅仅试图理解预先存在的代码是如何工作的,而没有添加任何我们自己的代码。这导致我们感觉好像没有取得任何进展。然而,我们慢慢意识到,所有花在构建心智模型上的时间实际上是解决整体问题的一个非常重要的步骤。在我们的整个实习期间,我们清楚地认识到,阅读代码比编写代码更难。因此,花在理解他人代码上的时间与花在编写自己的代码上的时间同样有价值。

如果没有 InfluxData 这里所有人的支持,我们这个夏天就不会学到如此宝贵的教训。因此,我们要感谢所有帮助使这个项目和实习成为可能的人。我们特别要感谢我们的导师 Bucky 和 Deniz,以及监控团队的其他成员。我们非常高兴有机会认识他们并与他们一起工作;我们非常感谢他们所有的建议和指导。没有他们,我们早就崩溃了。

您可以在 这里玩转这个可视化,在 Storybook 中,您也可以在 这里亲自试用 Giraffe。 与往常一样,如果您有任何问题,请将它们发布到我们的 InfluxDB 社区 Slack论坛站点