🗃️ 내가 다시 볼 것

Sentry로 에러 트래킹 하기 (Nest.js)

전호영 2024. 8. 5. 20:22

1. 에러 필터 작성 

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

@Catch()
export class AllExceptionsFilter implements ExceptionFilter {
  constructor(private readonly httpAdapterHost: HttpAdapterHost) {}

  catch(exception: unknown, host: ArgumentsHost) {
    const { httpAdapter } = this.httpAdapterHost;
    const ctx = host.switchToHttp();

    const httpStatus =
      exception instanceof HttpException
        ? exception.getStatus()
        : HttpStatus.INTERNAL_SERVER_ERROR;

    const responseBody = {
      statusCode: httpStatus,
      error: exception instanceof Error ? exception.message : exception,
      message:
        exception instanceof Error
          ? exception.message
          : 'Internal Server Error',
      timestamp: new Date().toISOString(),
      path: httpAdapter.getRequestUrl(ctx.getRequest()),
    };

    httpAdapter.reply(ctx.getResponse(), responseBody, httpStatus);
  }
}

 

2. provider 설정

// src/app.module.ts
import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { AllExceptionsFilter } from './common/filters/global.filter';
...

const configService = new ConfigService();

@Module({
  imports: [
  	...
  ],
  controllers: [AppController],
  providers: [
    AppService,
    {
      provide: APP_FILTER,
      useClass: AllExceptionsFilter,
    },
  ],
})
export class AppModule {}

 

3. Sentry 프로젝트 생성

이후 Sentry에 나오는 설명을 따라 하면 됨!

4. etc

Sentry의 설명을 따라가면 

app.module.ts에 

@Module({
	imports: [
    	SentryModule.forRoot(),
    	...
    ]
})

 

SentryModule을 import하는 코드가 있다.

이러면 내가 설정한 GlobalFilter가 작동하지 않음! 

 

저 부분을 추가하지 말고, 

import {
  ExceptionFilter,
  Catch,
  ArgumentsHost,
  HttpException,
  HttpStatus,
} from '@nestjs/common';
import { HttpAdapterHost } from '@nestjs/core';
import * as Sentry from '@sentry/node';

@Catch()
export class AllExceptionsFilter implements ExceptionFilter {
  constructor(private readonly httpAdapterHost: HttpAdapterHost) {}

  catch(exception: unknown, host: ArgumentsHost): void {
    Sentry.captureException(exception);
    // In certain situations `httpAdapter` might not be available in the
    // constructor method, thus we should resolve it here.
    console.log('in exception filter');
    const { httpAdapter } = this.httpAdapterHost;

    const ctx = host.switchToHttp();

    const httpStatus =
      exception instanceof HttpException
        ? exception.getStatus()
        : HttpStatus.INTERNAL_SERVER_ERROR;

    const responseBody = {
      statusCode: httpStatus,
      error:
        exception instanceof Error ? exception.name : 'Internal Server Error',
      message:
        exception instanceof Error
          ? exception.message
          : 'Internal server error',
      timestamp: new Date().toISOString(),
      path: httpAdapter.getRequestUrl(ctx.getRequest()),
    };

    httpAdapter.reply(ctx.getResponse(), responseBody, httpStatus);
  }
}

 

필터에 Sentry.captureExcpetion(execution)을 넣어주면 Filter에서 걸러진 에러를 Sentry로 보내줌! 

 

5. Slack에 알림 보내기.

Slack에 알림을 보내려면 먼저 Slack에 WebHook을 추가해야 함.

(아래 글 참조)

 

Slack으로 알림 보내기(Nest.js, Slack , Webhook)

웹사이트를 운영하다보면 실시간으로 알림을 받아야하는 경우가 있습니다.(현재 개발중인 동아리 웹사이트를 예로들면 에러 , 암벽화 대여 등) 저는 이를 Slack과 Slack Webhook을 통해 구현했습니

securityinit.tistory.com

 

코드는 다음과 같이 구성.

import {
  ExceptionFilter,
  Catch,
  ArgumentsHost,
  HttpException,
  HttpStatus,
} from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { HttpAdapterHost } from '@nestjs/core';
import * as Sentry from '@sentry/node';
import { IncomingWebhook } from '@slack/webhook';

@Catch()
export class AllExceptionsFilter implements ExceptionFilter {
  constructor(
    private readonly httpAdapterHost: HttpAdapterHost,
    private readonly configService: ConfigService,
  ) {}

  async catch(exception: unknown, host: ArgumentsHost) {
    Sentry.captureException(exception);
    // In certain situations `httpAdapter` might not be available in the
    // constructor method, thus we should resolve it here.

    const url = this.configService.get('SLACK_ERROR_WEBHOOK_URL');
    const webhook = new IncomingWebhook(url);
    const { httpAdapter } = this.httpAdapterHost;

    const ctx = host.switchToHttp();

    const httpStatus =
      exception instanceof HttpException
        ? exception.getStatus()
        : HttpStatus.INTERNAL_SERVER_ERROR;

    const responseBody = {
      statusCode: httpStatus,
      error:
        exception instanceof Error ? exception.name : 'Internal Server Error',
      message:
        exception instanceof Error
          ? exception.message
          : 'Internal server error',
      timestamp: new Date().toISOString(),
      path: httpAdapter.getRequestUrl(ctx.getRequest()),
    };

    await webhook.send({
      attachments: [
        {
          color: 'danger',
          text: 'DKUAC 에러 발생',
          fields: [
            {
              title:
                exception instanceof Error
                  ? exception.name
                  : 'Internal Server Error',
              value:
                exception instanceof Error
                  ? exception.message
                  : 'Internal server error',
              short: false,
            },
          ],
          ts: Math.floor(new Date().getTime() / 1000).toString(),
        },
      ],
    });

    httpAdapter.reply(ctx.getResponse(), responseBody, httpStatus);
  }
}