AWS Powertools for Lambda の最新リリースでは、カスタム機能を使用してロガーを簡単に拡張できます。
Logger クラスがさらに拡張可能になりました
以前はプライベートであったロガー メソッド createAndPopulateLogItem、printLog、および processLogItem を上書きできるようになりました。これにより、Logger を拡張して新しい機能を追加できます (独自のメッセージ バッファーを実装するなど)。
リリース v2.12.0
私はこの新しい拡張性を使用して、ログのバッファリングとフラッシュという便利な機能を実装しています。考え方は単純です。実稼働環境では、ログ領域が高価になり、ノイズが発生する可能性があるため、通常、警告やエラーなどの重要な情報のみをログに記録します。ただし、エラーが発生した場合は、あらゆる可能な情報が必要になります。関数全体に点在するすべてのデバッグ ログと情報ログが利用可能である必要があります。しかし、ログレベルの設定が低すぎるためではありません。
これらのデバッグ ログと情報ログをすべて内部で収集し、エラーなどの重要なイベントが発生した場合にそれらをコンソールに出力したらどうなるでしょうか?私はログを低レベル ログと高レベル ログの 2 つのカテゴリに分類します。構成されたログ レベルが 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 の一部として活用したい場合は、アイデアやフィードバックをそこで共有してください?
以上がPowertools for Lambda を使用してログをバッファリングし、エラー時に自動的にフラッシュするの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。