· Diogo Felizardo · NestJS  · 6 min de leitura

Logging no NestJs

Aprenda a implementar e customizar o sistema de logs no NestJs para melhorar a manutenção e monitoramento de aplicações.

Aprenda a implementar e customizar o sistema de logs no NestJs para melhorar a manutenção e monitoramento de aplicações.

O logging é uma parte essencial no desenvolvimento de aplicações backend, permitindo monitorar o comportamento do sistema, diagnosticar problemas e manter registros de eventos importantes. Neste post, exploraremos como implementar e customizar o sistema de logs no NestJs, um framework Node.js progressivo para construção de aplicações eficientes e escaláveis.

Sumário

Introdução

O NestJs fornece uma implementação de Logger que facilita a criação de logs estruturados e consistentes em sua aplicação. Com ele, é possível registrar informações importantes durante o ciclo de vida da aplicação, como inicialização de módulos, requisições HTTP, erros e muito mais.

Configurando o Logger no NestJs

Por padrão, o NestJs já vem com um sistema de logging básico. Para utilizá-lo, basta injetar o serviço Logger em seus componentes.

Exemplo Básico

// src/app.service.ts
import { Logger, Injectable } from '@nestjs/common';

@Injectable()
export class AppService {
  private readonly logger = new Logger(AppService.name);

  getHello(): string {
    this.logger.log('getHello foi chamado');
    return 'Hello World!';
  }
}

Neste exemplo, criamos uma instância do Logger e utilizamos o método log para registrar uma mensagem sempre que o método getHello for chamado.

Níveis de Log

O sistema de logging do NestJs suporta diferentes níveis de log, permitindo categorizar as mensagens de acordo com sua importância:

  • Log: Informações gerais sobre o funcionamento da aplicação.
  • Error: Erros que ocorrem durante a execução.
  • Warn: Avisos sobre situações que podem precisar de atenção.
  • Debug: Informações detalhadas para depuração.
  • Verbose: Logs muito detalhados para análise aprofundada.

Utilizando Diferentes Níveis

this.logger.log('Mensagem de log');
this.logger.error('Mensagem de erro');
this.logger.warn('Mensagem de aviso');
this.logger.debug('Mensagem de debug');
this.logger.verbose('Mensagem verbose');

Customizando o Logger

Para atender a necessidades específicas, é possível criar uma implementação customizada do Logger. Isso permite, por exemplo, enviar logs para serviços externos, formatar mensagens de maneira diferente ou alterar o comportamento padrão.

Criando um Logger Customizado

// src/custom-logger.service.ts
import { LoggerService, Injectable, Scope } from '@nestjs/common';

@Injectable({ scope: Scope.TRANSIENT })
export class CustomLogger implements LoggerService {
  log(message: string) {
    // Implementação personalizada
    console.log(`[CUSTOM LOG]: ${message}`);
  }

  error(message: string, trace: string) {
    // Implementação personalizada
    console.error(`[CUSTOM ERROR]: ${message} - Trace: ${trace}`);
  }

  warn(message: string) {
    // Implementação personalizada
    console.warn(`[CUSTOM WARN]: ${message}`);
  }

  debug(message: string) {
    // Implementação personalizada
    console.debug(`[CUSTOM DEBUG]: ${message}`);
  }

  verbose(message: string) {
    // Implementação personalizada
    console.info(`[CUSTOM VERBOSE]: ${message}`);
  }
}

Integrando o Logger Customizado

Para utilizar o CustomLogger, basta configurá-lo no bootstrap da aplicação:

// src/main.ts
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { CustomLogger } from './custom-logger.service';

async function bootstrap() {
  const app = await NestFactory.create(AppModule, {
    logger: new CustomLogger(),
  });
  await app.listen(3000);
}
bootstrap();

Usando Interceptadores para Logging

Interceptadores são uma poderosa funcionalidade do NestJs que permite interceptar chamadas de métodos e aplicar lógica adicional. Podemos utilizá-los para implementar logging de requisições e respostas de forma centralizada.

Criando um Interceptador de Logging

// src/logging.interceptor.ts
import {
  Injectable,
  NestInterceptor,
  ExecutionContext,
  CallHandler,
  Logger,
} from '@nestjs/common';
import { Observable } from 'rxjs';
import { tap } from 'rxjs/operators';

@Injectable()
export class LoggingInterceptor implements NestInterceptor {
  private readonly logger = new Logger('HTTP');

  intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
    const req = context.switchToHttp().getRequest();
    const { method, url } = req;

    this.logger.log(`Incoming Request: ${method} ${url}`);

    const now = Date.now();
    return next
      .handle()
      .pipe(
        tap(() =>
          this.logger.log(`Outgoing Response: ${method} ${url} - ${Date.now() - now}ms`),
        ),
      );
  }
}

Registrando o Interceptador Globalmente

// src/main.ts
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { LoggingInterceptor } from './logging.interceptor';
import { CustomLogger } from './custom-logger.service';

