虽然像 Sentry 这样的解决方案通常比保存在文件中的普通文本输出更加强大,但日志永 远不会消失。将一些信息写入标准输出或文件是应用可以做的最简单的事情之一,这一点不 应该被低估。有这样一种风险:raven 发送到 Sentry 的消息将无法送达。网络可能会出现故 障。Sentry 的存储可能会耗尽或者可能无法处理传入的负载。你的应用可能会在发送任何消 息之前崩溃(例如出现分段错误)。这些只是几种可能的情况。不太可能的是,你的应用将 无法记录将被写入文件系统的消息。这仍然是可能的,但我们应该诚实一点。如果你遇到这 种日志记录出现故障的情况,那么你很可能有许多比缺失的日志消息更重要的问题。
记住,日志不仅是关于错误的。许多开发人员曾经只将日志作为数据来源,这些数据 在调试问题时非常有用,也可以用于执行某种取证。当然,很少有人尝试用日志来生成应 用指标或者做一些统计分析。但日志的用途可能要更多。它甚至可以作为产品实现的核心。 Amazon 有一篇文章讲到了用日志构建产品的一个很好的例子,里面介绍了一个实时竞价服 务的示例架构,其中一切都围绕着访问日志收集和处理。参见 https://aws.amazon.com/blogs/ aws/real-time-ad-impression-bids-using-dynamodb/。
1.基本的低级日志实践
十二要素应用方法中说到,应该把日志当作事件流。因此日志文件不是日志本身,而只是 一种输出格式。日志是流,意味着它表示按时间排序的事件。它的原始格式通常为文本格式,每 行代表一个事件,但在某些情况下可能跨越多行。这是与运行错误相关的任何回溯的典型情况。
根据十二要素应用方法论,应用不应该知道日志的存储格式。也就是说,不应该由应 用代码来维护对文件的写入或日志的转储(rotation)和保留。这些是应用运行环境的责任。
这可能会造成一些困扰,因为许多框架都提供了用于管理日志文件的函数和类以及转储、 压缩和保留的实用程序。这些工具是很吸引人的,因为一切都可以包含在应用代码库中, 但实际上这是一种应该避免的反模式。
处理日志的最佳约定可以总结为几条规则:
● 应用应该总是将未缓冲的日志写入标准输出(stdout)。
● 执行环境应该负责收集日志并将其发送给最终目标。 对于上面提到的执行环境,其主要部分通常是某种进程管理工具。常见的 Python 解决
方案(例如 Supervisor 或 Circus)是第一批负责处理日志收集和发送的。如果日志要保存 在本地文件系统中,那么只将日志写入实际的日志文件中。
Supervisor 和 Circus 都能够处理被管理进程的日志转储和保留,但是你应该考虑这是 否是你想要的技术路线。成功的运营主要在于简单性和一致性。你自己的应用日志可能不 是你唯一想要处理和归档的日志。如果你使用 Apache 或 Nginx 作为反向代理,那么你可能 想要收集其访问日志。你可能还希望存储并处理缓存和数据库的日志。如果你正运行某个 流行的 Linux 发行版,那么很可能每个服务自己的日志文件都被一个叫作 logrotate 的 流行实用程序处理(转储、压缩等)。我强烈建议忘记 Supervisor 和 Circus 的日志转储功能, 以便与其他系统服务保持一致。logrotate 的可配置性更高,也支持压缩。
logrotate 与 Supervisor/Circus
在同时使用 logrotate 与 Supervisor 或 Circus 时,有一 件很重要的事情需要知道。日志转储将始终发生,而 Supervisor 进程对于转储的日志仍有开放的描述符。如果 你不采取适当的应对措施,那么新的事件将被写入已经 被 logrotate 删除的文件描述符。作为结果,文件系统 中无法继续保存新的内容。这个问题的解决方法相当简 单。用 copytruncate 选项配置 logrotate,可以处 理 Supervisor 或 Circus 所管理进程的日志文件。它不会在 转储后移动日志文件,而是会复制日志文件并将原始文 件在原位缩小到大小为零。这种方法不会使任何现有的 文件描述符失效,已经运行的进程也可以不间断地向日志 文件写入。Supervisor 还可以接受 SIGUSR2 信号,使其重 新打开所有的文件描述符。它可以作为 postrotate 脚 本包含在 logrotate 配置中。第二种方法在 I/O 操作方 面更加经济,但可靠性更低,也更难以维护。