本文强调了日志记录中上下文的重要性,包括“什么”、“何时”、“何地”、“为什么”和“谁”。建议使用UTC时间,包含事务ID,区分主要错误和副作用,并谨慎处理用户信息以符合隐私法规。还提供了捕获上下文的实用清单。
译自:Understanding Log Events: Why Context Is Key
作者:Phil Wilkins
编者按: 本文摘自 Manning 出版的书籍“日志记录最佳实践:云原生日志记录实用指南”的第 1 章。 下载完整书籍以完成第 1 章的阅读,了解最大化日志价值的一些最佳实践。
为什么日志记录中的上下文很重要
理解任何日志事件都需要上下文。 当我们开发和使用跟踪和调试日志时,上下文将在某种程度上为我们所知,也许是隐式的,因为代码中的位置将是上下文的一部分,或者正在运行的测试场景将是上下文。 但是,当我们进入生产环境时,上下文不太可能是隐式的,因此我们需要使其显式。
上下文的关键在于我们如何很好地回答以下问题:
- 什么 — 报告的是什么,错误还是仅是跟踪?
- 何时 — 日期和时间。 如果您使用某种形式的日志记录框架,这很容易。
- 何地 — 代码中的哪个位置以及基础架构中日志事件的来源在何处。
- 为什么 — 当涉及到信息级别及以上的日志级别时,我们为什么提供信息至关重要。 是否即将发生问题,或者我们是否正在报告您想要跟踪的内容,例如登录操作?
- 谁 — 谁触发了该操作? 谁的数据可能受到影响?
让我们更详细地探讨这些要点。
“什么”:识别日志事件代表的内容
日志事件的“什么”部分由事件中包含的日志级别来解决。 对于跟踪日志事件,当与“何地”结合使用时,记录事件的事实可能就足够了。 对于信息日志级别及以上,我们将提供一些额外的详细信息:信息记录是否用于审计目的? 发生了什么类型的错误? 警告与什么有关(例如存储空间不足)?
“什么”最好通过允许识别事务的详细信息来支持,包括事务的类型。 事务数据或代理(例如事务的唯一 ID,以便我们可以查找实际的事务数据)应该提供足够的洞察力; 例如,如果事务缺少对关联数据的引用,我们需要看到该值未设置。
“何时”:处理时区、时钟和全球系统
日志记录框架无需任何努力即可为您解决大部分问题,但可能会返回到服务器的系统时钟。 如果您正在寻找在应用夏令时的时区中运行的解决方案(因为有人正在查看时间戳并且它似乎已过期,因为他们正在应用夏令时,但日志不是)或解决方案是全球分布的,那么时区、时钟偏差等的影响可能会让您措手不及。 因此,您需要知道服务器所在的时区。 一种选择是将日志记录框架配置为在日志中包含时区,但更好的方法是将所有服务器与 UTC(协调世界时)对齐。
尝试将日志分析与用户错误报告对齐时,您需要清楚用户在哪个时区工作,以及错误报告是根据他们的时间还是系统时间记录的。
“何地”:代码位置和部署注意事项
命名代码位置需要了解如何处理代码。 当代码部署用于商业解决方案时,这一点尤其重要,在这种解决方案中,混淆和缩小工具可能会被使用,尤其是在基于脚本的解决方案(例如 JavaScript)上。 因此,依赖反射来获取代码位置的详细信息可能没有帮助。 尽管有一些工具,但一些混淆提供商将包括映射信息以识别原始代码,前提是提供正确的信息。
注意: 有关代码缩小和混淆的更多信息,请查看以下资源:
使用 ID 和版本来提高清晰度
应用程序通常是多线程的(例如 Java)或在 I/O 等待时进行上下文切换(Node.js),并且它们是单线程的。 上下文切换意味着我们不会一次处理一个事务,因此了解感兴趣的事件之前或之后的日志事件是否相关可能会变得具有挑战性。 这可以通过合并事务 ID 或会话 ID,或通过使用开放跟踪或开放遥测 ID 作为日志事件的一部分来克服。
一些日志记录框架将帮助您在其配置中捕获线程或进程 ID。 例如,在 Fluentd 中,我们可以使用日志文件输出中的 WorkerId。
“何地”也可能受到软件版本的影响。 我们可以同时在生产中使用同一逻辑的多个版本来支持以下活动:
- 运营 A/B 部署以帮助评估一种实现是否改善了用户交互。
- 以高可用性运行,因此软件更新需要进行滚动更新。
以下是另一种看待它的方式:您发现本书中有一张图片渲染得不太好。 您联系 Manning。 为了帮助您,我们需要知道哪个图表有故障。 如果这个问题以前见过并且已在新版本的书中修复了怎么办? 这并不是说每个日志事件都需要发布版本信息的每个方面,但我们需要使其易于提供足够的信息。
也许当我们记录错误或更糟的情况时,此信息会写入日志中。 这是注入到日志事件中可能有所帮助的领域。 如果一个日志事件被识别为重发软件中的异常情况,例如错误,那么 Fluentd 可以检索 正在运行的软件的版本并将其注入到日志中 以供将来参考。
“为什么”:理解日志事件背后的目的
这归结为为什么会发生事件 — 它是错误还是仅显示代码位置(跟踪)或应用程序当前状态(调试)的信号? 当我们转移到更高的级别(在我们分类中的警告、错误和致命)时,“为什么”变得越来越重要,并且仅从 日志级别 中不太明显。 信息级别的日志事件可能是审计或系统当前状态的定期快照,无论情况好坏 — 例如,记录消息队列的深度。 但是,日志事件使用者需要能够理解为什么会生成该事件。 经过一番思考,这个问题很容易解决。
一个简单的属性,例如“当前状态”或“审计操作”,可以与共享数据一起包含在内。 实际上,在许多情况下,我们正在为日志事件提供二级分类。 鉴于我们提供了额外的元数据,只要我们在开发组织中保持一致,我们就可以构建它。
区分主要错误和副作用
当涉及到报告警告和错误时,“为什么”会回到“什么”触发了警告或错误。 它是主要错误,还是发生了先前问题的副产品? 尝试指示错误是原因还是结果是困难的。 如果我们可以确定,我们应该清楚; 如果不是,我们可以向日志事件使用者提供一些关于可能性的提示。 编码此类信息可能很复杂且难以测试。 但它很容易链接到错误代码并提供确认原因或效果的步骤。
我们使用事件生成的记录需要清楚地提供信息以帮助执行诊断,不仅在操作上,而且在代码中是否可能需要某些东西,例如更具防御性的代码或更好的数据验证。 由于解决方案现在处于不愉快的路径上,我们不应该害怕慷慨地提供信息 — 只要它不会引发敏感性问题,我们稍后会看到这一点。 对于错误处理路径,我们处于不应考虑性能的位置,因为代码库的这一部分应该只不经常执行。 通常,信息太少比太多要糟糕得多。
“谁”:平衡身份、隐私和合规性
记录“谁”可能很棘手。 记录可识别个人的信息将使我们的 日志处理 受制于立法、合同和商业要求。
重要的是要考虑何时需要“谁”,以及我们是否可以安全地使用其他数据作为真实身份的代理。 例如,也许“谁”仅在登录会话期间相关,因此我们只需要携带会话 ID 并使用它即可。 如果我们需要将会话中的操作归因于特定个人,我们会以安全的方式单独记录它。 该会话 ID 可以同样是事务或订单 ID,依此类推。
当记录不需要特定个人的事件(例如登录失败或应用程序交互)时,我们可能仍然需要“谁”的值,例如始发 IP 地址。 例如,单个服务器 ping 可能是无害的(活动服务报告可能只是这样做),但是来自同一位置的真正快速重复发生是不好的。 但是,拥有该 IP 意味着可以确定是同一系统在调用,因此可以阻止谁。
“谁”在行动中的上下文
与客户的 DevOps 团队合作时,我们发现客户的安全团队雇用了一个第三方组织来定期扫描他们所有面向 Internet 的服务器。 我们弄清楚了发生了什么,因为我们会看到我们的 API 网关服务器报告来自几个 IP 之一的非法请求,这些请求以一定的频率发生。 一旦我们确定了模式和记录的详细信息(如 IP 来源、时间和 HTTP 请求),我们就会向安全团队提出我们的怀疑,安全团队确认使用了第三方。
日志事件中的系统与人为参与者
不要忘记“谁”可能是系统或应用程序组件。 例如,当处理工资单时,该活动由计划程序触发。 因此,知道哪个计划或计划程序触发了该过程很有帮助。
捕获上下文的实用清单
解决什么、何时、何地、为什么和谁可能有点抽象。 就我个人而言,我尝试使用以下问题来解决它:
- 事件来自代码中的哪个位置?
- 您的代码在生产中是否有多个版本的可能性? 如果是这样,那么哪个版本变得重要。
- 如何处理交易? 如果需要在以后修复数据中的问题的影响,这一点尤其重要。
- 哪个服务器、进程或线程遇到了问题? 如果问题与基础架构相关,您需要知道它与哪个服务器、虚拟机 (VM) 或容器相关。
- 错误的原因是否可识别(除以零的错误可识别错误中涉及的值)? 错误日志 是否可以追溯到代码中的位置? 至少,表达错误的性质,如果可行,表达相关的数据值(除以零的错误 — 说明什么被零除以及涉及的值)。
- 什么数据值导致执行进入特定路径?
常见问题解答
1. 如何在不给事件响应造成混乱的情况下,在日志记录中处理时区和全球系统?
时区会导致混乱,因为夏令时转换、服务器位置差异以及用户与系统时间不匹配。 最佳实践是将所有服务器与 UTC 对齐,并清楚地注明时间戳是用户报告的还是系统生成的。
2. 如何区分日志中的主要错误和副作用,以避免在事件期间追逐错误的问题?
通常不清楚错误是原因还是副作用,因此日志应包含提示或参考(如错误代码)以进行澄清。 提供丰富的上下文,如关联 ID、分类属性和额外细节(不暴露敏感数据)比信息太少要好。
3. 如何在平衡隐私要求和运营需求的同时,处理日志中的“谁”上下文?
- 仅在必要时捕获身份 — 使用会话 ID 或代理而不是完整的用户详细信息。
- 对于操作,“谁”可能是系统或自动化过程。
- 如果存储个人标识符,日志将受到合规性要求的约束,因此请使用所需的最少数据。
本文摘自 Manning 出版的书籍“日志记录最佳实践”。 即使有健康检查,您也需要知道检查可能失败的原因。 这就是 Fluent Bit 的错误代码的用武之地,它提供了一个窗口,可以了解影响您的输入和输出的确切问题。 您可以在此处完整阅读“日志记录最佳实践”。