您编写的应用程序执行大量代码,这些代码本质上是不可见的。那么您如何知道
- 代码是否正常工作?
- 它是否运行良好?
- 谁在使用它,以及如何使用?
可观测性是指查看可以告诉您代码正在做什么的数据的能力。在这种情况下,主要问题领域是分布式系统中的服务器代码。并不是说可观测性对于客户端应用程序不重要,而是客户端往往不是用 Python 编写的。并不是说可观测性对于数据科学无关紧要,而是数据科学中的可观测性工具(主要是 Juptyter 和快速反馈)是不同的。
为什么可观测性很重要
那么,为什么可观测性很重要?可观测性是软件开发生命周期 (SDLC) 的重要组成部分。
发布应用程序不是结束,而是一个新周期的开始。在该周期中,第一阶段是确认新版本运行良好。否则,可能需要回滚。哪些功能运行良好?哪些功能有细微的错误?您需要了解正在发生的事情,才能知道下一步该做什么。事情会以奇怪的方式失败。无论是自然灾害、底层基础设施的推出,还是应用程序进入奇怪的状态,事情都可能随时因任何原因而失败。
在标准 SDLC 之外,您需要知道一切仍在运行。如果它没有运行,那么必须有一种方法知道它是如何失败的。
反馈
可观测性的第一部分是获得反馈。当代码提供有关其正在做什么的信息时,反馈可以在许多方面提供帮助。在暂存或测试环境中,反馈有助于更快地发现问题,更重要的是,对问题进行分类。这改进了验证步骤周围的工具和沟通。
在进行 Canary 部署或更改功能标志时,反馈也很重要,它可以让您知道是继续、等待更长时间还是回滚。
监控
有时您怀疑出了问题。也许依赖的服务存在问题,或者社交媒体正在向您猛烈提问关于您的网站。也许相关系统存在复杂的运行,您想确保您的系统能够很好地处理它。在这些情况下,您需要将来自可观测性系统的数据聚合到仪表板中。
在编写应用程序时,这些仪表板需要成为设计标准的一部分。它们只有在您的应用程序与它们共享数据时才能显示数据。
警报
一次连续观看仪表板超过 15 分钟就像看着油漆变干。不应该让任何人遭受这种折磨。对于这项任务,我们有警报系统。警报系统将可观测性数据与预期数据进行比较,并在不匹配时发送通知。深入研究事件管理超出了本文的范围。但是,可观测的应用程序在以下两个方面对警报友好
- 它们产生足够的数据,并且质量足够高,可以发送高质量的警报。
- 警报具有足够的数据,或者接收者可以轻松获取数据,以帮助分类来源。
高质量警报具有三个属性
- 低误报:如果有警报,则肯定存在问题。
- 低漏报:当出现问题时,会触发警报。
- 及时:快速发送警报,以最大限度地减少恢复时间。
这三个属性之间存在三向冲突。您可以通过提高检测阈值来减少误报,但代价是增加漏报。您可以通过降低检测阈值来减少漏报,但代价是增加误报。您可以通过收集更多数据来减少误报和漏报,但代价是降低及时性。
改进所有三个参数更难。这就是可观测性数据的质量发挥作用的地方。更高质量的数据可以减少所有三个参数。
日志记录
有些人喜欢嘲笑基于打印的调试。但是在大多数软件都在非本地 PC 上运行的世界中,打印调试是您唯一能做的。日志记录是打印调试的形式化。Python 日志记录库,尽管存在各种缺陷,但允许标准化日志记录。最重要的是,这意味着您可以从库中进行日志记录。
应用程序负责配置哪些日志的去向。具有讽刺意味的是,经过多年应用程序在字面上负责配置之后,这种情况越来越不真实。现代应用程序在现代容器编排环境中记录到标准错误和标准输出,并信任编排系统来正确管理日志。
但是,您不应该在库中或几乎任何地方依赖它。如果您想让操作员知道发生了什么,请使用日志记录,而不是打印。
日志记录级别
日志记录最重要的功能之一是日志记录级别。日志记录级别允许您适当地过滤和路由日志。但这只有在日志记录级别一致的情况下才能完成。至少,您应该使它们在您的应用程序中保持一致。
在一些帮助下,选择不兼容语义的库可以通过在应用程序级别进行适当的配置来追溯修复。通过使用 Python 中最重要的通用约定来做到这一点:使用getLogger(__name-_)
。
大多数合理的库都遵循此约定。过滤器可以在发出日志对象之前就地修改它们。您可以将过滤器附加到处理程序,该过滤器将根据名称修改消息以具有适当的级别。
import logging
LOGGER=logging.getLogger(__name__)
考虑到这一点,您现在必须实际指定日志记录级别的语义。有很多选择,但以下是我最喜欢的
- 错误:这会发送立即警报。应用程序处于需要操作员注意的状态。(这意味着 Critical 和 Error 被折叠。)
- 警告:我喜欢称它们为“工作时间警报”。应该在一天之内有人查看。
- 信息:这在正常流程中发出。它的目的是帮助人们了解应用程序在做什么,如果他们已经怀疑存在问题。
- 调试:默认情况下,这不会在生产环境中发出。它可能会或可能不会在开发或暂存中发出,如果需要更多信息,可以在生产中显式启用它。
在任何情况下,您都不应在日志中包含 PII(个人身份信息)或密码。无论级别如何,这都是正确的。级别会更改,调试级别会激活等等。日志聚合系统很少是 PII 安全的,尤其是在不断发展的 PII 法规(HIPAA、GDPR 等)的情况下。
日志聚合
现代系统几乎总是分布式的。冗余、扩展,有时是管辖需求意味着水平分布。微服务意味着垂直分布。登录到每台机器来检查日志不再现实。出于适当的控制原因,这通常是一个坏主意:允许开发人员登录到机器会赋予他们过多的权限。
所有日志都应发送到聚合器。有商业产品,您可以配置 ELK 堆栈,或者您可以使用任何其他数据库(SQL 或 no-SQL)。作为一种非常低技术的解决方案,您可以将日志写入文件并将它们发送到对象存储。有太多的解决方案可以解释,但最重要的是选择一个并聚合所有内容。
日志查询
将所有内容记录到一个地方后,日志太多了。特定的聚合器定义了如何编写查询,但无论是通过存储进行 grepping 还是编写 NoSQL 查询,日志查询以匹配源和详细信息都很有用。
指标抓取
指标抓取是一种服务器拉取模型。指标服务器定期连接到应用程序并拉取指标。
至少,这意味着服务器需要所有相关应用程序服务器的连接和发现。
Prometheus 作为标准
如果您使用的指标聚合器是 Prometheus,则 Prometheus 格式作为端点很有用。但即使不是,它也很有用!几乎所有系统都包含 Prometheus 端点的兼容性 shim。
使用客户端 Python 库向您的应用程序添加 Prometheus shim 允许大多数指标聚合器抓取它。 Prometheus 期望在发现服务器后找到一个指标端点。这通常是应用程序路由的一部分,通常位于/metrics
。无论 Web 应用程序的平台如何,如果您可以在给定的端点以自定义内容类型提供自定义字节流,则可以被 Prometheus 抓取。
对于最流行的框架,还有一个中间件插件或类似的东西,可以自动收集一些指标,例如延迟和错误率。这通常是不够的。您希望收集自定义应用程序数据:例如,每个端点的缓存命中/未命中率、数据库延迟等。
使用计数器
Prometheus 支持多种数据类型。一种重要而微妙的类型是计数器。计数器总是前进——只有一个注意事项。
当应用程序重置时,计数器将返回零。通过将计数器“创建时间”作为元数据发送来管理计数器中的这些“时期”。 Prometheus 将知道不要比较来自两个不同时期的计数器。
使用仪表
仪表要简单得多:它们测量瞬时值。将它们用于上下波动的测量:例如,总分配的内存、缓存大小等。
使用枚举
枚举对于应用程序的整体状态很有用,尽管它们可以更精细地收集。例如,如果您使用的是功能门控框架,则可能需要将可以具有多种状态的功能(例如,正在使用、已禁用、影子)作为枚举。
分析
分析与指标的不同之处在于它们对应于连贯的事件。例如,在网络服务器中,一个事件是一个外部请求及其产生的工作。特别是,分析事件必须等到事件完成后才能发送。
一个事件包含特定的测量值:延迟、数量以及可能与对其他服务发出的请求相关的详细信息,等等。
结构化日志记录
当前一个可能的选择是结构化日志记录。发送事件只是发送一个带有正确格式的有效负载的日志。可以从日志聚合器查询此数据,对其进行解析并将其提取到适当的系统中,以便可以了解它。
错误跟踪
您可以使用日志跟踪错误,也可以使用分析跟踪错误。但是,专门的错误系统是值得的。针对错误优化的系统可以发送更多数据,因为错误很少见。它可以发送正确的数据,并且可以对数据做聪明的事情。Python 中的错误跟踪系统通常会挂钩到通用异常处理程序,收集数据并将其发送到专用错误聚合器。
使用 Sentry
在许多情况下,自己运行 Sentry 是正确的选择。当发生错误时,就意味着出现了问题。可靠地移除敏感数据是不可能的,因为这些情况恰恰是敏感数据可能最终出现在不该出现的地方。
通常,它不会造成很大的负载:异常应该很少见。最后,这并不是一个需要高质量、高可靠性备份的系统。昨天的错误已经修复了(希望如此),如果还没有修复——你会知道的!
快速、安全、可重复:三者都要
可观察的系统开发速度更快,因为它们能给你反馈。它们运行起来更安全,因为当出现问题时,它们会让你更快地知道。最后,可观察性有助于围绕它构建可重复的流程,因为它存在一个反馈循环。可观察性让你了解你的应用程序。而了解情况就成功了一半。
前期投资会有回报
构建所有的可观察性层是很困难的。而且通常感觉像是浪费工作,或者至少像是“锦上添花,但并不紧急”。
你能以后再构建它吗?也许可以,但你不应该。正确地构建它能让你在开发的各个阶段都加快速度:测试、监控,甚至包括新员工的入职。在科技行业这种人员流动性很大的行业中,仅仅减少新员工入职的负担就值得了。
事实上,可观察性很重要,所以在流程的早期就编写它,并在整个过程中维护它。反过来,它将帮助你维护你的软件。
评论已关闭。