Passer au contenu

Gestion des journaux dans NestJS

Logger

Nest est livré avec un logger intégré basé sur du texte, utilisé pendant le démarrage de l’application et dans plusieurs autres circonstances, comme l’affichage des exceptions capturées (c’est-à-dire la journalisation système). Cette fonctionnalité est fournie via la classe Logger dans le paquet @nestjs/common. Vous pouvez entièrement contrôler le comportement du système de journalisation, y compris l’option suivante :

  • désactiver totalement la journalisation
  • spécifier le niveau de détail des journaux (par exemple, afficher les erreurs, les avertissements, les informations de débogage, etc.)
  • remplacer l’horodatage dans le logger par défaut (par exemple, utiliser le format ISO8601 comme format de date)
  • remplacer complètement le logger par défaut
  • personnaliser le logger par défaut en l’étendant
  • utiliser l’injection de dépendances pour simplifier la composition et les tests de votre application

Vous pouvez également utiliser le logger intégré ou créer votre propre mise en œuvre personnalisée pour enregistrer vos propres événements et messages au niveau de l’application.

Pour une fonctionnalité de journalisation plus avancée, vous pouvez utiliser n’importe quel paquet de journalisation Node.js, tel que Winston, pour implémenter un système de journalisation entièrement personnalisé et de qualité production.

Personnalisation de base

Pour désactiver la journalisation, définissez la propriété logger sur false dans l’objet d’options d’application Nest (optionnel) passé comme second argument à la méthode NestFactory.create().

Désactiver la journalisation
const app = await NestFactory.create(AppModule, {
logger: false,
});
await app.listen(3000);

Pour activer des niveaux de journalisation spécifiques, définissez la propriété logger sur un tableau de chaînes spécifiant les niveaux de journalisation à afficher, comme suit :

Activer des niveaux de journalisation spécifiques
const app = await NestFactory.create(AppModule, {
logger: ['error', 'warn'],
});
await app.listen(3000);

Les valeurs dans le tableau peuvent être n’importe quelle combinaison de 'log', 'fatal', 'error', 'warn', 'debug', et 'verbose'.

Mise en œuvre personnalisée

Vous pouvez fournir une mise en œuvre personnalisée du logger à utiliser par Nest pour la journalisation du système en définissant la valeur de la propriété logger sur un objet qui remplit l’interface LoggerService. Par exemple, vous pouvez demander à Nest d’utiliser l’objet console global JavaScript intégré (qui implémente l’interface LoggerService), comme suit :

Utilisation de l'objet console
const app = await NestFactory.create(AppModule, {
logger: console,
});
await app.listen(3000);

Mettre en œuvre votre propre logger personnalisé est simple. Il suffit d’implémenter chacune des méthodes de l’interface LoggerService comme montré ci-dessous.

Implémentation du LoggerService
import { LoggerService, Injectable } from '@nestjs/common';
@Injectable()
export class MyLogger implements LoggerService {
/**
* Écrire un log de niveau 'log'.
*/
log(message: any, ...optionalParams: any[]) {}
/**
* Écrire un log de niveau 'fatal'.
*/
fatal(message: any, ...optionalParams: any[]) {}
/**
* Écrire un log de niveau 'error'.
*/
error(message: any, ...optionalParams: any[]) {}
/**
* Écrire un log de niveau 'warn'.
*/
warn(message: any, ...optionalParams: any[]) {}
/**
* Écrire un log de niveau 'debug'.
*/
debug?(message: any, ...optionalParams: any[]) {}
/**
* Écrire un log de niveau 'verbose'.
*/
verbose?(message: any, ...optionalParams: any[]) {}
}

Vous pouvez ensuite fournir une instance de MyLogger via la propriété logger de l’objet d’options de l’application Nest.

Fournir une instance de MyLogger
const app = await NestFactory.create(AppModule, {
logger: new MyLogger(),
});
await app.listen(3000);

Cette technique, bien que simple, n’utilise pas l’injection de dépendances pour la classe MyLogger. Cela peut poser certains défis, notamment pour les tests, et limiter la réutilisabilité de MyLogger. Pour une meilleure solution, consultez la section d’Injection de dépendance ci-dessous.

Étendre le logger intégré

Plutôt que d’écrire un logger depuis le début, vous pouvez répondre à vos besoins en étendant la classe ConsoleLogger intégrée et en remplaçant le comportement sélectionné de l’implémentation par défaut.

Étendre ConsoleLogger
import { ConsoleLogger } from '@nestjs/common';
export class MyLogger extends ConsoleLogger {
error(message: any, stack?: string, context?: string) {
// Ajoutez votre logique personnalisée ici
super.error(...arguments);
}
}

Vous pouvez utiliser un tel logger étendu dans vos modules de fonctionnalités comme décrit dans la section Utilisation du logger pour la journalisation d’application ci-dessous.

