Skip to content

【机翻转载】【2】最佳 Nodejs 日志记录工具的建议

原文链接

目录


欢迎回到我们正在进行的关于 Node.js 参考架构的系列。第 1 部分介绍了 Node.js 参考体系结构的全部内容。在本文中,我们将深入研究 Node.js 中的日志记录。了解用于记录 Node.js 应用程序和部署的工具是确保它们在生产中正常运行的关键步骤。

首先,我们将讨论为什么 Node.js 参考架构 | Github推荐 Pino 作为登录 Node.js 应用程序的绝佳工具。然后,我们将根据 IBM 团队将日志记录集成到 Node.js 应用程序中的经验来演练一个示例。

注意:与我们所有的 Node.js 参考架构建议一样,我们专注于定义一组良好且可靠的默认选择。您的团队可能会根据最适合您的用例的建议偏离其中一些建议。

什么是好的日志记录工具?

在决定推荐哪种工具之前,我们必须定义一个好的日志记录工具的要求。我们很快达成一致,我们选择的记录器应该是:

  • 能够支持结构化日志记录/JSON 格式。
  • 简单 - 鉴于现代日志存储库和查看器的可用性,我们希望选择一个简单的记录器,并建议在 Node.js 进程本身之外执行任何复杂的过滤/处理。
  • 积极维护 - 这似乎是显而易见的,但在这个过程中,我们意识到一个团队正在使用不再维护的记录器。这当然是一个提醒,要密切关注你的依赖关系!

Node.js 日志记录的最佳实践

我们的团队还就以下准则达成了一致,即在 Node.js 中记录错误时的良好做法:

  • 将日志发送到标准输出 (stdout)。使用日志聚合工具跨进程、容器和应用程序收集日志。所有日志处理都应在单独的进程中完成。
  • 规划日志数据的最终安息之地。七天后,将日志数据迁移到成本较低的存储。手动检索可能是可以的。
  • 向应用程序添加允许 logger.level 通过环境变量设置的代码,以便在容器环境中轻松更改。
  • 使用密文选项 | Github确保不记录敏感数据。
  • 将警告、错误和致命级别的使用限制为必须始终记录的信息。
  • 将信息级别的使用限制为重要信息,这些信息始终可以记录下来,而不会带来大量开销。出于安全审核目的,建议在此级别使用客户使用的 REST 响应日志(状态/持续时间/URL)。
  • 不要抛出并捕获多个错误 - 抛出一次并在最高级别捕获/记录。

为什么 Pino 是最好的记录器

根据我们商定的要求和准则以及团队成员所经历的记录器,有几个不错的选择。我们决定使用 Pino 的原因如下:

  • 大规模部署成功:我们的一些生产 Node.js 部署已经使用了 Pino,因此我们已经看到它在实际的大规模部署中取得了成功。
  • 样品和入门套件支持:我们已经在许多样品和入门套件中使用了 Pino,因此我们对其他团队使用它充满信心。
  • 易用性:在支持结构化/JSON 日志记录的选项中,Pino 是开发人员最容易上手的。尽管其他选项具有更全面的功能,但我们认为日志记录选项应该很简单,因此我们选择了 Pino 而不是其他工具。

日志记录指南的实际实现

所有这些建议如何转化为实际实施?虽然参考架构坚持简明的建议,但我们希望扩展一下我们决策背后的“原因”,并提供 Node.js 使用关键概念的真实示例。本部分详细讨论一个团队对这些日志记录原则的实现。

在这一点上,Jim 将分享他的团队在 IBM 将日志记录集成到 Node.js 应用程序中的经验的详细信息。希望您能看到这种观点(以及其他观点)如何塑造了我们的指导和先前的建议。

结构化日志记录

一个好的日志记录策略可确保日志的结构为回车符分隔的 JSON 块;这使得现代日志存储库和查看器(如 LogDNA、Kibana 和 Loggly)可以轻松地对 JSON 块中的各种关键字进行排序和过滤。尽管现代日志记录存储库会在内部将基于字符串的日志转换为 JSON 日志,但提供 JSON 日志允许应用程序定义自己的自定义关键字进行排序或搜索。容器启动时的 shell 脚本不使用 JSON 日志,但在同一容器中的 Node.js 进程在启动后使用 JSON 日志记录仍然很常见。

开发团队有时会对此进行反击,因为在日志文件中查看日志时会变得更难阅读。我们使用的解决方法是使用日志格式化方法对特定字段进行排序,使其顺序与非 JSON 格式相同。例如,序列化的 JSON 块中的第一个是时间戳、级别,然后是消息,其余关键字按任意顺序跟随它们。

