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을 추가해야 함.
(아래 글 참조)
코드는 다음과 같이 구성.
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);
}
}