How to Monitor and Log Errors in NestJS

Updated: January 1, 2024 By: Guest Contributor Post a comment

Understanding Errors in NestJS

When developing applications with Node.js and the NestJS framework, encountering errors is a natural part of the development process. Errors in NestJS can originate from various sources, including runtime exceptions, unhandled promises, or logic errors within the application code. Monitoring and logging these errors is crucial for maintaining a healthy and reliable application. It ensures that developers are alerted to issues as they arise and can act quickly to resolve them.

Configure Exception Filters

The first step toward effective error monitoring in a NestJS application is to configure exception filters. Exception filters in NestJS are responsible for catching unhandled exceptions thrown during the handling of a request and then formatting the response sent back to the client. You can create a global filter that will catch any uncaught exception across the entire application.

To create a global exception filter, you must define a custom filter that extends the BaseExceptionFilter class. Within your custom filter, you can define your logging mechanism, such as console logging, or writing to a file. Below is an example of a simple global exception filter that logs the error:

import { ExceptionFilter, Catch, ArgumentsHost, HttpException, HttpStatus } from '@nestjs/common';
import { BaseExceptionFilter } from '@nestjs/core';

@Catch()
export class GlobalExceptionFilter extends BaseExceptionFilter {
  catch(exception: unknown, host: ArgumentsHost) {
    super.catch(exception, host);
    const ctx = host.switchToHttp();
    const response = ctx.getResponse();
    const request = ctx.getRequest();
    const status = exception instanceof HttpException ? exception.getStatus() : HttpStatus.INTERNAL_SERVER_ERROR;

    // Implement the logging mechanism of your choice here
    console.error(
      `Http Status: ${status} Error Message: ${exception.message}, Trace: ${exception.stack}`
    );
  }
}

Integrate a Logging Package

To further refine your error logging strategy, consider integrating a sophisticated logging package such as Winston or Pino. These packages provide more advanced features such as logging levels, log rotation, and the ability to send your logs to various outputs, such as a file, a console, or external services like Logstash or Sentry for further analysis. Here is how you might set up Winston in your NestJS application:

import { Module } from '@nestjs/common';
import { WinstonModule } from 'nest-winston';
import * as winston from 'winston';

@Module({
  imports: [
    WinstonModule.forRoot({
      transports: [
        new winston.transports.Console(),
        new winston.transports.File({ filename: 'combined.log' })
      ],
    }),
  ],
})
export class AppModule {}

Once you have set up Winston or another log management system, ensure you use it within your exception filter to log errors effectively.

Interceptors and Middleware for Error Handling

Besides filters, you can employ interceptors and middleware in NestJS for catching and logging errors. Interceptors allow you to manipulate the request and response objects, including the ability to catch errors thrown by a method before they are handled by your global exception filter. Middleware can catch errors at the route level before they reach your controller methods.

It is essential, however, to consider the order of operations. Filters are the last step in the request-response cycle when an exception occurs. Consequently, logging at the interceptor or middleware layer will catch errors before they hit the filter, which could be advantageous for granulating error handling and response.

Handle Uncaptured Promise Rejections

Uncaptured promise rejections represent an error case that can sometimes slip through the cracks in Node.js applications. NestJS does not automatically handle these, so it’s important to add a listener for unhandledRejection events at the process level to log these errors too. Here is how you might do that:

process.on('unhandledRejection', (reason, promise) => {
  console.error('Unhandled Rejection at:', promise, 'reason:', reason);
  // Recommended: send to your error tracking service here
});

This should be done when bootstrapping your application, to ensure that no unhandled rejections go unnoticed.

Testing and Verification

Once you’ve implemented error handling and logging in your NestJS application, ensure that you test your implementation thoroughly. You can simulate various errors to make sure your logging captures and records the errors as expected. Regularly verify the log files or log management system to confirm they are operating correctly and actually contain the necessary information for debugging and analysis.

Conclusion

Monitoring and logging errors in your NestJS application can make a vast difference in its stability and maintainability. By utilizing exception filters, third-party logging libraries, interceptors, middleware, and handling unhandled promise rejections, you can ensure that you catch and log a comprehensive range of errors that might occur in your application. This strengthens your ability to respond to and fix issues promptly, leading to a more professional and reliable product.