Sling Academy
Home/Node.js/How to Log Requests in NestJS

How to Log Requests in NestJS

Last updated: December 31, 2023

Introduction

Efficient request logging is crucial for debugging and monitoring applications. NestJS, a progressive Node.js framework, provides robust solutions for logging requests with ease. In this guide, we delve into several methods to achieve this using NestJS’s latest features and TypeScript.

Using Middleware for Basic Logging

To start logging requests in NestJS, you can utilize middleware, which are functions that have access to the request and response objects.

import { Injectable, NestMiddleware, Logger } from '@nestjs/common';
import { Request, Response, NextFunction } from 'express';

@Injectable()
export class LoggingMiddleware implements NestMiddleware {
  use(req: Request, res: Response, next: NextFunction) {
    const { method, path } = req;
    const message = `${method} ${path}`;

    Logger.log(message, 'IncomingRequest');

    res.on('finish', () => {
      const { statusCode } = res;
      const responseMessage = `${method} ${path} ${statusCode}`;
      Logger.log(responseMessage, 'OutgoingResponse');
    });

    next();
  }
}

After creating your middleware, apply it globally in your main.ts file or attach it to specific routes or controllers.

Custom Logger Services

For finer control over your logging, building a custom logger service is ideal. This can involve extending NestJS’s built-in Logger class.

import { Injectable, LoggerService, Scope, Logger } from '@nestjs/common';

@Injectable({ scope: Scope.TRANSIENT })
class MyLogger extends Logger implements LoggerService {
  // Custom log method implementation
  log(message: string) {
    // Add your custom logging functionality here
    super.log(message); // Call the base class log method
  }

  // Other methods like error, warn, etc.
}

Integrate your custom logger with main.ts by setting it as the application’s logger of choice.

import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { MyLogger } from './my-logger.service';

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

  await app.listen(3000);
}
bootstrap();

Interceptors for Advanced Logging

Interceptors offer a way to tap into the flow of a method execution. Here’s how to use an interceptor for advanced request logging.

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

@Injectable()
export class LoggingInterceptor implements NestInterceptor {
  intercept(context: ExecutionContext, next: CallHandler): Observable {
    const httpContext = context.switchToHttp();
    const request = httpContext.getRequest();
    const { method, url } = request;
    const reqLog = `Request: ${method} ${url}`;
    Logger.log(reqLog, 'RequestInterceptor');

    return next
      .handle()
      .pipe(
        tap(() => {
          const response = httpContext.getResponse();
          const resLog = `Response: ${method} ${url} ${response.statusCode}`;
          Logger.log(resLog, 'ResponseInterceptor');
        }),
      );
  }
}

Add the interceptor to your application.

import { Module } from '@nestjs/common';
import { APP_INTERCEPTOR } from '@nestjs/core';
import { LoggingInterceptor } from './logging-interceptor.service';

@Module({
  providers: [
    {
      provide: APP_INTERCEPTOR,
      useClass: LoggingInterceptor,
    },
  ],
  //... other module properties
})
class AppModule {}

Extended Request Logging

Logging can be more than just console output; storing logs for future analysis or real-time monitoring is often needed. A more elaborate setup using a logging library like Winston might include file transports or external log management solutions.

Here’s an example of how you might extend a logger for file-based logging with context.

// Additional imports and setup for Winston...

class ExtendedLogger extends Logger {
  private logger = new WinstonLogger(/* ...Winston configuration... */);

  log(message: string, context?: string) {
    this.logger.info(message, { context });
    super.log(message, context);
  }

//... other methods and implementation details ...
}

Such advanced setups allow you to create compact and structured logs that are suitable for processing with modern log management systems.

Conclusion

Through this guide, we have seen that NestJS offers several ways to implement request logging, from straightforward middleware-based methods to a more advanced interceptor-driven approach. Choose the strategy that aligns best with your project’s requirements and make sure your logs are meaningful and actionable.

Next Article: How to Extract and Parse Headers in NestJS

Previous Article: How to Use Multiple Databases in NestJS

Series: Nest.js Tutorials: From Basics to Advanced

Node.js

You May Also Like

  • NestJS: How to create cursor-based pagination (2 examples)
  • Cursor-Based Pagination in SequelizeJS: Practical Examples
  • MongooseJS: Cursor-Based Pagination Examples
  • Node.js: How to get location from IP address (3 approaches)
  • SequelizeJS: How to reset auto-increment ID after deleting records
  • SequelizeJS: Grouping Results by Multiple Columns
  • NestJS: Using Faker.js to populate database (for testing)
  • NodeJS: Search and download images by keyword from Unsplash API
  • NestJS: Generate N random users using Faker.js
  • Sequelize Upsert: How to insert or update a record in one query
  • NodeJS: Declaring types when using dotenv with TypeScript
  • Using ExpressJS and Multer with TypeScript
  • NodeJS: Link to static assets (JS, CSS) in Pug templates
  • NodeJS: How to use mixins in Pug templates
  • NodeJS: Displaying images and links in Pug templates
  • ExpressJS + Pug: How to use loops to render array data
  • ExpressJS: Using MORGAN to Log HTTP Requests
  • NodeJS: Using express-fileupload to simply upload files
  • ExpressJS: How to render JSON in Pug templates