最新版本的 AWS Powertools for Lambda 让使用自定义功能扩展 Logger 变得更加轻松:
Logger 类现在更具可扩展性
您现在可以覆盖 Logger 方法 createAndPopulateLogItem、printLog 和 processLogItem,这些方法以前是私有的。这允许您扩展记录器并添加新功能,例如实现您自己的消息缓冲区。
发布 v2.12.0
我正在使用这个新的扩展性来实现一个有用的功能:日志缓冲和刷新。这个想法很简单:在生产环境中,我们通常只记录重要信息,例如警告和错误,因为日志空间可能会变得昂贵并且会产生噪音。但是,当发生错误时,我们希望获得所有可能的信息:分散在函数中的所有调试和信息日志都应该可用。但它们并不是因为我们将日志级别设置得太低。
如果我们在内部收集所有这些调试和信息日志,并且如果发生错误等重要事件,我们将它们打印到控制台怎么办?我把日志分为两类:低级日志和高级日志。如果我们配置的日志级别是 WARN,则 DEBUG 或 INFO 日志将是低级别日志,而 ERROR 将是高级日志。
现在,当我们打印低级日志时,我们不会像现在这样丢弃它,而是将日志缓冲在内部列表中。一旦我们有了高级日志,我们就会将所有缓冲的日志刷新到控制台。
为了添加此功能,我们创建一个新类,该类从 Powertools 扩展 Logger 类并重写 processLogItem()。这是由不同的日志方法(如 logger.debug())调用的中心方法。如果日志项处于正确的级别,原始实现会将日志项打印到控制台。通过重写此方法,我们可以根据日志级别添加缓冲和刷新日志的特殊逻辑。
import { LogItem, Logger as PowertoolsLogger } from '@aws-lambda-powertools/logger'; import type { LogItemExtraInput, LogItemMessage } from '@aws-lambda-powertools/logger/types'; export class Logger extends PowertoolsLogger { #buffer: Record<string, Array<[number, string]>> = {}; get buffer(): Record<string, Array<[number, string]>> { return this.#buffer; } protected override processLogItem(logLevel: number, input: LogItemMessage, extraInput: LogItemExtraInput): void { const xRayTraceId = this['envVarsService'].getXrayTraceId() as string; // Flush buffer when log level is higher than the configured log level if (logLevel > this.level && xRayTraceId) { const buffer = this.#buffer[xRayTraceId] ?? []; // Print all log items in the buffer if (buffer.length) this.info(`Flushing buffer with ${buffer.length} log items`); for (const [bufferLogLevel, bufferLogItem] of buffer) { // Create a new LogItem from the stringified log item this.printLog(bufferLogLevel, new LogItem(JSON.parse(bufferLogItem))); } // Clear the buffer after flushing // This also removes entries from other X-Ray trace IDs this.#buffer = {}; } // Buffer the log item when log level is lower than the configured log level if (logLevel < this.level && xRayTraceId) { const buffer = this.#buffer[xRayTraceId] ?? []; // Add the stringified log item to the buffer // Serializing the log item ensures it is not mutated after being added to the buffer buffer.push([logLevel, JSON.stringify(this.createAndPopulateLogItem(logLevel, input, extraInput))]); // Update the buffer with the new log item // This also removes other X-Ray trace IDs from the buffer this.#buffer = { [xRayTraceId]: buffer, }; } // Call the parent method to ensure the log item is processed super.processLogItem(logLevel, input, extraInput); } }
您可能会问为什么我们在这里使用 X 射线追踪 ID。在处理程序函数之外实例化 Logger 是很常见的。但是,由于 Lambda 执行环境可能会重复用于多次调用,因此缓冲区可能包含先前调用的日志项。这就是缓冲区被实现为对象而不是简单数组的原因。我们使用 X-Ray Trace ID 作为标识符,仅缓冲来自同一调用的日志项。
缓冲区被实现为一个对象而不是一个简单的数组。当缓冲区被刷新时,我们可以简单地重置对象,从而从其他调用中清除项目。
让我们快速在本地验证一下实现:
// set X-Ray Trace ID manually if running locally process.env._X_AMZN_TRACE_ID = '1-abcdef12-3456abcdef123456abcdef12'; // log level = WARN const logger = new Logger({ logLevel: 'WARN' }); logger.debug('debug'); // < log level logger.info('info'); // < log level logger.warn('warn'); // = log level logger.error('error'); // > log level
这是我们得到的输出:
import { LogItem, Logger as PowertoolsLogger } from '@aws-lambda-powertools/logger'; import type { LogItemExtraInput, LogItemMessage } from '@aws-lambda-powertools/logger/types'; export class Logger extends PowertoolsLogger { #buffer: Record<string, Array<[number, string]>> = {}; get buffer(): Record<string, Array<[number, string]>> { return this.#buffer; } protected override processLogItem(logLevel: number, input: LogItemMessage, extraInput: LogItemExtraInput): void { const xRayTraceId = this['envVarsService'].getXrayTraceId() as string; // Flush buffer when log level is higher than the configured log level if (logLevel > this.level && xRayTraceId) { const buffer = this.#buffer[xRayTraceId] ?? []; // Print all log items in the buffer if (buffer.length) this.info(`Flushing buffer with ${buffer.length} log items`); for (const [bufferLogLevel, bufferLogItem] of buffer) { // Create a new LogItem from the stringified log item this.printLog(bufferLogLevel, new LogItem(JSON.parse(bufferLogItem))); } // Clear the buffer after flushing // This also removes entries from other X-Ray trace IDs this.#buffer = {}; } // Buffer the log item when log level is lower than the configured log level if (logLevel < this.level && xRayTraceId) { const buffer = this.#buffer[xRayTraceId] ?? []; // Add the stringified log item to the buffer // Serializing the log item ensures it is not mutated after being added to the buffer buffer.push([logLevel, JSON.stringify(this.createAndPopulateLogItem(logLevel, input, extraInput))]); // Update the buffer with the new log item // This also removes other X-Ray trace IDs from the buffer this.#buffer = { [xRayTraceId]: buffer, }; } // Call the parent method to ensure the log item is processed super.processLogItem(logLevel, input, extraInput); } }
警告是第一条消息,因为调试和信息日志已被缓冲。当错误被记录时,我们在实际打印错误之前刷新了缓冲的日志(并打印了信息)。
我的幼稚实现有一些警告。最重要的是,缓冲区大小不受限制,这意味着如果缓冲区增长太大,可能会导致内存问题。有几种方法可以缓解此问题,例如,将缓冲区实现为滑动窗口,仅保留最新日志或限制总缓冲区大小。
此外,缓冲日志仅在受控情况下刷新,例如在 logger.error() 上,但在未处理的错误上不会刷新。如果我们将缓冲区公开并使用像 Middy.js 这样的中间件,则可以轻松实现此行为。 Middy 公开了一个 onError 事件,我们可以利用该事件来刷新缓冲区。
我在官方 AWS Powertools for Lambda 存储库的评论请求中更详细地介绍了这一点。
如果您希望看到此功能成为 Powertools for Lambda 的一部分,请在那里分享您的想法和反馈?
以上是使用 Lambda 的 Powertools 缓冲日志并在出现错误时自动刷新的详细内容。更多信息请关注PHP中文网其他相关文章!