现代日志记录存储库处理应用程序生成的日志的存储、筛选和可视化。图 1 显示了查看 IBM Log Analysis 服务已馈送到 LogDNA 的日志的示例。(此处可以看到的 graphql_calltransaction_id 字段将在有关使用日志记录格式化程序和线程局部变量的部分中讨论。

图 1:在 LogDNA 中查看日志

图 1:在 LogDNA 中查看日志。

静态和动态日志级管理

在早期,您应该定义一组应用程序开发人员应使用的标准日志级别。这些可能因你选择的日志记录工具而异,但明确定义它们有助于确保整个团队的一致使用。

团队应查看应用程序将生成错误日志的所有位置,以确认是否记录了调试该错误路径所需的不同变量。这样做有助于避免以后需要使用更详细的日志记录重新创建问题。您的目标应该是“首次故障数据捕获”,这样您就不必重新创建问题来调试客户问题。如果需要其他数据来调试客户问题,则应创建一个单独的问题来跟踪添加其他日志记录,以涵盖该错误路径中所需的其他数据。

如前所述,初始应用程序日志记录级别由环境变量设置确定。但是,我们发现了某些“损坏状态”情况,我们希望在不重新部署或重新启动应用程序的情况下将日志记录级别更改为更详细。当我们开始偶尔出现数据库或队列服务错误时,就会发生这种情况。我们使用了一个未公开的隐藏 REST 端点,但我们仍然可以使用管理路由工具(如 kubectl proxy )调用它。REST 端点可以与日志记录库通信,以动态更改日志记录级别。

记录格式化程序和线程局部变量

日志记录格式化程序是可自定义的方法,允许应用程序将数据添加或转换为输出的 JSON 日志。使用格式化程序将特定的线程局部变量导出到日志中是一种强大的技术,可以获取支持团队在所有日志中所需的有用数据。

例如,您的支持团队可能希望能够将日志筛选为特定的失败 REST 调用。在 REST 处理的早期(即第一个快速中间件层之一)生成 REST 调用并将其存储为特定的线程局部变量,允许任何关注该 REST 调用 trace-guid 中日志的人共享该 trace-guid 变量。

提供的客户错误消息 trace-guid 使您的支持团队能够轻松地通过筛选来帮助 trace-guid 客户,以准确了解客户在做什么。否则,您的支持团队必须通过日志查找具有相似时间戳的错误,这很耗时。还有其他方法可以获取跨事务信息,包括分布式跟踪,但在日志中包含此类信息通常仍然很有用。

虽然 Node.js 没有像 Python 和 Ruby 那样明显的线程局部变量支持,但它确实通过其 async-hooks 库公开了该功能。具体而言,在最新的 Node.js 版本中,您可以访问 AsyncLocalStorage。这些 API 仍处于实验阶段,但随着时间的推移相对稳定。否则,如果您仍在使用较旧的 Node.js 版本,则可以使用 cls 挂钩库,甚至可以回到 Node.js 8 来获得等效的功能。

在通用身份验证验证层中设置的良好线程局部变量包括 user-guid (而不是用于 GDPR 合规性的电子邮件地址) organization-id 以及用户是否是应用程序管理员。这些允许安全审计员跟踪特定用户或组织执行的操作,并验证您自己的管理员没有采取不适当的操作。

如果您的应用程序使用 GraphQL,您会注意到,由于所有 GraphQL 调用共享相同的入口端点,因此传统的 REST 跟踪日志不是很有帮助,因为它们不会指示调用了哪个 GraphQL 调用。我们更新了 GraphQL 入口方法,以在线程局部变量中设置方法名称,以便我们可以将该名称包含在 REST 跟踪日志中。

记录真实 IP

在大多数基于 Kubernetes 的部署中,应用程序看到的源 IP 地址只是入口路由器的 IP 地址,而不是来自调用方的 IP 地址。如果您在 Kubernetes 集群前面使用安全防火墙(例如 IBM Cloud 中提供的 Cloudflare 服务),则调用方的 IP 地址将存储在标头 X-Forwarded-For 中。然后,应用程序终结点处理程序可以提取并记录标头,而不是 REST 请求的 IP 地址。如果您没有使用这样的前端,则可能需要专门配置负载均衡器和入口,以确保将公共 IP 带入 Kubernetes 入口,然后进入您的应用程序。

启动时的配置日志记录

在启动时转储应用程序配置设置可以帮助开发人员快速识别和理解配置问题,而不是猜测配置“应该是什么”。这提供了关键信息,可以节省调查问题的时间,同时限制后续日志中出现敏感信息的可能性。

但是,这样做有时会暴露通常应保密的密码或 apikey 等值。一个常见的例子是,开发人员在应用程序启动时添加一个 printenv 以转储所有环境变量,并无意中暴露了作为环境变量传入的数据库密码。

convict 这样的 Node.js 库可以轻松定义如何导入配置设置。它们允许应用确定是否应从挂载的配置文件和/或环境变量导入设置。它们还确定配置中的哪些元素是可选的,默认设置应该是什么,以及字段的值是否应被视为敏感值(即值是密码或 apikey)。只要开发者在配置定义中将密码和 apikey 字段标记为敏感字段,那么配置对象就可以在启动时安全记录;敏感字段值将转换为星号。

由于我们提到了敏感信息,因此考虑应用程序正在收集的信息并在记录该信息时使用密文删除敏感字段也很重要。

What’s next? 下一步是什么?

我们希望本文能让您更深入地了解 Node.js 参考架构的日志记录部分 | Github中的建议和指导,并深入了解团队的一些经验和我们发现有用的内容。

本系列的第 3 部分介绍了 Node.js 中的代码一致性。请查看 GitHub 项目,探索未来文章中可能涉及的部分。

要了解有关红帽在 Node.js 方面的最新动态的更多信息,请查看我们的 Node.js 主题页面。

Copyright © 2022 田园幻想乡 浙ICP备2021038778号-1