async function bootstrap() {
  const app = await NestFactory.create(AppModule, {
    logger: new CustomLogger(),
  });
  app.useGlobalInterceptors(new LoggingInterceptor());
  await app.listen(3000);
}
bootstrap();

Com isso, todas as requisições HTTP serão logadas automaticamente, tanto na entrada quanto na saída.

Exemplo Prático

Vamos criar uma aplicação simples que utiliza logging para monitorar operações CRUD em um recurso chamado Cats.

Estrutura do Projeto

src/
├── cats/
│   ├── cats.controller.ts
│   ├── cats.service.ts
│   └── dto/
│       └── create-cat.dto.ts
├── custom-logger.service.ts
├── logging.interceptor.ts
├── main.ts
└── app.module.ts

Implementação

Instalando class-validator e class-transformer

Para utilizar validações nos DTOs, é necessário instalar as dependências class-validator e class-transformer. Execute o seguinte comando no terminal:

npm install class-validator class-transformer

Em seguida, configure o ValidationPipe globalmente na aplicação para habilitar a validação automática dos DTOs:

// src/main.ts
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { LoggingInterceptor } from './logging.interceptor';
import { ValidationPipe } from '@nestjs/common';

async function bootstrap() {
  const app = await NestFactory.create(AppModule, {
    logger: new CustomLogger(),
  });
  app.useGlobalInterceptors(new LoggingInterceptor());
  app.useGlobalPipes(new ValidationPipe());
  await app.listen(3000);
}
bootstrap();

src/cats/dto/create-cat.dto.ts

Este arquivo define o DTO para criar um novo gato, incluindo validações para garantir que os dados recebidos sejam válidos.

// src/cats/dto/create-cat.dto.ts
import { IsString, IsInt, IsOptional } from 'class-validator';

export class CreateCatDto {
  @IsString()
  readonly name: string;

  @IsInt()
  readonly age: number;

  @IsOptional()
  @IsString()
  readonly breed?: string;
}

Explicação:

  • @IsString(): Garante que o campo seja uma string.
  • @IsInt(): Garante que o campo seja um número inteiro.
  • @IsOptional(): Indica que o campo é opcional.

Essas validações ajudam a prevenir que dados inválidos cheguem aos serviços da aplicação, melhorando a robustez e a segurança.

src/cats/cats.service.ts

// src/cats/cats.service.ts
import { Injectable, Logger } from '@nestjs/common';
import { CreateCatDto } from './dto/create-cat.dto';

@Injectable()
export class CatsService {
  private readonly logger = new Logger(CatsService.name);
  private readonly cats = [];

  create(createCatDto: CreateCatDto) {
    this.cats.push(createCatDto);
    this.logger.log(`Criado um novo gato: ${JSON.stringify(createCatDto)}`);
  }

  findAll() {
    this.logger.log('Retornando todos os gatos');
    return this.cats;
  }
}

src/cats/cats.controller.ts

// src/cats/cats.controller.ts
import { Controller, Get, Post, Body } from '@nestjs/common';
import { CatsService } from './cats.service';
import { CreateCatDto } from './dto/create-cat.dto';

@Controller('cats')
export class CatsController {
  constructor(private readonly catsService: CatsService) {}

  @Post()
  create(@Body() createCatDto: CreateCatDto) {
    this.catsService.create(createCatDto);
    return 'Gato criado com sucesso!';
  }

  @Get()
  findAll() {
    return this.catsService.findAll();
  }
}

Com essa configuração, ao criar ou buscar gatos, as operações serão logadas, facilitando o monitoramento e a depuração da aplicação. Além disso, as validações asseguram que os dados recebidos estejam no formato correto, aumentando a confiabilidade da aplicação.

Repositório no GitHub

Para acessar o código fonte deste projeto, visite o Acesse o Repositório no GitHub.

Conclusão

Implementar um sistema de logging eficaz no NestJs é fundamental para manter a saúde e a performance de sua aplicação. O NestJs oferece ferramentas robustas e flexíveis para criar logs personalizados, permitindo que você adapte o sistema de acordo com as necessidades específicas do seu projeto. Ao seguir as práticas apresentadas neste post, você estará melhor equipado para monitorar, diagnosticar e manter sua aplicação NestJs de maneira eficiente.

Referências

Olá Dev 👋🏻

Se você achou este post útil, considere dar uma estrela no repositório do GitHub ou compartilhar nas suas redes sociais favoritas 😍. Seu apoio faria toda a diferença!

Dúvidas? 🙋

Se tiver alguma pergunta sobre desenvolvimento backend, sinta-se à vontade para criar uma nova issue no GitHub usando o botão abaixo. Ficarei feliz em ajudar com qualquer assunto que você queira explorar!
Compartilhar conhecimento é a melhor forma de crescermos juntos 👨🏻‍💻.
Me faça uma pergunta
Back to Blog

Posts relacionados

Ver todos os posts »
Guia Completo dos Comandos CLI do NestJS

Guia Completo dos Comandos CLI do NestJS

Aprenda a utilizar a CLI do NestJS de forma eficiente com este guia completo. Descubra os principais comandos, entenda para que cada um serve e veja o que eles geram em seu projeto.