Vous pouvez demander à Nest d’utiliser votre logger étendu pour la journalisation du système en passant une instance de celui-ci via la propriété logger de l’objet d’options de l’application (comme indiqué dans la section Mise en œuvre personnalisée ci-dessus), ou en utilisant la technique montrée dans la section d’Injection de dépendance ci-dessous.

Si vous le faites, vous devez veiller à appeler super, comme montré dans le code exemple ci-dessus, pour déléguer l’appel de méthode de log spécifique à la classe parente (intégrée), afin que Nest puisse compter sur les fonctionnalités intégrées qu’il attend.

Utilisation du logger pour la journalisation d’application

Nous pouvons combiner plusieurs des techniques ci-dessus pour fournir un comportement et un formatage cohérents à la fois dans la journalisation système de Nest et dans notre propre journalisation d’événements/messages d’application.

Une bonne pratique consiste à instancier la classe Logger de @nestjs/common dans chacun de nos services. Nous pouvons fournir le nom de notre service comme argument context dans le constructeur Logger, comme suit :

Utilisation de Logger dans un service
import { Logger, Injectable } from '@nestjs/common';
@Injectable()
class MyService {
private readonly logger = new Logger(MyService.name);
doSomething() {
this.logger.log('Doing something...');
}
}

Dans l’implémentation de logger par défaut, context est imprimé entre crochets, comme NestFactory dans l’exemple ci-dessous :

Fenêtre de terminal
[Nest] 19096 - 12/08/2019, 7:12:59 AM [NestFactory] Starting Nest application...

Si nous fournissons un logger personnalisé via app.useLogger(), il sera effectivement utilisé par Nest en interne. Cela signifie que notre code reste indépendant de l’implémentation, tout en nous permettant de remplacer facilement le logger par défaut par le nôtre en appelant app.useLogger().

De cette façon, si nous suivons les étapes de la section précédente et appelons app.useLogger(app.get(MyLogger)), les appels suivants à this.logger.log() depuis MyService entraîneraient des appels à la méthode log de l’instance MyLogger.

Cela devrait convenir dans la plupart des cas. Mais si vous avez besoin de plus de personnalisation (comme l’ajout et l’appel de méthodes personnalisées), passez à la section suivante.

Injection d’un logger personnalisé

Pour commencer, étendez le logger intégré avec du code comme le suivant. Nous fournissons l’option scope comme métadonnée de configuration pour la classe ConsoleLogger, spécifiant un scope transitoire, pour s’assurer que nous aurons une instance unique de MyLogger dans chaque module de fonctionnalité.

Étendre ConsoleLogger avec un scope
import { Injectable, Scope, ConsoleLogger } from '@nestjs/common';
@Injectable({ scope: Scope.TRANSIENT })
export class MyLogger extends ConsoleLogger {
customLog() {
this.log('Please feed the cat!');
}
}

Ensuite, créez un LoggerModule avec une construction semblable à celle-ci :

Créer LoggerModule
import { Module } from '@nestjs/common';
import { MyLogger } from './my-logger.service';
@Module({
providers: [MyLogger],
exports: [MyLogger],
})
export class LoggerModule {}

Ensuite, importez le LoggerModule dans votre module de fonctionnalité. Puisque nous avons étendu le logger par défaut, nous avons le confort d’utiliser la méthode setContext. Ainsi nous pouvons commencer à utiliser le logger personnalisé sensible au contexte, comme ceci :

Utilisation de MyLogger dans un module de fonctionnalité
import { Injectable } from '@nestjs/common';
import { MyLogger } from './my-logger.service';
@Injectable()
export class CatsService {
private readonly cats: Cat[] = [];
constructor(private myLogger: MyLogger) {
this.myLogger.setContext('CatsService');
}
findAll(): Cat[] {
this.myLogger.warn('About to return cats!');
return this.cats;
}
}

Enfin, demandez à Nest d’utiliser une instance du logger personnalisé dans votre fichier main.ts comme montré ci-dessous. Bien sûr, dans cet exemple, nous n’avons pas réellement personnalisé le comportement du logger (en étendant les méthodes Logger comme log(), warn(), etc.), donc cette étape n’est pas réellement nécessaire. Mais elle serait nécessaire si vous ajoutiez une logique personnalisée à ces méthodes et vouliez que Nest utilise la même implémentation.

Utiliser MyLogger dans main.ts
const app = await NestFactory.create(AppModule, {
bufferLogs: true,
});
app.useLogger(new MyLogger());
await app.listen(3000);

Utiliser un logger externe

Les applications de production ont souvent des exigences de journalisation spécifiques, y compris un filtrage avancé, un formatage et une journalisation centralisée. Le logger intégré de Nest est utilisé pour surveiller le comportement du système Nest et peut également être utile pour la journalisation de texte formatée de base dans vos modules de fonctionnalités pendant le développement, mais les applications de production profitent souvent de modules de journalisation dédiés comme Winston. Comme avec n’importe quelle application Node.js standard, vous pouvez tirer pleinement parti de tels modules dans Nest.