Sling Academy
Home/Node.js/NestJS: How to log stack trace of an error

NestJS: How to log stack trace of an error

Last updated: December 31, 2023

Understanding the Stack Trace Error in NestJS

Logging a stack trace for errors is essential when developing applications in Node.js with NestJS. A stack trace provides a detailed snapshot of the call stack and helps us determine where the error originated. The absence of stack trace can make debugging more challenging as you won’t get enough context to understand the error cause.

Introducing the Logger Service

NestJS comes with a built-in logger service that makes it simple to log errors and their stack traces. However, to log a stack trace when an error occurs, you need to catch the exception and pass it to the logger service. This process can take place in several places within your application, such as in your services, controllers, or through global filters.

Using Global Exception Filters

One way to log error stack traces is to implement a global exception filter. Global exception filters can catch exceptions from anywhere in the application. Let’s create a custom HttpExceptionFilter which will log every exception along with its stack trace.

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

@Catch()
class HttpExceptionFilter implements ExceptionFilter {
  private readonly logger = new Logger(HttpExceptionFilter.name);

  catch(exception: any, host: ArgumentsHost) {
    const ctx = host.switchToHttp();
    const request = ctx.getRequest();
    const response = ctx.getResponse();
    const status = exception instanceof HttpException ? exception.getStatus() : HttpStatus.INTERNAL_SERVER_ERROR;

    this.logger.error(`Status: ${status} Error: ${exception.message}`, exception.stack);

    response.status(status).json({
      statusCode: status,
      timestamp: new Date().toISOString(),
      path: request.url,
    });
  }
}

You can apply this filter globally in your main.ts file to ensure all unhandled exceptions are logged.

import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { HttpExceptionFilter } from './http-exception.filter';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  app.useGlobalFilters(new HttpExceptionFilter());
  await app.listen(3000);
}
bootstrap();

Using Interceptors for Enhanced Control

If you want more control over the responses or handle other types of non-HTTP exceptions, you might consider using interceptors. Interceptors can manipulate the request and response objects, and also handle errors.

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

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

  intercept(context: ExecutionContext, next: CallHandler): Observable {
    return next
      .handle()
      .pipe(
        catchError((error) => {
          this.logger.error('An error occurred', error.stack);
          throw error;
        }),
      );
  }
}

You could then attach this interceptor either globally or at a controller/method scope, depending on your application needs.

Using Controllers and Services

Finally, an error can also be logged from within a service or controller by catching the error in a try-catch block and using the Logger service.

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

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

  async someFunction(): Promise {
    try {
      // ... Your logic here
    catch (error) {
      this.logger.error('An error occurred', error.stack);
      throw error; // Rethrow the error if you want to handle it further up the stack
    }
  }
}

You would write similar code within a controller, catching exceptions within route handlers.

Conclusion

Being able to log a stack trace when an error occurs in NestJS is essential for understanding and resolving issues effectively. In this guide, we explored using global exception filters, custom interceptors, and direct logging within services or controllers. While the error handling strategy might differ depending on the project needs, logging the stack trace remains a critical debugging aid.

Next Article: Fixing ‘done’ Callback Issue in NestJS Testing

Previous Article: Fixing NestJS Error in Production: Cannot Find Module ‘@nestjs/microservices’

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