La dernière version d'AWS Powertools for Lambda facilite l'extension du Logger avec des fonctionnalités personnalisées :
La classe Logger est désormais plus extensible
Vous pouvez désormais remplacer les méthodes Logger createAndPopulateLogItem, printLog et processLogItem, qui étaient auparavant privées. Cela vous permet d'étendre le Logger et d'ajouter de nouvelles fonctionnalités, par exemple, implémenter votre propre tampon de messages.
Sortie v2.12.0
J'utilise cette nouvelle extensibilité pour implémenter une fonctionnalité utile : la mise en mémoire tampon et le vidage des journaux. L'idée est simple : dans un environnement de production, nous enregistrons généralement uniquement les informations importantes, telles que les avertissements et les erreurs, car l'espace de journalisation peut coûter cher et créer du bruit. Cependant, lorsqu'une erreur se produit, nous voulons toutes les informations possibles : tous ces journaux de débogage et d'informations dispersés dans une fonction doivent être disponibles. Mais ce n’est pas le cas parce que nous avons fixé notre niveau de journalisation trop bas.
Et si nous collections tous ces journaux de débogage et d'informations en interne, et si un événement important se produit comme une erreur, nous les imprimions sur la console ? Je classe les journaux en deux catégories : les journaux de bas niveau et les journaux de haut niveau. Si notre niveau de journalisation configuré est WARN, un journal DEBUG ou INFO serait de bas niveau et ERROR serait un journal de haut niveau.
Maintenant, lorsque nous imprimons un journal de bas niveau, au lieu de le supprimer tel qu'il est actuellement, nous mettons le journal en mémoire tampon dans une liste interne. Dès que nous avons un journal de haut niveau, nous vidons tous les journaux mis en mémoire tampon vers la console.
Pour ajouter cette fonctionnalité, nous créons une nouvelle classe qui étend la classe Logger de Powertools et remplaçons processLogItem(). Il s'agit de la méthode centrale appelée par les différentes méthodes de journalisation comme logger.debug(). L'implémentation d'origine imprime l'élément de journal sur la console s'il se trouve au bon niveau. En remplaçant cette méthode, nous pouvons ajouter notre logique spéciale de mise en mémoire tampon et de vidage des journaux en fonction du niveau de journalisation.
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); } }
Vous pourriez vous demander pourquoi nous utilisons l'identifiant X-Ray Trace ici. Il est courant d'instancier le Logger en dehors de la fonction de gestionnaire. Toutefois, étant donné que l'environnement d'exécution Lambda est réutilisé pour des appels potentiellement multiples, le tampon peut contenir des éléments de journal des appels précédents. C'est la raison pour laquelle le tampon est implémenté comme un objet plutôt que comme un simple tableau. Nous utilisons l'ID de trace X-Ray comme identifiant pour mettre en mémoire tampon uniquement les éléments de journal provenant de la même invocation.
Le tampon est implémenté comme un objet plutôt que comme un simple tableau. Lorsque le tampon est vidé, nous pouvons simplement réinitialiser l'objet et donc purger les éléments des autres invocations.
Vérifions rapidement la mise en œuvre localement :
// 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
Voici le résultat que nous obtenons :
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); } }
L'avertissement est le premier message car les journaux de débogage et d'informations ont été mis en mémoire tampon. Lorsque l'erreur a été enregistrée, nous avons vidé les journaux mis en mémoire tampon (et imprimé une information) avant que l'erreur ne soit réellement imprimée.
Mon implémentation naïve comporte quelques mises en garde. Plus important encore, la taille du tampon n’est pas limitée, ce qui signifie que cela pourrait entraîner des problèmes de mémoire si le tampon devient trop volumineux. Il existe plusieurs approches pour atténuer ce problème, par exemple en implémentant le tampon sous forme de fenêtre coulissante qui ne conserve que les journaux les plus récents ou en limitant la taille totale du tampon.
De plus, les journaux mis en mémoire tampon ne sont vidés que dans des cas contrôlés comme sur logger.error(), mais pas en cas d'erreurs non gérées. Ce comportement pourrait facilement être obtenu si nous rendons le tampon public et utilisons un middleware comme Middy.js. Middy expose un événement onError que nous pourrions utiliser pour vider le tampon.
J'ai écrit à ce sujet plus en détail dans cette demande de commentaires sur le référentiel officiel AWS Powertools for Lambda.
Si vous souhaitez voir cette fonctionnalité devenir une partie de Powertools pour Lambda, veuillez partager vos idées et vos commentaires ici ?
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!